Initial commit to Gerrit tizen/20120530.1
authorYang Lin <lin.a.yang@intel.com>
Wed, 30 May 2012 10:34:09 +0000 (18:34 +0800)
committerYang Lin <lin.a.yang@intel.com>
Wed, 30 May 2012 10:34:09 +0000 (18:34 +0800)
511 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
btio/btio.c [new file with mode: 0644]
btio/btio.h [new file with mode: 0644]
compile [new file with mode: 0755]
config.guess [new file with mode: 0755]
config.h.in [new file with mode: 0644]
config.sub [new file with mode: 0755]
configure [new file with mode: 0755]
configure.ac [new file with mode: 0644]
depcomp [new file with mode: 0755]
doc/audio-settings-api.txt [new file with mode: 0644]
doc/call-barring-api.txt [new file with mode: 0644]
doc/call-forwarding-api.txt [new file with mode: 0644]
doc/call-meter-api.txt [new file with mode: 0644]
doc/call-settings-api.txt [new file with mode: 0644]
doc/call-volume-api.txt [new file with mode: 0644]
doc/calypso-modem.txt [new file with mode: 0644]
doc/cell-broadcast-api.txt [new file with mode: 0644]
doc/connman-api.txt [new file with mode: 0644]
doc/features.txt [new file with mode: 0644]
doc/location-reporting-api.txt [new file with mode: 0644]
doc/manager-api.txt [new file with mode: 0644]
doc/message-api.txt [new file with mode: 0644]
doc/message-waiting-api.txt [new file with mode: 0644]
doc/messagemanager-api.txt [new file with mode: 0644]
doc/modem-api.txt [new file with mode: 0644]
doc/network-api.txt [new file with mode: 0644]
doc/ofono-paper.txt [new file with mode: 0644]
doc/ofonod.8 [new file with mode: 0644]
doc/overview.txt [new file with mode: 0644]
doc/phonebook-api.txt [new file with mode: 0644]
doc/pushnotification-api.txt [new file with mode: 0644]
doc/radio-settings-api.txt [new file with mode: 0644]
doc/release-faq.txt [new file with mode: 0644]
doc/sim-api.txt [new file with mode: 0644]
doc/smartmessaging-api.txt [new file with mode: 0644]
doc/stk-api.txt [new file with mode: 0644]
doc/supplementaryservices-api.txt [new file with mode: 0644]
doc/text-telephony-api.txt [new file with mode: 0644]
doc/voicecall-api.txt [new file with mode: 0644]
doc/voicecallmanager-api.txt [new file with mode: 0644]
drivers/atmodem/atmodem.c [new file with mode: 0644]
drivers/atmodem/atmodem.h [new file with mode: 0644]
drivers/atmodem/atutil.c [new file with mode: 0644]
drivers/atmodem/atutil.h [new file with mode: 0644]
drivers/atmodem/call-barring.c [new file with mode: 0644]
drivers/atmodem/call-forwarding.c [new file with mode: 0644]
drivers/atmodem/call-meter.c [new file with mode: 0644]
drivers/atmodem/call-settings.c [new file with mode: 0644]
drivers/atmodem/call-volume.c [new file with mode: 0644]
drivers/atmodem/cbs.c [new file with mode: 0644]
drivers/atmodem/devinfo.c [new file with mode: 0644]
drivers/atmodem/gnss.c [new file with mode: 0644]
drivers/atmodem/gprs-context.c [new file with mode: 0644]
drivers/atmodem/gprs.c [new file with mode: 0644]
drivers/atmodem/network-registration.c [new file with mode: 0644]
drivers/atmodem/phonebook.c [new file with mode: 0644]
drivers/atmodem/sim-auth.c [new file with mode: 0644]
drivers/atmodem/sim.c [new file with mode: 0644]
drivers/atmodem/sms.c [new file with mode: 0644]
drivers/atmodem/stk.c [new file with mode: 0644]
drivers/atmodem/stk.h [new file with mode: 0644]
drivers/atmodem/ussd.c [new file with mode: 0644]
drivers/atmodem/vendor.h [new file with mode: 0644]
drivers/atmodem/voicecall.c [new file with mode: 0644]
drivers/calypsomodem/calypsomodem.c [new file with mode: 0644]
drivers/calypsomodem/calypsomodem.h [new file with mode: 0644]
drivers/calypsomodem/stk.c [new file with mode: 0644]
drivers/calypsomodem/voicecall.c [new file with mode: 0644]
drivers/cdmamodem/cdmamodem.c [new file with mode: 0644]
drivers/cdmamodem/cdmamodem.h [new file with mode: 0644]
drivers/cdmamodem/connman.c [new file with mode: 0644]
drivers/cdmamodem/devinfo.c [new file with mode: 0644]
drivers/cdmamodem/voicecall.c [new file with mode: 0644]
drivers/dunmodem/dunmodem.c [new file with mode: 0644]
drivers/dunmodem/dunmodem.h [new file with mode: 0644]
drivers/dunmodem/gprs.c [new file with mode: 0644]
drivers/dunmodem/network-registration.c [new file with mode: 0644]
drivers/hfpmodem/call-volume.c [new file with mode: 0644]
drivers/hfpmodem/devinfo.c [new file with mode: 0644]
drivers/hfpmodem/handsfree.c [new file with mode: 0644]
drivers/hfpmodem/hfpmodem.c [new file with mode: 0644]
drivers/hfpmodem/hfpmodem.h [new file with mode: 0644]
drivers/hfpmodem/network-registration.c [new file with mode: 0644]
drivers/hfpmodem/slc.c [new file with mode: 0644]
drivers/hfpmodem/slc.h [new file with mode: 0644]
drivers/hfpmodem/voicecall.c [new file with mode: 0644]
drivers/hsomodem/gprs-context.c [new file with mode: 0644]
drivers/hsomodem/hsomodem.c [new file with mode: 0644]
drivers/hsomodem/hsomodem.h [new file with mode: 0644]
drivers/hsomodem/radio-settings.c [new file with mode: 0644]
drivers/huaweimodem/audio-settings.c [new file with mode: 0644]
drivers/huaweimodem/cdma-netreg.c [new file with mode: 0644]
drivers/huaweimodem/gprs-context.c [new file with mode: 0644]
drivers/huaweimodem/huaweimodem.c [new file with mode: 0644]
drivers/huaweimodem/huaweimodem.h [new file with mode: 0644]
drivers/huaweimodem/radio-settings.c [new file with mode: 0644]
drivers/huaweimodem/ussd.c [new file with mode: 0644]
drivers/huaweimodem/voicecall.c [new file with mode: 0644]
drivers/ifxmodem/audio-settings.c [new file with mode: 0644]
drivers/ifxmodem/ctm.c [new file with mode: 0644]
drivers/ifxmodem/gprs-context.c [new file with mode: 0644]
drivers/ifxmodem/ifxmodem.c [new file with mode: 0644]
drivers/ifxmodem/ifxmodem.h [new file with mode: 0644]
drivers/ifxmodem/radio-settings.c [new file with mode: 0644]
drivers/ifxmodem/stk.c [new file with mode: 0644]
drivers/ifxmodem/voicecall.c [new file with mode: 0644]
drivers/isimodem/audio-settings.c [new file with mode: 0644]
drivers/isimodem/call-barring.c [new file with mode: 0644]
drivers/isimodem/call-forwarding.c [new file with mode: 0644]
drivers/isimodem/call-meter.c [new file with mode: 0644]
drivers/isimodem/call-settings.c [new file with mode: 0644]
drivers/isimodem/call.h [new file with mode: 0644]
drivers/isimodem/cbs.c [new file with mode: 0644]
drivers/isimodem/debug.c [new file with mode: 0644]
drivers/isimodem/debug.h [new file with mode: 0644]
drivers/isimodem/devinfo.c [new file with mode: 0644]
drivers/isimodem/gpds.h [new file with mode: 0644]
drivers/isimodem/gprs-context.c [new file with mode: 0644]
drivers/isimodem/gprs.c [new file with mode: 0644]
drivers/isimodem/gss.h [new file with mode: 0644]
drivers/isimodem/info.h [new file with mode: 0644]
drivers/isimodem/infoserver.c [new file with mode: 0644]
drivers/isimodem/infoserver.h [new file with mode: 0644]
drivers/isimodem/isimodem.c [new file with mode: 0644]
drivers/isimodem/isimodem.h [new file with mode: 0644]
drivers/isimodem/isiutil.h [new file with mode: 0644]
drivers/isimodem/mtc.h [new file with mode: 0644]
drivers/isimodem/network-registration.c [new file with mode: 0644]
drivers/isimodem/network.h [new file with mode: 0644]
drivers/isimodem/phonebook.c [new file with mode: 0644]
drivers/isimodem/radio-settings.c [new file with mode: 0644]
drivers/isimodem/sim.c [new file with mode: 0644]
drivers/isimodem/sim.h [new file with mode: 0644]
drivers/isimodem/sms.c [new file with mode: 0644]
drivers/isimodem/sms.h [new file with mode: 0644]
drivers/isimodem/ss.h [new file with mode: 0644]
drivers/isimodem/uicc-util.c [new file with mode: 0644]
drivers/isimodem/uicc-util.h [new file with mode: 0644]
drivers/isimodem/uicc.c [new file with mode: 0644]
drivers/isimodem/uicc.h [new file with mode: 0644]
drivers/isimodem/ussd.c [new file with mode: 0644]
drivers/isimodem/voicecall.c [new file with mode: 0644]
drivers/mbmmodem/gprs-context.c [new file with mode: 0644]
drivers/mbmmodem/location-reporting.c [new file with mode: 0644]
drivers/mbmmodem/mbmmodem.c [new file with mode: 0644]
drivers/mbmmodem/mbmmodem.h [new file with mode: 0644]
drivers/mbmmodem/stk.c [new file with mode: 0644]
drivers/nwmodem/nwmodem.c [new file with mode: 0644]
drivers/nwmodem/nwmodem.h [new file with mode: 0644]
drivers/nwmodem/radio-settings.c [new file with mode: 0644]
drivers/stemodem/caif_rtnl.c [new file with mode: 0644]
drivers/stemodem/caif_rtnl.h [new file with mode: 0644]
drivers/stemodem/caif_socket.h [new file with mode: 0644]
drivers/stemodem/gprs-context.c [new file with mode: 0644]
drivers/stemodem/if_caif.h [new file with mode: 0644]
drivers/stemodem/radio-settings.c [new file with mode: 0644]
drivers/stemodem/stemodem.c [new file with mode: 0644]
drivers/stemodem/stemodem.h [new file with mode: 0644]
drivers/stemodem/voicecall.c [new file with mode: 0644]
examples/emulator.c [new file with mode: 0644]
examples/history.c [new file with mode: 0644]
examples/nettime.c [new file with mode: 0644]
examples/private-network.c [new file with mode: 0644]
examples/provision.c [new file with mode: 0644]
gatchat/crc-ccitt.c [new file with mode: 0644]
gatchat/crc-ccitt.h [new file with mode: 0644]
gatchat/gat.h [new file with mode: 0644]
gatchat/gatchat.c [new file with mode: 0644]
gatchat/gatchat.h [new file with mode: 0644]
gatchat/gathdlc.c [new file with mode: 0644]
gatchat/gathdlc.h [new file with mode: 0644]
gatchat/gatio.c [new file with mode: 0644]
gatchat/gatio.h [new file with mode: 0644]
gatchat/gatmux.c [new file with mode: 0644]
gatchat/gatmux.h [new file with mode: 0644]
gatchat/gatppp.c [new file with mode: 0644]
gatchat/gatppp.h [new file with mode: 0644]
gatchat/gatrawip.c [new file with mode: 0644]
gatchat/gatrawip.h [new file with mode: 0644]
gatchat/gatresult.c [new file with mode: 0644]
gatchat/gatresult.h [new file with mode: 0644]
gatchat/gatserver.c [new file with mode: 0644]
gatchat/gatserver.h [new file with mode: 0644]
gatchat/gatsyntax.c [new file with mode: 0644]
gatchat/gatsyntax.h [new file with mode: 0644]
gatchat/gattty.c [new file with mode: 0644]
gatchat/gattty.h [new file with mode: 0644]
gatchat/gatutil.c [new file with mode: 0644]
gatchat/gatutil.h [new file with mode: 0644]
gatchat/gsm0710.c [new file with mode: 0644]
gatchat/gsm0710.h [new file with mode: 0644]
gatchat/gsmdial.c [new file with mode: 0644]
gatchat/ppp.h [new file with mode: 0644]
gatchat/ppp_auth.c [new file with mode: 0644]
gatchat/ppp_cp.c [new file with mode: 0644]
gatchat/ppp_cp.h [new file with mode: 0644]
gatchat/ppp_ipcp.c [new file with mode: 0644]
gatchat/ppp_ipv6cp.c [new file with mode: 0644]
gatchat/ppp_lcp.c [new file with mode: 0644]
gatchat/ppp_net.c [new file with mode: 0644]
gatchat/ringbuffer.c [new file with mode: 0644]
gatchat/ringbuffer.h [new file with mode: 0644]
gatchat/test-qcdm.c [new file with mode: 0644]
gatchat/test-server.c [new file with mode: 0644]
gdbus/gdbus.h [new file with mode: 0644]
gdbus/mainloop.c [new file with mode: 0644]
gdbus/object.c [new file with mode: 0644]
gdbus/polkit.c [new file with mode: 0644]
gdbus/watch.c [new file with mode: 0644]
gisi/client.c [new file with mode: 0644]
gisi/client.h [new file with mode: 0644]
gisi/common.h [new file with mode: 0644]
gisi/iter.c [new file with mode: 0644]
gisi/iter.h [new file with mode: 0644]
gisi/message.c [new file with mode: 0644]
gisi/message.h [new file with mode: 0644]
gisi/modem.c [new file with mode: 0644]
gisi/modem.h [new file with mode: 0644]
gisi/netlink.c [new file with mode: 0644]
gisi/netlink.h [new file with mode: 0644]
gisi/pep.c [new file with mode: 0644]
gisi/pep.h [new file with mode: 0644]
gisi/phonet.h [new file with mode: 0644]
gisi/pipe.c [new file with mode: 0644]
gisi/pipe.h [new file with mode: 0644]
gisi/server.c [new file with mode: 0644]
gisi/server.h [new file with mode: 0644]
gisi/socket.c [new file with mode: 0644]
gisi/socket.h [new file with mode: 0644]
include/audio-settings.h [new file with mode: 0644]
include/call-barring.h [new file with mode: 0644]
include/call-forwarding.h [new file with mode: 0644]
include/call-meter.h [new file with mode: 0644]
include/call-settings.h [new file with mode: 0644]
include/call-volume.h [new file with mode: 0644]
include/cbs.h [new file with mode: 0644]
include/cdma-connman.h [new file with mode: 0644]
include/cdma-netreg.h [new file with mode: 0644]
include/cdma-provision.h [new file with mode: 0644]
include/cdma-sms.h [new file with mode: 0644]
include/cdma-voicecall.h [new file with mode: 0644]
include/ctm.h [new file with mode: 0644]
include/dbus.h [new file with mode: 0644]
include/devinfo.h [new file with mode: 0644]
include/emulator.h [new file with mode: 0644]
include/gnss.h [new file with mode: 0644]
include/gprs-context.h [new file with mode: 0644]
include/gprs-provision.h [new file with mode: 0644]
include/gprs.h [new file with mode: 0644]
include/handsfree.h [new file with mode: 0644]
include/history.h [new file with mode: 0644]
include/location-reporting.h [new file with mode: 0644]
include/log.h [new file with mode: 0644]
include/message-waiting.h [new file with mode: 0644]
include/modem.h [new file with mode: 0644]
include/netreg.h [new file with mode: 0644]
include/nettime.h [new file with mode: 0644]
include/phonebook.h [new file with mode: 0644]
include/plugin.h [new file with mode: 0644]
include/private-network.h [new file with mode: 0644]
include/radio-settings.h [new file with mode: 0644]
include/sim-auth.h [new file with mode: 0644]
include/sim.h [new file with mode: 0644]
include/sms.h [new file with mode: 0644]
include/stk.h [new file with mode: 0644]
include/types.h [new file with mode: 0644]
include/ussd.h [new file with mode: 0644]
include/version.h.in [new file with mode: 0644]
include/voicecall.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
ltmain.sh [new file with mode: 0755]
missing [new file with mode: 0755]
ofono.pc.in [new file with mode: 0644]
packaging/0001-ifxmodem-Add-support-for-IPv6-and-dual-mode-contexts.patch [new file with mode: 0644]
packaging/0002-ifxmodem-Add-support-for-dynamic-DNS-for-IPv6-and-du.patch [new file with mode: 0644]
packaging/0003-ifx-Setup-CSCS-to-use-GSM-for-the-aux-port.patch [new file with mode: 0644]
packaging/0004-atmodem-Fix-handling-of-IFX-signal-strength-indicati.patch [new file with mode: 0644]
packaging/0005-call-volume.c-Register-the-call-volume-interface-jus.patch [new file with mode: 0644]
packaging/0006-gisi-Remove-includes-of-glib-gtypes.h.patch [new file with mode: 0644]
packaging/0007-isimodem-Remove-includes-of-glib-gtypes.h.patch [new file with mode: 0644]
packaging/init_ofono [new file with mode: 0644]
packaging/ofono-1.4.tar.bz2 [new file with mode: 0644]
packaging/ofono.changes [new file with mode: 0644]
packaging/ofono.spec [new file with mode: 0644]
plugins/alcatel.c [new file with mode: 0644]
plugins/bluetooth.c [new file with mode: 0644]
plugins/bluetooth.h [new file with mode: 0644]
plugins/caif.c [new file with mode: 0644]
plugins/calypso.c [new file with mode: 0644]
plugins/cdma-provision.c [new file with mode: 0644]
plugins/connman.c [new file with mode: 0644]
plugins/dun_gw.c [new file with mode: 0644]
plugins/g1.c [new file with mode: 0644]
plugins/gobi.c [new file with mode: 0644]
plugins/hfp_ag.c [new file with mode: 0644]
plugins/hfp_hf.c [new file with mode: 0644]
plugins/hso.c [new file with mode: 0644]
plugins/huawei.c [new file with mode: 0644]
plugins/ifx.c [new file with mode: 0644]
plugins/isiusb.c [new file with mode: 0644]
plugins/linktop.c [new file with mode: 0644]
plugins/mbm.c [new file with mode: 0644]
plugins/mbpi.c [new file with mode: 0644]
plugins/mbpi.h [new file with mode: 0644]
plugins/n900.c [new file with mode: 0644]
plugins/nokia-gpio.c [new file with mode: 0644]
plugins/nokia-gpio.h [new file with mode: 0644]
plugins/nokia.c [new file with mode: 0644]
plugins/nokiacdma.c [new file with mode: 0644]
plugins/novatel.c [new file with mode: 0644]
plugins/ofono-speedup.rules [new file with mode: 0644]
plugins/ofono.rules [new file with mode: 0644]
plugins/palmpre.c [new file with mode: 0644]
plugins/phonesim.c [new file with mode: 0644]
plugins/phonesim.conf [new file with mode: 0644]
plugins/provision.c [new file with mode: 0644]
plugins/push-notification.c [new file with mode: 0644]
plugins/samsung.c [new file with mode: 0644]
plugins/sap.c [new file with mode: 0644]
plugins/sierra.c [new file with mode: 0644]
plugins/sim900.c [new file with mode: 0644]
plugins/smart-messaging.c [new file with mode: 0644]
plugins/speedup.c [new file with mode: 0644]
plugins/speedupcdma.c [new file with mode: 0644]
plugins/ste.c [new file with mode: 0644]
plugins/stemgr.c [new file with mode: 0644]
plugins/tc65.c [new file with mode: 0644]
plugins/telit.c [new file with mode: 0644]
plugins/u8500.c [new file with mode: 0644]
plugins/udev.c [new file with mode: 0644]
plugins/udevng.c [new file with mode: 0644]
plugins/wavecom.c [new file with mode: 0644]
plugins/zte.c [new file with mode: 0644]
src/audio-settings.c [new file with mode: 0644]
src/call-barring.c [new file with mode: 0644]
src/call-forwarding.c [new file with mode: 0644]
src/call-meter.c [new file with mode: 0644]
src/call-settings.c [new file with mode: 0644]
src/call-volume.c [new file with mode: 0644]
src/cbs.c [new file with mode: 0644]
src/cdma-connman.c [new file with mode: 0644]
src/cdma-netreg.c [new file with mode: 0644]
src/cdma-provision.c [new file with mode: 0644]
src/cdma-sms.c [new file with mode: 0644]
src/cdma-smsutil.c [new file with mode: 0644]
src/cdma-smsutil.h [new file with mode: 0644]
src/cdma-voicecall.c [new file with mode: 0644]
src/common.c [new file with mode: 0644]
src/common.h [new file with mode: 0644]
src/ctm.c [new file with mode: 0644]
src/dbus.c [new file with mode: 0644]
src/emulator.c [new file with mode: 0644]
src/genbuiltin [new file with mode: 0755]
src/gnss.c [new file with mode: 0644]
src/gnssagent.c [new file with mode: 0644]
src/gnssagent.h [new file with mode: 0644]
src/gprs-provision.c [new file with mode: 0644]
src/gprs.c [new file with mode: 0644]
src/handsfree.c [new file with mode: 0644]
src/history.c [new file with mode: 0644]
src/idmap.c [new file with mode: 0644]
src/idmap.h [new file with mode: 0644]
src/location-reporting.c [new file with mode: 0644]
src/log.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/manager.c [new file with mode: 0644]
src/message-waiting.c [new file with mode: 0644]
src/message.c [new file with mode: 0644]
src/message.h [new file with mode: 0644]
src/modem.c [new file with mode: 0644]
src/nettime.c [new file with mode: 0644]
src/network.c [new file with mode: 0644]
src/ofono.conf [new file with mode: 0644]
src/ofono.h [new file with mode: 0644]
src/ofono.service.in [new file with mode: 0644]
src/ofono.ver [new file with mode: 0644]
src/phonebook.c [new file with mode: 0644]
src/plugin.c [new file with mode: 0644]
src/private-network.c [new file with mode: 0644]
src/radio-settings.c [new file with mode: 0644]
src/sim-auth.c [new file with mode: 0644]
src/sim.c [new file with mode: 0644]
src/simfs.c [new file with mode: 0644]
src/simfs.h [new file with mode: 0644]
src/simutil.c [new file with mode: 0644]
src/simutil.h [new file with mode: 0644]
src/sms.c [new file with mode: 0644]
src/smsagent.c [new file with mode: 0644]
src/smsagent.h [new file with mode: 0644]
src/smsutil.c [new file with mode: 0644]
src/smsutil.h [new file with mode: 0644]
src/stk.c [new file with mode: 0644]
src/stkagent.c [new file with mode: 0644]
src/stkagent.h [new file with mode: 0644]
src/stkutil.c [new file with mode: 0644]
src/stkutil.h [new file with mode: 0644]
src/storage.c [new file with mode: 0644]
src/storage.h [new file with mode: 0644]
src/ussd.c [new file with mode: 0644]
src/util.c [new file with mode: 0644]
src/util.h [new file with mode: 0644]
src/voicecall.c [new file with mode: 0644]
src/watch.c [new file with mode: 0644]
test/activate-context [new file with mode: 0755]
test/answer-calls [new file with mode: 0755]
test/backtrace [new file with mode: 0755]
test/cancel-ussd [new file with mode: 0755]
test/cdma-connman-disable [new file with mode: 0755]
test/cdma-connman-enable [new file with mode: 0755]
test/cdma-dial-number [new file with mode: 0755]
test/cdma-hangup [new file with mode: 0755]
test/cdma-list-call [new file with mode: 0755]
test/cdma-set-credentials [new file with mode: 0755]
test/create-internet-context [new file with mode: 0755]
test/create-mms-context [new file with mode: 0755]
test/create-multiparty [new file with mode: 0755]
test/deactivate-all [new file with mode: 0755]
test/deactivate-context [new file with mode: 0755]
test/dial-number [new file with mode: 0755]
test/disable-call-forwarding [new file with mode: 0755]
test/disable-gprs [new file with mode: 0755]
test/disable-modem [new file with mode: 0755]
test/enable-cbs [new file with mode: 0755]
test/enable-gprs [new file with mode: 0755]
test/enable-modem [new file with mode: 0755]
test/enter-pin [new file with mode: 0755]
test/get-icon [new file with mode: 0755]
test/get-operators [new file with mode: 0755]
test/get-tech-preference [new file with mode: 0755]
test/hangup-active [new file with mode: 0755]
test/hangup-all [new file with mode: 0755]
test/initiate-ussd [new file with mode: 0755]
test/list-calls [new file with mode: 0755]
test/list-contexts [new file with mode: 0755]
test/list-messages [new file with mode: 0755]
test/list-modems [new file with mode: 0755]
test/list-operators [new file with mode: 0755]
test/lock-pin [new file with mode: 0755]
test/lockdown-modem [new file with mode: 0755]
test/monitor-ofono [new file with mode: 0755]
test/offline-modem [new file with mode: 0755]
test/online-modem [new file with mode: 0755]
test/private-chat [new file with mode: 0755]
test/process-context-settings [new file with mode: 0755]
test/receive-sms [new file with mode: 0755]
test/reject-calls [new file with mode: 0755]
test/remove-contexts [new file with mode: 0755]
test/reset-pin [new file with mode: 0755]
test/scan-for-operators [new file with mode: 0755]
test/send-sms [new file with mode: 0755]
test/send-vcal [new file with mode: 0755]
test/send-vcard [new file with mode: 0755]
test/set-call-forwarding [new file with mode: 0755]
test/set-cbs-topics [new file with mode: 0755]
test/set-context-property [new file with mode: 0755]
test/set-fast-dormancy [new file with mode: 0755]
test/set-gsm-band [new file with mode: 0755]
test/set-mic-volume [new file with mode: 0755]
test/set-mms-details [new file with mode: 0755]
test/set-roaming-allowed [new file with mode: 0755]
test/set-speaker-volume [new file with mode: 0755]
test/set-tech-preference [new file with mode: 0755]
test/set-tty [new file with mode: 0755]
test/set-umts-band [new file with mode: 0755]
test/set-use-sms-reports [new file with mode: 0755]
test/swap-calls [new file with mode: 0755]
test/test-advice-of-charge [new file with mode: 0755]
test/test-call-barring [new file with mode: 0755]
test/test-call-forwarding [new file with mode: 0755]
test/test-call-settings [new file with mode: 0755]
test/test-cbs [new file with mode: 0755]
test/test-gnss [new file with mode: 0755]
test/test-message-waiting [new file with mode: 0755]
test/test-modem [new file with mode: 0755]
test/test-network-registration [new file with mode: 0755]
test/test-phonebook [new file with mode: 0755]
test/test-push-notification [new file with mode: 0755]
test/test-smart-messaging [new file with mode: 0755]
test/test-sms [new file with mode: 0755]
test/test-ss-control-cb [new file with mode: 0755]
test/test-ss-control-cf [new file with mode: 0755]
test/test-ss-control-cs [new file with mode: 0755]
test/test-stk-menu [new file with mode: 0755]
test/test-ussd [new file with mode: 0755]
test/test-voicecall [new file with mode: 0755]
test/unlock-pin [new file with mode: 0755]
tools/auto-enable.c [new file with mode: 0644]
tools/get-location.c [new file with mode: 0644]
tools/huawei-audio.c [new file with mode: 0644]
tools/lookup-apn.c [new file with mode: 0644]
tools/lookup-provider-name.c [new file with mode: 0644]
unit/test-caif.c [new file with mode: 0644]
unit/test-cdmasms.c [new file with mode: 0644]
unit/test-common.c [new file with mode: 0644]
unit/test-idmap.c [new file with mode: 0644]
unit/test-mux.c [new file with mode: 0644]
unit/test-simutil.c [new file with mode: 0644]
unit/test-sms.c [new file with mode: 0644]
unit/test-stkutil.c [new file with mode: 0644]
unit/test-util.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..b7acb10
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,80 @@
+Denis Kenzior <denis.kenzior@intel.com>
+Marcel Holtmann <marcel.holtmann@intel.com>
+Andrzej Zaborowski <andrew.zaborowski@intel.com>
+Minjun Li <minjun.li@intel.com>
+Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
+Aki Niemi <aki.niemi@nokia.com>
+Yang Gu <yang.gu@intel.com>
+Shane Bryan <shane.bryan@linux.intel.com>
+Santtu Lakkala <inz@inz.fi>
+Andres Salomon <dilinger@collabora.co.uk>
+Alexander Kanavin <alexander.kanavin@nokia.com>
+Ismo Puustinen <ismo.h.puustinen@nokia.com>
+Zhenhua Zhang <zhenhua.zhang@intel.com>
+Jukka Saunamäki <jukka.saunamaki@nokia.com>
+Pekka Pessi <pekka.pessi@nokia.com>
+Marko Saukko <marko.saukko@gmail.com>
+Olivier Le Thanh Duong <olivier.le.thanh@collabora.co.uk>
+Ryan Raasch <ryan.raasch@gmail.com>
+Gustavo F. Padovan <padovan@profusion.mobi>
+Martin Xu <martin.xu@intel.com>
+Zhigang Li <zhigang.li@intel.com>
+Anders Gustafsson <agustafsson@gmail.com>
+Jussi Kukkonen <jku@linux.intel.com>
+Sjur Brændeland <sjur.brandeland@stericsson.com>
+João Paulo Rechi Vita <jprvita@profusion.mobi>
+Vinicius Costa Gomes <vinicius.gomes@openbossa.org>
+Inaky Perez-Gonzalez <inaky@linux.intel.com>
+Kristen Carlson Accardi <kristen@linux.intel.com>
+Matthias Günther <matgnt@gmail.com>
+Daniel Wagner <daniel.wagner@bmw-carit.de>
+Kalle Valo <kalle.valo@canonical.com>
+Pasi Miettinen <pasi.miettinen@ixonos.com>
+Florian Steinel <florian.steinel@gmail.com>
+Arun Ravindran <arunlee@gmail.com>
+Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
+Petteri Tikander <petteri.tikander@ixonos.com>
+Jeevaka Badrappan <jeevaka.badrappan@elektrobit.com>
+Frank Gau <fgau@gau-net.de>
+Kai Vehmanen <kai.vehmanen@nokia.com>
+Mika Liljeberg <mika.liljeberg@nokia.com>
+Marit Henriksen <marit.henriksen@stericsson.com>
+Guillaume Lucas <guillaumex.lucas@intel.com>
+George Matveev <george@matveev.se>
+Antti Paila <antti.paila@nokia.com>
+Rafael Ignacio Zurita <rafael.zurita@profusion.mobi>
+Helen Clemson <helen.clemson@stericsson.com>
+Jessica Nilsson <jessica.j.nilsson@stericsson.com>
+Oleg Zhurakivskyy <oleg.zhurakivskyy@nokia.com>
+Lasse Kunnasluoto <lasse.kunnasluoto@tieto.com>
+John Mathew <john.mathew@elektrobit.com>
+Benoît Monin <benoit.monin@gmx.fr>
+Dara Spieker-Doyle <dara.spieker-doyle@nokia.com>
+Neil Jerram <neil@ossau.uklinux.net>
+Lei Yu <lei.2.yu@nokia.com>
+Oskari Timperi <oskari.timperi@iki.fi>
+Faiyaz Baxamusa <faiyaz.baxamusa@nokia.com>
+Jussi Kangas <jussi.kangas@tieto.com>
+Guillaume Zajac <guillaume.zajac@linux.intel.com>
+Olivier Guiter <olivier.guiter@linux.intel.com>
+Amit Mendapara <mendapara.amit@gmail.com>
+Frédéric Danis <frederic.danis@linux.intel.com>
+Frédéric Dalleau <frederic.dalleau@linux.intel.com>
+Paavo Leinonen <paavo.leinonen@tieto.com>
+Jan Luebbe <jluebbe@debian.org>
+Antoine Reversat <a.reversat@gmail.com>
+Patrick Porlan <patrick.porlan@linux.intel.com>
+Miia Leinonen <miia.leinonen@tieto.com>
+Jarko Poutiainen <jarko.poutiainen@tieto.com>
+Bertrand Aygon <bertrand.aygon@intel.com>
+Christian Lam <christian.lam@nokia.com>
+Philippe Nunes <philippe.nunes@linux.intel.com>
+Nicolas Bertrand <nicolas.bertrand@linux.intel.com>
+Caiwen Zhang <caiwen.zhang@windriver.com>
+Bernhard Guillon <Bernhard.Guillon@hale.at>
+Michael Schloh von Bennewitz <ofonoconn@encambio.com>
+Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Mikel Astiz <mikel.astiz@bmw-carit.de>
+Christopher Vogl <christopher.vogl@hale.at>
+Syam Sidhardhan <syamsidhardh@gmail.com>
+Renat Zaripov <r.r.zaripov@gmail.com>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..3912109
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..f4c900b
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,582 @@
+ver 1.4:
+       Fix issue with new SPN watch semantics.
+       Fix issue with handling malformed emergency numbers.
+       Fix issue with missing XSIMSTATE query for Infineon.
+       Add support for new Infineon voice settings handling.
+
+ver 1.3:
+       Add support for CDMA PIN management.
+       Add support for CDMA provider name and SID.
+       Add support for Huawei USSD 8-bit PDU mode.
+       Add support for SIMCom SIM900 modems.
+
+ver 1.2:
+       Fix issue with PIN type string for network PUK.
+       Fix issue with voice dialing and Qualcomm MSM modems.
+       Fix issue with Option HSO and SIM card detection.
+       Add support for Option HSO voice call handling.
+       Add support for Huawei modem capabilities check.
+       Add support for Huawei unified GSM/UMTS and CDMA driver.
+
+ver 1.1:
+       Fix issue with Telit modem and signal strength indication.
+       Fix issue with Bluetooth and outstanding Connect/Disconnect.
+       Fix issue with Handsfree support and hanging up all calls.
+       Add support for more advanced Handsfree features.
+       Add support for exposing Bluetooth address information.
+       Add support for Mobile Provider Database provisioning.
+       Add support for CPHS SPN and short-SPN identifiers.
+       Add support for CDMA signal strength notification.
+       Add support for CDMA dormant notification.
+       Add support for CDMA network registration.
+       Add support for CDMA call waiting feature.
+       Add support for PPP IPv6 Control Protocol.
+
+ver 1.0:
+       Fix issue with phonebook driver and SIM busy.
+       Add support for SIM Access Profile client.
+       Add support for 27.007 SIM Toolkit commands.
+       Add support for Huawei CDMA data modems.
+       Add support for Huawei GPRS bearer notifications.
+       Add support for Huawei technology reporting.
+       Add support for ZTE network time reports.
+
+ver 0.53:
+       Add support for disabling data carrier detect.
+       Add support for username/password settings for CDMA.
+       Add support for Huawei network time reports.
+       Add support for Huawei CDMA modems.
+       Add support for SpeedUp CDMA modems.
+       Add support for ZTE MF631 and MF688 modems.
+
+ver 0.52:
+       Add support for SIM Toolkit user confirmation handling.
+       Add support for ZTE MF180, MF190, MF637 and MF668 modems.
+       Add support for Huawei E173 modems.
+       Add support for various SpeedUp modems.
+
+ver 0.51:
+       Fix issue with alignment and STK event lists.
+       Fix issue with alignment and STK channel data length.
+       Fix issue with Linktop device handling functionality.
+       Fix issue with detection of HP HS2330 devices.
+       Add support for UICC SIM driver for ISI modems.
+       Add support for ACFC and PFC options for PPP.
+
+ver 0.50:
+       Fix issue with STK respond on exit flag handling.
+       Fix issue with STK and canceling pending DTMF tones.
+       Fix issue with IPv4 gateway setting and Ericsson modems.
+       Add support for handling IFX emergency number list.
+       Add support for Telit UC864-G devices.
+
+ver 0.49:
+       Fix issue with missing signal on context removal.
+       Fix issue with missing cleanup for GPRS interfaces.
+       Fix issue with online setting when not powered.
+       Fix issue with memory leak in GAtChat notifiers.
+       Fix issue with PPP Protocol-Reject packet handling.
+       Add support for PPP escape sequence handling.
+       Add support for initial SMS handling for CDMA.
+
+ver 0.48:
+       Fix issue with crash due to not stopped PPP timers.
+       Fix issue with offline mode handling and Huawei modem.
+       Fix issue with missing check for Huawei modem device open.
+       Fix issue with USSD and use of non-cloned GAtChat object.
+
+ver 0.47:
+       Fix issue with entering offline mode prematurely.
+       Add support for CPHS CSP network registration handling.
+
+ver 0.46:
+       Fix issue with operator name reading and older ISI modems.
+       Fix issue with networking registration and older ISI modems.
+       Fix issue with missing handling of PIN/SIM states and ISI modems.
+       Fix issue with voice call state reporting and ISI modems.
+       Fix issue with STK handling of environment variables.
+       Fix issue with STK and empty URL for launch browser.
+       Fix issue with voice call pause character validation.
+       Fix issue with buffer length and long phone numbers.
+       Fix issue with SMS sending retries and network timeout.
+       Fix issue with missing SMS submit canceled history status.
+       Add support for cancellation of SMS submission.
+       Add support for handling SIM Toolkit display action commands.
+       Add support for handling call forwarding and SIM refresh.
+       Add support for handling EFimg and EFiidf changes.
+       Add support for handling EFmsisdn and EFsdn changes.
+       Add support for handling emergency calls without SIM.
+       Add support for handling emergency calls without PIN.
+       Add support for handling emergency number updates.
+       Add support for assisted satellite navigation interface.
+       Add support for IPv6 contexts and ISI modems.
+       Add support for dual-stack GPRS contexts.
+       Add limited support for CDMA connection manager interface.
+
+ver 0.45:
+       Fix issue with SIM Toolkit null data object.
+       Fix issue with SIM filesystem and modem release.
+       Fix issue with disconnect handling and Huawei modems.
+       Add support for improved SSN and voicecall handling.
+       Add support for SIM Toolkit Refresh handled by the modem.
+       Add support for multiple AT channels and STE modems.
+       Add support for ISI drivers and wgmodem2.5 handling.
+       Add support for optimized ringbuffer operations.
+       Add support for optimized PPP buffer management.
+
+ver 0.44:
+       Fix issue with presence detection of Bluetooth daemon.
+       Fix issue with HDLC processing and PPP server.
+       Fix issue with SIM state and PIN2/PUK2 handling.
+       Fix issue with potential SIM lockout condition.
+       Add support for basic handling of SIM Toolkit Refresh.
+       Add support for location reporting interface.
+       Add support for GPS engine and MBM modems.
+       Add support for CNAP handling and ISI modems.
+       Add support for multiple contexts and STE modems.
+       Add support for ST-Ericsson U8500 modem.
+
+ver 0.43:
+       Fix issue with PPP transmit ACCM and receive ACCM mixup.
+       Fix issue with PPP not using default ACCM in transmit.
+       Fix issue with PPP interface and EM770W modem.
+       Add support for basic modem emulator interfaces.
+       Add support for handling ATS5 command feature.
+       Add support for Linktop LW27x data cards.
+
+ver 0.42:
+       Fix issue with ECT pre-conditions check.
+       Add support for watching SIM file changes.
+       Add support for using SIM codes longer than 8 digits.
+       Add support for SPN handling with GPRS provisioning.
+       Add support for better handling COLP with IFX modem.
+       Add support for CNAP handling with IFX modem.
+       Remove support for +CSSI type SS notifications.
+
+ver 0.41:
+       Fix issue with SIM callback handling.
+       Fix issue with XTMS handling and IFX modem.
+       Add support for alphabets and SMS encoding.
+       Add support for generic PIN retries handling.
+       Add support for PIN retries and MBM modem.
+       Add support for radio settings and MBM modem.
+       Add support for cell broadcast and STE modem.
+       Add support for handling ECAV status Released.
+
+ver 0.40:
+       Fix issue with MessageCenter and MessageProxy settings.
+       Fix issue with voice call support and Calypso modem.
+       Fix issue with user busy release and ISI modem.
+       Fix issue with DTMF sending and ISI modem.
+       Add support for handling long phone numbers.
+       Add support for persisting outgoing messages.
+       Add support for GPRS provision infrastructure.
+       Add support for proper GPRS handling in offline mode.
+       Add support for handling Launch Browser proactive command.
+       Remove support for deprecated deregister method.
+
+ver 0.39:
+       Fix issue with not handling empty EFecc properly.
+       Fix issue with string length and DTMF handling.
+       Fix issue with missing info for terminal busy result.
+       Fix issue with signal strength handling and IFX modem.
+       Fix handling of SIM Toolkit enabling and IFX modem.
+       Add support for packet switched bearer notifications.
+       Add support for handling called line identification.
+       Add support for PIN retry counter interface.
+       Add support for ST-Ericsson modem init daemon.
+       Add support for Cinterion TC65 modem.
+       Add support for simple ISI client interface.
+
+ver 0.38:
+       Change CalledLine* to ConnectedLine* properties.
+       Fix issue with calling presentation property.
+       Fix issue with network time and ISI modems.
+       Fix issue with timezone reporting and HSO modems.
+       Fix issue with SIM ready status and HSO modems.
+       Fix issue with hidden caller ID and STE modems.
+       Fix issue with handling of STK Setup Menu.
+       Fix issue with missing STK text and icon checks.
+       Fix issue with missing signal strength query.
+
+ver 0.37:
+       Fix issue with parsing of un-quoted CREG / CGREG.
+       Fix issue with call forwarding for data and fax.
+       Fix issue with too short timeout for DisplayText.
+       Fix issue with handling zero length text strings.
+       Fix issue with decoding of optional SMS elements.
+       Fix issue with charset and MWI DCS decoding.
+       Fix issue with WAP push notification handling.
+       Fix issue with calling handling and ISI modem.
+       Fix issue with network interfaces and STE modem.
+       Fix issue with SIM state notification of Huawei modem.
+       Add support for radio settings handling and Huawei modem.
+       Add support for provide local info proactive command.
+       Add support for calling name presentation properties.
+       Add support for modem lockdown handling and property.
+       Add support for handling silent modem reset trigger.
+       Add support for frequency band selection interface.
+       Add support for text telephony interface.
+
+ver 0.36:
+       Fix issue with CLIR Invocation and Suppression.
+       Fix issue with power/online transition with ZTE devices.
+       Fix segmentation fault when removing Nokia Datacard.
+       Add support for Nokia CS-17 dongles.
+       Add support for Ericsson F5521gw devices.
+       Add support for CAIF network interface management.
+       Add support for COLR in generic AT modem driver.
+       Add support for SMS Point-to-Point download to UICC.
+       Add support for checking specific service availability.
+       Add support for handling null text field for STK.
+
+ver 0.35:
+       Fix issue with FDN and BDN enabled checks.
+       Fix issue with capabilities and Phonet support.
+       Fix issue with timeout for ISI network deregistration.
+       Add support for Push Notification interface.
+       Add support for Smart Messaging interface.
+       Remove generic AT command modem plugin.
+
+ver 0.34:
+       Fix issue with sim_fs_op_error handling.
+       Fix issue with not handling GPRS context driver failures.
+       Add support for multiple GPRS context activations.
+       Add support for deactivating all GPRS contexts.
+       Add support for configuring MMS context settings.
+       Add support for barred dialing indication property.
+       Add support for fast dormancy settings property.
+       Add support for handling Play Tone proactive command.
+       Add support for indicating handled STK proactive commands.
+       Add support for two active GPRS contexts with MBM modems.
+       Add support for time zone reporting with Ericsson MBM modems.
+       Add support for detecting IFX modems stuck in multiplexer mode.
+       Add support for IFX using up to three active GPRS contexts.
+       Add support for IFX device shutdown when DLC disconnects.
+       Add support for Phonesim specific configuration files.
+       Remove deprecated modem.conf support.
+
+ver 0.33:
+       Fix wrong string to enum mapping of radio settings.
+       Fix issue with MMI code to bearer class mappings.
+       Fix issue with setting correct phase from EFphase.
+       Fix issue with phonebook handling and Infineon modems.
+       Fix issue with STK session end handling and Infineon modems.
+       Fix issue with SMS handling and ISI modems.
+       Fix issue with setting SCA type and ISI modems.
+       Add support for FastDormancy property.
+       Add support for FixedDialing property to indicate FDN.
+       Add support for Infineon specific M-RAW_IP GPRS context.
+       Add support for handling Send DTMF proactive command.
+       Add support for handling SIM Toolkit text attributes.
+
+ver 0.32:
+       Fix issue with AT+VTS not using quotes.
+       Fix issue with entering PUK and Infineon modems.
+       Fix issue with SIM hotswap and Infineon modems.
+       Fix issue with hangup active and ISI modems.
+       Fix issue with logic to validate USSD strings.
+       Add support for call in progress logic to USSD handling.
+       Add support for detecting FDN enabled SIM cards.
+       Add support for accessing SIM icon storage.
+
+ver 0.31:
+       Fix issue with signal strength reporting for ISI modems.
+       Fix issue with GPRS detach reporting for ISI modems.
+       Fix issue with single voice call termination handling.
+       Fix issue with Huawei modem driver and release of voice calls.
+       Fix issue with Infineon modem driver not sending AT+CHUP.
+       Fix issue with Infineon SIM ready checking and newer firmware.
+       Add support for Infineon specific model detection handling.
+       Add support for Infineon specific audio configuration.
+       Add support for audio settings interface.
+       Add support for generic ISI modem driver.
+       Add support for N900 specific ISI modem driver.
+
+ver 0.30:
+       Fix issue with 8-bit port handling of SMS.
+       Fix issue with CBS decoding and ISI modem driver.
+       Fix issue with CBS topic settings and ISI modem driver.
+       Fix issue with username and password order for Option HSO.
+       Fix wrong power and reset paths of Calypso support.
+       Add Infineon modem plugin support.
+       Add support for Infineon specific voice call handling.
+       Add support for Infineon specific SIM ready handling.
+       Add support for Infineon signal strength reporting.
+       Add support for Infineon CNMA without PDU.
+       Add support for Infineon radio settings.
+       Add support for Huawei specific voice call handling.
+       Add Huawei audio utility for voice routing.
+
+ver 0.29:
+       Fix issue with Huawei devices initial SIM state.
+       Fix issue with Huawei devices and online support.
+       Fix SIM Toolkit User Cancel response to Set Up Call.
+       Add support for handling of Send USSD proactive command.
+       Add support for Language Notification proactive command.
+       Add support for UCS2 to GSM 7bit conversions.
+       Add support for parsing CSCS queries.
+       Add support for USSD encoding function.
+       Add support for GPRS suspended notifications.
+       Add support for messaging D-Bus interface.
+
+ver 0.28:
+       Update modem manager D-Bus API.
+       Add support for online feature for ZTE devices.
+       Add support for online feature for Huawei devices.
+       Add support for online feature for Novatel devices.
+       Add support for online feature for Option HSO devices.
+       Add support for online feature for Ericsson MBM devices.
+       Add support for online feature for ST-Ericsson devices.
+       Add support for using 8-bit SMS reference numbers by default.
+       Fix wrong code point in Portuguese alphabet table.
+       Fix issue with EFiidf reads larger than 256 bytes.
+
+ver 0.27:
+       Update network registration D-Bus API.
+       Update voice call manager D-Bus API.
+       Update connection manager D-Bus API.
+       Update message manager D-Bus API.
+       Fix issue with GPRS attach/detach logic.
+       Fix issue with GPRS context IP configuration and ISI modems.
+       Fix issue with call forwarding and ISI modems.
+       Fix issue with LockedPins in case SIM wants a PUK first.
+       Fix issue with missing reset of MNC length on SIM removal.
+       Fix issue with SIM state logic of Huawei devices.
+       Fix issue with SIM Toolkit and GSMv1 parser for MBM devices.
+       Add more features for SIM Toolkit agent support.
+       Add SIM Toolkit support for Calypso modem.
+       Add SIM Toolkit support for ST-Ericsson devices.
+       Add support for radio settings of ST-Ericsson devices.
+       Add support for hangup all voice calls functionality.
+       Add support for reading EFust, EFest and EFimg.
+       Add support for adding a default empty PDP context.
+       Add support for embedded \r and \n in responses.
+       Add support for cloning GAtChat instances.
+       Add support for Nokia Datacard devices.
+       Add support for ZTE based devices.
+       Add support for creating backtraces.
+
+ver 0.26:
+       Fix busy loop in PPP disconnect with Huawei modem.
+       Add support for MCC/MNC via network registration interface.
+       Add support for SIM Toolkit agent interface.
+       Add initial support for IPv6 PDP context.
+
+ver 0.25:
+       Fix issue with PPP IPCP and too short timeouts.
+       Fix issue with Calypso modem and DTMF chars.
+       Fix issue with detection of some Huawei devices.
+       Fix issue with SIM polling and Ericsson MBM devices.
+       Fix potential overflow with SMS and GSM extension chars.
+       Add support for OFONO_ERROR_TYPE_SIM for negative SIM status.
+       Add support for display text decoding.
+       Add support for idle text proactive command.
+       Add support for SMS proactive commands.
+
+ver 0.24:
+       Fix race condition with GRPS attach operation.
+       Fix some issues with Option based devices.
+       Fix Huawei TTY hangup on context termination.
+       Fix crash within HDLC handling.
+       Fix incorrect packet length within PPP.
+       Add support for PPP server side.
+       Add support for decoding USSD PDUs.
+       Add support for SMS status report assembly.
+       Add support for SMS bearer settings.
+       Add initial support for Bluetooth plugin.
+
+ver 0.23:
+       Fix issue with operator info when not registered.
+       Fix issue with clean PPP shutdown on device removal.
+       Add support for status report notification via CDSI.
+       Add better support for Huawei E160 and E176 devices.
+       Add full GPRS support for Novatel based devices.
+       Add support for Novatel specific radio settings.
+       Add support for Option specific radio settings.
+       Add support for setting CBS topics on Qualcomm devices.
+
+ver 0.22:
+       Fix issue with VPATH builds.
+       Fix issue with SMS and more than 31 fragments.
+       Add even more SIM Toolkit parsing support.
+       Add support for modem online property.
+
+ver 0.21:
+       Add more parsing support for SIM Toolkit.
+       Add support for SIM insertion/removal events.
+       Add support for NITZ (network time) events.
+       Add support for reading EF_ICCID information.
+       Add support for advanced PPP integration.
+       Add support for HDLC specific abstraction.
+       Add support for simpler Technology values.
+       Add support for separate MCC/MNC SIM properties.
+       Add support for GPRS context with ISI modems.
+       Add support for SMS handling with ISI modems.
+       Add support for Wavecom WMP100 based devices.
+       Add support for Option iCON 451 based devices.
+       Add support for Huawei E1552 HSDPA USB devices.
+       Add support for Dell 5530 based devices.
+
+ver 0.20:
+       Fix issue with empty operator names.
+       Add missing API documentation.
+       Add support for Huawei EM770 modem.
+       Add more detailed parsing support for SIM Toolkit.
+       Add additional functionality for AT command server.
+       Add initial PPP implementation.
+
+ver 0.19:
+       Fix parsing of EFspdi for PLMN list.
+       Fix issues with Bluetooth handsfree handling.
+       Fix non-blocking handling for AT command server.
+       Add support for network-initiated USSD requests.
+       Add utility functions for SIM Toolkit support.
+
+ver 0.18:
+       Fix handling of GPRS attach logic.
+       Fix handling of username/password settings for STE/MBM modems.
+       Add support for Bluetooth Handsfree handling.
+       Add support for USSD_STATE_USER_ACTION.
+       Add radio settings atom and driver API.
+       Add framework for AT command server.
+
+ver 0.17:
+       Add support for ST-Ericsson based modems.
+       Add support for processing CBS even if no EFcbmid.
+       Add support for devices with CSCB mode 0 only.
+       Add support for Handsfree devices via BlueZ.
+       Add CID and LAC quirk handling for Huawei modems.
+
+ver 0.16:
+       Fix unregister of operators with null MCC/MNC.
+       Fix CPHS mailbox usage for 3GPP SIM cards.
+       Add support for persistent CBS Topics list.
+       Add support for persistent primary context identifiers.
+       Add support for SIM cache indexing by phase.
+       Add netmask to HSO GPRS context driver.
+
+ver 0.15:
+       Fix missing netmask value for newer MBM devices.
+       Fix concatenation of datagram SMS messages.
+       Add support for 51.011 EFecc format.
+       Add support for Powered property to CbsManager.
+       Add utility for checking if CBS topic is in range.
+
+ver 0.14:
+       Fix some issues with modem shutdown behavior.
+       Fix reset of context settings when deactivated.
+       Fix signal strength handling for Calypso modem.
+       Add proper signal strength handling for HSO modem.
+       Add support for enabling HSO Speech Services.
+       Add modem description for newer MBM devices.
+       Add clip_timeout for HFP incoming call handling.
+       Add poll_clcc for HFP multiparty calls.
+       Add utility for testing GSM GPRS dialing.
+
+ver 0.13:
+       Add better support for call id allocation.
+       Add CLCC query when initializing modem.
+       Add DTMF tone sending support for HFP modem.
+       Add support for modem disabling on shutdown.
+
+ver 0.12:
+       Fix various issues with Calypso modem driver.
+       Fix order of CMER and CIND in SLC connection.
+       Fix issue with SMS references stored as 8 bits.
+       Add static IP configuration for newer MBM devices.
+       Add context status polling for older MBM devices.
+       Add username/password support for MBM devices.
+       Add support for Huawei specific error terminator.
+       Add support for weird naming of Dell 5530 devices.
+       Add udev rules for Option GI0201 and GTM382 modems.
+
+ver 0.11:
+       Fix issue with repeated CCWA notifications.
+       Fix issue with double-swap when 3-way dialing.
+       Add CLCC polling for better multiparty call support.
+       Add GPRS context driver for Option HSO devices.
+       Add support for GPRS interface configuration.
+
+ver 0.10:
+       Fix issues with correct ATD handling.
+       Fix issues with indication handling.
+       Add support for SMS history capability.
+       Add basic save/restore support for GPRS settings.
+       Add three-way calling support to HFP voice driver.
+       Add call volume support to HFP modem plugin.
+       Add initial support for Palm Pre modems.
+
+ver 0.9:
+       Fix issues with voice call dialing logic.
+       Fix issues with USSD decoding support.
+       Add initial GPRS support for MBM modems.
+       Add mode property to network registration.
+       Add support for advanced options in modem.conf file.
+       Add voice call driver for Bluetooth Handsfree.
+
+ver 0.8:
+       Fix crash when internal structures differ.
+       Fix issues with handling empty text messages.
+       Add driver model for TTY multiplexer support.
+       Add support for multiplexer usage with Calypso modems.
+       Add support for PhoNet/ISI call barring, forwarding and waiting.
+       Add support for PhoNet/ISI voice call handling.
+
+ver 0.7:
+       Fix handling of empty SMS text messages.
+       Fix GAtChat's next_hexstring to handle optional quotes.
+       Fix generic SIM driver to work correctly with 3G SIM cards.
+       Add utility functions to parse 2G and 3G get response data.
+       Add call volume interface to adjust speaker and mic volume.
+       Add support for basic elementary file database.
+
+ver 0.6:
+       Fix build issue with example history plugin.
+       Fix segmentation fault from SIM reading on Calypso modem.
+       Add more scripts for SMS and voice call testing.
+
+ver 0.5:
+       Fix reading of left overs in ME storage on startup.
+       Fix parsing of Enhanced Voicemail notifications.
+       Add reading of various CBS related EFs.
+       Add ability to expire PLMN wide messages.
+       Add support for national language variants.
+       Add support for PIN and PUK handling.
+       Add support for TI Calypso modem.
+       Add initial support for Novatel based devices.
+       Add initial support for Huawei based devices.
+       Add initial support for Option HSO based devices.
+       Add initial support for TTY multiplexing.
+
+ver 0.4:
+       Add atom framework and update all drivers.
+       Add support for modem driver framework.
+       Add support for static modem configuration.
+       Add support for specialized phone simulator driver.
+       Add support for HTC G1 modem devices.
+       Add support for Ericsson MBM devices.
+       Add support for AT command PDU listing.
+       Add support for low-level PhoNet/ISI pipe endpoints.
+       Add support for full non-recursive build.
+
+ver 0.3:
+       Fix support for phonebook reading.
+       Fix some issues with network registration.
+       Fix some issues with MSISDN handling.
+       Fix some issues with SIM storage support.
+       Add caching for EF-PNN and EF-OPL SIM files.
+       Add support for SIM ADN type number handling.
+       Add support for tracking message waiting indications.
+       Add support for user-provided AT parsers.
+       Add initial drafts of API documentation.
+
+ver 0.2:
+       Add more detailed handling for network names.
+       Add character set support for phonebook.
+       Add SIM file reading and writing utilities.
+       Add experimental support for MT incoming SMS store.
+       Add special support for ti_calypso based devices.
+
+ver 0.1:
+       Initial public release.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..0472dfa
--- /dev/null
@@ -0,0 +1,692 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
+                       include/dbus.h include/modem.h include/types.h \
+                       include/call-barring.h include/call-forwarding.h \
+                       include/call-meter.h include/call-settings.h \
+                       include/phonebook.h include/ussd.h \
+                       include/sms.h include/sim.h include/message-waiting.h \
+                       include/netreg.h include/voicecall.h include/devinfo.h \
+                       include/cbs.h include/call-volume.h \
+                       include/gprs.h include/gprs-context.h \
+                       include/radio-settings.h include/stk.h \
+                       include/audio-settings.h include/nettime.h \
+                       include/ctm.h include/cdma-voicecall.h \
+                       include/cdma-sms.h include/sim-auth.h \
+                       include/gprs-provision.h include/emulator.h \
+                       include/location-reporting.h \
+                       include/cdma-connman.h include/gnss.h \
+                       include/private-network.h include/cdma-netreg.h \
+                       include/cdma-provision.h include/handsfree.h
+
+nodist_pkginclude_HEADERS = include/version.h
+
+local_headers = $(foreach file,$(pkginclude_HEADERS) \
+                               $(nodist_pkginclude_HEADERS), \
+                                       include/ofono/$(notdir $(file)))
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = ofono.pc
+
+
+if DATAFILES
+dbusconfdir = @DBUS_CONFDIR@
+
+dist_dbusconf_DATA = src/ofono.conf
+
+if SYSTEMD
+systemdunitdir = @SYSTEMD_UNITDIR@
+
+systemdunit_DATA = src/ofono.service
+endif
+
+confdir = $(sysconfdir)/ofono
+
+dist_conf_DATA =
+
+statedir = $(localstatedir)/lib/ofono
+
+state_DATA =
+endif
+
+builtin_modules =
+builtin_sources =
+builtin_libadd =
+builtin_cflags =
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+                                       gdbus/object.c gdbus/polkit.c
+
+gatchat_sources = gatchat/gatchat.h gatchat/gatchat.c \
+                               gatchat/gatresult.h gatchat/gatresult.c \
+                               gatchat/gatsyntax.h gatchat/gatsyntax.c \
+                               gatchat/ringbuffer.h gatchat/ringbuffer.c \
+                               gatchat/gatio.h gatchat/gatio.c \
+                               gatchat/crc-ccitt.h gatchat/crc-ccitt.c \
+                               gatchat/gatmux.h gatchat/gatmux.c \
+                               gatchat/gsm0710.h gatchat/gsm0710.c \
+                               gatchat/gattty.h gatchat/gattty.c \
+                               gatchat/gatutil.h gatchat/gatutil.c \
+                               gatchat/gat.h \
+                               gatchat/gatserver.h gatchat/gatserver.c \
+                               gatchat/gatrawip.h gatchat/gatrawip.c \
+                               gatchat/gathdlc.c gatchat/gathdlc.h \
+                               gatchat/gatppp.c gatchat/gatppp.h \
+                               gatchat/ppp.h gatchat/ppp_cp.h \
+                               gatchat/ppp_cp.c gatchat/ppp_lcp.c \
+                               gatchat/ppp_auth.c gatchat/ppp_net.c \
+                               gatchat/ppp_ipcp.c gatchat/ppp_ipv6cp.c
+
+gisi_sources = gisi/client.c gisi/client.h gisi/common.h \
+                               gisi/iter.c gisi/iter.h \
+                               gisi/message.c gisi/message.h \
+                               gisi/modem.c gisi/modem.h \
+                               gisi/netlink.c gisi/netlink.h \
+                               gisi/pep.c gisi/pep.h \
+                               gisi/phonet.h \
+                               gisi/pipe.c gisi/pipe.h \
+                               gisi/server.c gisi/server.h \
+                               gisi/socket.c gisi/socket.h
+
+btio_sources = btio/btio.h btio/btio.c
+
+if UDEV
+builtin_modules += udev
+builtin_sources += plugins/udev.c
+builtin_cflags += @UDEV_CFLAGS@
+builtin_libadd += @UDEV_LIBS@
+
+builtin_modules += udevng
+builtin_sources += plugins/udevng.c
+endif
+
+if ISIMODEM
+builtin_modules += isimodem
+builtin_sources += $(gisi_sources) \
+                               drivers/isimodem/isimodem.h \
+                               drivers/isimodem/isimodem.c \
+                               drivers/isimodem/mtc.h \
+                               drivers/isimodem/debug.h \
+                               drivers/isimodem/isiutil.h \
+                               drivers/isimodem/debug.c \
+                               drivers/isimodem/phonebook.c \
+                               drivers/isimodem/devinfo.c \
+                               drivers/isimodem/info.h \
+                               drivers/isimodem/network-registration.c \
+                               drivers/isimodem/network.h \
+                               drivers/isimodem/infoserver.h \
+                               drivers/isimodem/infoserver.c \
+                               drivers/isimodem/voicecall.c \
+                               drivers/isimodem/call.h \
+                               drivers/isimodem/sms.c \
+                               drivers/isimodem/sms.h \
+                               drivers/isimodem/cbs.c \
+                               drivers/isimodem/sim.c \
+                               drivers/isimodem/sim.h \
+                               drivers/isimodem/ussd.c \
+                               drivers/isimodem/call-forwarding.c \
+                               drivers/isimodem/call-settings.c \
+                               drivers/isimodem/call-barring.c \
+                               drivers/isimodem/call-meter.c \
+                               drivers/isimodem/ss.h \
+                               drivers/isimodem/radio-settings.c \
+                               drivers/isimodem/gss.h \
+                               drivers/isimodem/gprs.c \
+                               drivers/isimodem/gprs-context.c \
+                               drivers/isimodem/gpds.h \
+                               drivers/isimodem/audio-settings.c \
+                               drivers/isimodem/uicc.h \
+                               drivers/isimodem/uicc.c \
+                               drivers/isimodem/uicc-util.h \
+                               drivers/isimodem/uicc-util.c
+
+builtin_modules += isiusb
+builtin_sources += plugins/isiusb.c
+
+builtin_modules += n900
+builtin_sources += plugins/n900.c plugins/nokia-gpio.h plugins/nokia-gpio.c
+
+builtin_modules += u8500
+builtin_sources += plugins/u8500.c
+endif
+
+if ATMODEM
+builtin_modules += atmodem
+builtin_sources += $(gatchat_sources) \
+                               drivers/atmodem/atmodem.h \
+                               drivers/atmodem/atmodem.c \
+                               drivers/atmodem/call-settings.c \
+                               drivers/atmodem/sms.c \
+                               drivers/atmodem/cbs.c \
+                               drivers/atmodem/call-forwarding.c \
+                               drivers/atmodem/call-meter.c \
+                               drivers/atmodem/network-registration.c \
+                               drivers/atmodem/sim.c \
+                               drivers/atmodem/stk.c \
+                               drivers/atmodem/stk.h \
+                               drivers/atmodem/ussd.c \
+                               drivers/atmodem/voicecall.c \
+                               drivers/atmodem/call-barring.c \
+                               drivers/atmodem/phonebook.c \
+                               drivers/atmodem/devinfo.c \
+                               drivers/atmodem/call-volume.c \
+                               drivers/atmodem/vendor.h \
+                               drivers/atmodem/atutil.h \
+                               drivers/atmodem/atutil.c \
+                               drivers/atmodem/gprs.c \
+                               drivers/atmodem/gprs-context.c \
+                               drivers/atmodem/sim-auth.c \
+                               drivers/atmodem/gnss.c
+
+builtin_modules += nwmodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/nwmodem/nwmodem.h \
+                       drivers/nwmodem/nwmodem.c \
+                       drivers/nwmodem/radio-settings.c
+
+builtin_modules += huaweimodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/huaweimodem/huaweimodem.h \
+                       drivers/huaweimodem/huaweimodem.c \
+                       drivers/huaweimodem/ussd.c \
+                       drivers/huaweimodem/voicecall.c \
+                       drivers/huaweimodem/audio-settings.c \
+                       drivers/huaweimodem/gprs-context.c \
+                       drivers/huaweimodem/radio-settings.c \
+                       drivers/huaweimodem/cdma-netreg.c
+
+builtin_modules += calypsomodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/calypsomodem/calypsomodem.h \
+                       drivers/calypsomodem/calypsomodem.c \
+                       drivers/calypsomodem/voicecall.c \
+                       drivers/calypsomodem/stk.c
+
+builtin_modules += mbmmodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/mbmmodem/mbmmodem.h \
+                       drivers/mbmmodem/mbmmodem.c \
+                       drivers/mbmmodem/gprs-context.c \
+                       drivers/mbmmodem/stk.c \
+                       drivers/mbmmodem/location-reporting.c
+
+builtin_modules += hsomodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/hsomodem/hsomodem.h \
+                       drivers/hsomodem/hsomodem.c \
+                       drivers/hsomodem/gprs-context.c \
+                       drivers/hsomodem/radio-settings.c
+
+builtin_modules += ifxmodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/ifxmodem/ifxmodem.h \
+                       drivers/ifxmodem/ifxmodem.c \
+                       drivers/ifxmodem/voicecall.c \
+                       drivers/ifxmodem/audio-settings.c \
+                       drivers/ifxmodem/radio-settings.c \
+                       drivers/ifxmodem/gprs-context.c \
+                       drivers/ifxmodem/stk.c \
+                       drivers/ifxmodem/ctm.c
+
+builtin_modules += stemodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/stemodem/stemodem.h \
+                       drivers/stemodem/stemodem.c \
+                       drivers/stemodem/voicecall.c \
+                       drivers/stemodem/radio-settings.c \
+                       drivers/stemodem/caif_rtnl.c \
+                       drivers/stemodem/caif_rtnl.h \
+                       drivers/stemodem/gprs-context.c \
+                       drivers/stemodem/caif_socket.h \
+                       drivers/stemodem/if_caif.h
+
+builtin_modules += dunmodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/dunmodem/dunmodem.h \
+                       drivers/dunmodem/dunmodem.c \
+                       drivers/dunmodem/network-registration.c \
+                       drivers/dunmodem/gprs.c
+
+builtin_modules += hfpmodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/hfpmodem/hfpmodem.h \
+                       drivers/hfpmodem/hfpmodem.c \
+                       drivers/hfpmodem/slc.h \
+                       drivers/hfpmodem/slc.c \
+                       drivers/hfpmodem/voicecall.c \
+                       drivers/hfpmodem/network-registration.c \
+                       drivers/hfpmodem/call-volume.c \
+                       drivers/hfpmodem/devinfo.c \
+                       drivers/hfpmodem/handsfree.c
+
+if PHONESIM
+builtin_modules += phonesim
+builtin_sources += plugins/phonesim.c
+
+if DATAFILES
+dist_conf_DATA += plugins/phonesim.conf
+endif
+endif
+
+if CDMAMODEM
+builtin_modules += cdmamodem
+builtin_sources += drivers/cdmamodem/cdmamodem.h \
+                       drivers/cdmamodem/cdmamodem.c \
+                       drivers/cdmamodem/voicecall.c \
+                       drivers/cdmamodem/devinfo.c \
+                       drivers/cdmamodem/connman.c
+endif
+
+builtin_modules += g1
+builtin_sources += plugins/g1.c
+
+builtin_modules += gobi
+builtin_sources += plugins/gobi.c
+
+builtin_modules += wavecom
+builtin_sources += plugins/wavecom.c
+
+builtin_modules += calypso
+builtin_sources += plugins/calypso.c
+
+builtin_modules += mbm
+builtin_sources += plugins/mbm.c
+
+builtin_modules += hso
+builtin_sources += plugins/hso.c
+
+builtin_modules += zte
+builtin_sources += plugins/zte.c
+
+builtin_modules += huawei
+builtin_sources += plugins/huawei.c
+
+builtin_modules += sierra
+builtin_sources += plugins/sierra.c
+
+builtin_modules += novatel
+builtin_sources += plugins/novatel.c
+
+builtin_modules += palmpre
+builtin_sources += plugins/palmpre.c
+
+builtin_modules += ifx
+builtin_sources += plugins/ifx.c
+
+builtin_modules += ste
+builtin_sources += plugins/ste.c
+
+builtin_modules += stemgr
+builtin_sources += plugins/stemgr.c
+
+builtin_modules += caif
+builtin_sources += plugins/caif.c
+
+builtin_modules += tc65
+builtin_sources += plugins/tc65.c
+
+builtin_modules += nokia
+builtin_sources += plugins/nokia.c
+
+builtin_modules += nokiacdma
+builtin_sources += plugins/nokiacdma.c
+
+builtin_modules += linktop
+builtin_sources += plugins/linktop.c
+
+builtin_modules += alcatel
+builtin_sources += plugins/alcatel.c
+
+builtin_modules += speedup
+builtin_sources += plugins/speedup.c
+
+builtin_modules += speedupcdma
+builtin_sources += plugins/speedupcdma.c
+
+builtin_modules += samsung
+builtin_sources += plugins/samsung.c
+
+builtin_modules += sim900
+builtin_sources += plugins/sim900.c
+
+if BLUETOOTH
+builtin_modules += bluetooth
+builtin_sources += plugins/bluetooth.c plugins/bluetooth.h
+
+builtin_modules += telit
+builtin_sources += plugins/telit.c plugins/bluetooth.h
+
+builtin_modules += sap
+builtin_sources += plugins/sap.c plugins/bluetooth.h
+
+builtin_modules += hfp
+builtin_sources += plugins/hfp_hf.c plugins/bluetooth.h
+
+builtin_modules += hfp_ag
+builtin_sources += plugins/hfp_ag.c plugins/bluetooth.h
+
+builtin_modules += dun_gw
+builtin_sources += plugins/dun_gw.c plugins/bluetooth.h
+
+builtin_modules += connman
+builtin_sources += plugins/connman.c
+
+builtin_sources += $(btio_sources)
+builtin_cflags += @BLUEZ_CFLAGS@
+builtin_libadd += @BLUEZ_LIBS@
+endif
+endif
+
+if PROVISION
+builtin_sources += plugins/mbpi.h plugins/mbpi.c
+
+builtin_modules += provision
+builtin_sources += plugins/provision.c
+
+builtin_modules += cdma_provision
+builtin_sources += plugins/cdma-provision.c
+endif
+
+if MAINTAINER_MODE
+builtin_modules += example_history
+builtin_sources += examples/history.c
+
+builtin_modules += example_nettime
+builtin_sources += examples/nettime.c
+
+builtin_modules += example_provision
+builtin_sources += examples/provision.c
+
+builtin_modules += example_emulator
+builtin_sources += examples/emulator.c
+
+builtin_modules += example_private_network
+builtin_sources += examples/private-network.c
+endif
+
+builtin_modules += smart_messaging
+builtin_sources += plugins/smart-messaging.c
+
+builtin_modules += push_notification
+builtin_sources += plugins/push-notification.c
+
+sbin_PROGRAMS = src/ofonod
+
+src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) src/ofono.ver \
+                       src/main.c src/ofono.h src/log.c src/plugin.c \
+                       src/modem.c src/common.h src/common.c \
+                       src/manager.c src/dbus.c src/util.h src/util.c \
+                       src/network.c src/voicecall.c src/ussd.c src/sms.c \
+                       src/call-settings.c src/call-forwarding.c \
+                       src/call-meter.c src/smsutil.h src/smsutil.c \
+                       src/call-barring.c src/sim.c src/stk.c \
+                       src/phonebook.c src/history.c src/message-waiting.c \
+                       src/simutil.h src/simutil.c src/storage.h \
+                       src/storage.c src/cbs.c src/watch.c src/call-volume.c \
+                       src/gprs.c src/idmap.h src/idmap.c \
+                       src/radio-settings.c src/stkutil.h src/stkutil.c \
+                       src/nettime.c src/stkagent.c src/stkagent.h \
+                       src/simfs.c src/simfs.h src/audio-settings.c \
+                       src/smsagent.c src/smsagent.h src/ctm.c \
+                       src/cdma-voicecall.c src/sim-auth.c \
+                       src/message.h src/message.c src/gprs-provision.c \
+                       src/emulator.c src/location-reporting.c \
+                       src/cdma-connman.c src/gnss.c \
+                       src/gnssagent.c src/gnssagent.h \
+                       src/cdma-smsutil.h src/cdma-smsutil.c \
+                       src/cdma-sms.c src/private-network.c src/cdma-netreg.c \
+                       src/cdma-provision.c src/handsfree.c
+
+src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
+
+src_ofonod_LDFLAGS = -Wl,--export-dynamic \
+                               -Wl,--version-script=$(srcdir)/src/ofono.ver
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
+CLEANFILES = $(BUILT_SOURCES) $(rules_DATA)
+
+plugindir = $(pkglibdir)/plugins
+
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+build_plugindir = $(plugindir)
+endif
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ @USB_CFLAGS@ \
+                                       $(builtin_cflags) \
+                                       -DOFONO_PLUGIN_BUILTIN \
+                                       -DPLUGINDIR=\""$(build_plugindir)"\"
+
+INCLUDES = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/src \
+                       -I$(srcdir)/gdbus -I$(srcdir)/gisi -I$(srcdir)/gatchat \
+                       -I$(srcdir)/btio
+
+doc_files = doc/overview.txt doc/ofono-paper.txt doc/release-faq.txt \
+               doc/manager-api.txt doc/modem-api.txt doc/network-api.txt \
+                       doc/voicecallmanager-api.txt doc/voicecall-api.txt \
+                       doc/call-forwarding-api.txt doc/call-settings-api.txt \
+                       doc/call-meter-api.txt doc/call-barring-api.txt \
+                       doc/supplementaryservices-api.txt \
+                       doc/connman-api.txt doc/features.txt \
+                       doc/pushnotification-api.txt \
+                       doc/smartmessaging-api.txt \
+                       doc/call-volume-api.txt doc/cell-broadcast-api.txt \
+                       doc/messagemanager-api.txt doc/message-waiting-api.txt \
+                       doc/phonebook-api.txt doc/radio-settings-api.txt \
+                       doc/sim-api.txt doc/stk-api.txt \
+                       doc/audio-settings-api.txt doc/text-telephony-api.txt \
+                       doc/calypso-modem.txt doc/message-api.txt \
+                       doc/location-reporting-api.txt
+
+
+test_scripts = test/backtrace \
+               test/create-internet-context \
+               test/create-mms-context \
+               test/activate-context \
+               test/deactivate-context \
+               test/deactivate-all \
+               test/dial-number \
+               test/list-calls \
+               test/answer-calls \
+               test/reject-calls \
+               test/create-multiparty \
+               test/private-chat \
+               test/disable-modem \
+               test/enable-modem \
+               test/enter-pin \
+               test/reset-pin \
+               test/hangup-all \
+               test/hangup-active \
+               test/set-mms-details \
+               test/set-roaming-allowed \
+               test/list-contexts \
+               test/list-modems \
+               test/list-operators \
+               test/scan-for-operators \
+               test/get-operators\
+               test/monitor-ofono \
+               test/process-context-settings \
+               test/receive-sms \
+               test/remove-contexts \
+               test/send-sms \
+               test/set-mic-volume \
+               test/set-speaker-volume \
+               test/test-stk-menu \
+               test/test-advice-of-charge \
+               test/test-call-barring \
+               test/test-call-forwarding \
+               test/test-call-settings \
+               test/test-modem \
+               test/test-network-registration \
+               test/test-phonebook \
+               test/test-cbs \
+               test/test-ss-control-cb \
+               test/test-ss-control-cf \
+               test/test-ss-control-cs \
+               test/test-voicecall \
+               test/test-ussd \
+               test/cancel-ussd \
+               test/initiate-ussd \
+               test/offline-modem \
+               test/online-modem \
+               test/get-tech-preference \
+               test/set-tech-preference \
+               test/set-use-sms-reports \
+               test/set-cbs-topics \
+               test/enable-cbs \
+               test/lock-pin \
+               test/unlock-pin \
+               test/enable-gprs \
+               test/disable-gprs \
+               test/get-icon \
+               test/set-fast-dormancy \
+               test/test-push-notification \
+               test/test-smart-messaging \
+               test/send-vcard \
+               test/send-vcal \
+               test/set-tty \
+               test/set-gsm-band \
+               test/set-umts-band \
+               test/lockdown-modem \
+               test/set-call-forwarding \
+               test/cdma-list-call \
+               test/cdma-dial-number \
+               test/cdma-hangup \
+               test/cdma-set-credentials \
+               test/disable-call-forwarding \
+               test/list-messages \
+               test/test-sms \
+               test/test-message-waiting \
+               test/cdma-connman-disable \
+               test/cdma-connman-enable \
+               test/set-context-property \
+               test/test-gnss \
+               test/swap-calls
+
+if TEST
+testdir = $(pkglibdir)/test
+test_SCRIPTS = $(test_scripts)
+endif
+
+EXTRA_DIST = src/genbuiltin plugins/ofono.rules plugins/ofono-speedup.rules \
+                               $(doc_files) $(test_scripts)
+
+dist_man_MANS = doc/ofonod.8
+
+
+unit_objects =
+
+unit_tests = unit/test-common unit/test-util unit/test-idmap \
+                               unit/test-simutil unit/test-stkutil \
+                               unit/test-sms unit/test-cdmasms
+
+noinst_PROGRAMS = $(unit_tests) \
+                       unit/test-sms-root unit/test-mux unit/test-caif
+
+unit_test_common_SOURCES = unit/test-common.c src/common.c src/util.c
+unit_test_common_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_common_OBJECTS)
+
+unit_test_util_SOURCES = unit/test-util.c src/util.c
+unit_test_util_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_utils_OBJECTS)
+
+unit_test_idmap_SOURCES = unit/test-idmap.c src/idmap.c
+unit_test_idmap_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_idmap_OBJECTS)
+
+unit_test_simutil_SOURCES = unit/test-simutil.c src/util.c \
+                                src/simutil.c src/smsutil.c src/storage.c
+unit_test_simutil_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_simutil_OBJECTS)
+
+unit_test_stkutil_SOURCES = unit/test-stkutil.c src/util.c \
+                                src/storage.c src/smsutil.c \
+                                src/simutil.c src/stkutil.c
+unit_test_stkutil_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_stkutil_OBJECTS)
+
+unit_test_sms_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
+unit_test_sms_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_sms_OBJECTS)
+
+unit_test_cdmasms_SOURCES = unit/test-cdmasms.c src/cdma-smsutil.c
+unit_test_cdmasms_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_cdmasms_OBJECTS)
+
+unit_test_sms_root_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
+unit_test_sms_root_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_sms_root_OBJECTS)
+
+unit_test_mux_SOURCES = unit/test-mux.c $(gatchat_sources)
+unit_test_mux_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_mux_OBJECTS)
+
+unit_test_caif_SOURCES = unit/test-caif.c $(gatchat_sources) \
+                                       drivers/stemodem/caif_socket.h \
+                                       drivers/stemodem/if_caif.h
+unit_test_caif_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_caif_OBJECTS)
+
+TESTS = $(unit_tests)
+
+if TOOLS
+noinst_PROGRAMS += tools/huawei-audio tools/auto-enable \
+                       tools/get-location tools/lookup-apn \
+                       tools/lookup-provider-name
+
+tools_huawei_audio_SOURCES = $(gdbus_sources) tools/huawei-audio.c
+tools_huawei_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_auto_enable_SOURCES = $(gdbus_sources) tools/auto-enable.c
+tools_auto_enable_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_get_location_SOURCES = tools/get-location.c
+tools_get_location_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_lookup_apn_SOURCES = plugins/mbpi.c plugins/mbpi.h tools/lookup-apn.c
+tools_lookup_apn_LDADD = @GLIB_LIBS@
+
+tools_lookup_provider_name_SOURCES = plugins/mbpi.c plugins/mbpi.h \
+                               tools/lookup-provider-name.c
+tools_lookup_provider_name_LDADD = @GLIB_LIBS@
+endif
+
+noinst_PROGRAMS += gatchat/gsmdial gatchat/test-server gatchat/test-qcdm
+
+gatchat_gsmdial_SOURCES = gatchat/gsmdial.c $(gatchat_sources)
+gatchat_gsmdial_LDADD = @GLIB_LIBS@
+
+gatchat_test_server_SOURCES = gatchat/test-server.c $(gatchat_sources)
+gatchat_test_server_LDADD = @GLIB_LIBS@ -lutil
+
+gatchat_test_qcdm_SOURCES = gatchat/test-qcdm.c $(gatchat_sources)
+gatchat_test_qcdm_LDADD = @GLIB_LIBS@
+
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles
+
+MAINTAINERCLEANFILES = Makefile.in \
+       aclocal.m4 configure config.h.in config.sub config.guess \
+       ltmain.sh depcomp compile missing install-sh mkinstalldirs
+
+
+src/builtin.h: src/genbuiltin config.status
+       $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+plugins/%.rules:
+       $(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@
+
+include/ofono/version.h: include/version.h
+       $(AM_V_at)$(MKDIR_P) include/ofono
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+include/ofono/%.h: $(abs_top_srcdir)/include/%.h
+       $(AM_V_at)$(MKDIR_P) include/ofono
+       $(AM_V_GEN)$(LN_S) $< $@
+
+clean-local:
+       @$(RM) -rf include/ofono
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..fbbf69f
--- /dev/null
@@ -0,0 +1,3236 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@UDEV_TRUE@am__append_1 = udev udevng
+@UDEV_TRUE@am__append_2 = plugins/udev.c plugins/udevng.c
+@UDEV_TRUE@am__append_3 = @UDEV_CFLAGS@
+@UDEV_TRUE@am__append_4 = @UDEV_LIBS@
+@ISIMODEM_TRUE@am__append_5 = isimodem isiusb n900 u8500
+@ISIMODEM_TRUE@am__append_6 = $(gisi_sources) \
+@ISIMODEM_TRUE@        drivers/isimodem/isimodem.h \
+@ISIMODEM_TRUE@        drivers/isimodem/isimodem.c \
+@ISIMODEM_TRUE@        drivers/isimodem/mtc.h drivers/isimodem/debug.h \
+@ISIMODEM_TRUE@        drivers/isimodem/isiutil.h \
+@ISIMODEM_TRUE@        drivers/isimodem/debug.c \
+@ISIMODEM_TRUE@        drivers/isimodem/phonebook.c \
+@ISIMODEM_TRUE@        drivers/isimodem/devinfo.c \
+@ISIMODEM_TRUE@        drivers/isimodem/info.h \
+@ISIMODEM_TRUE@        drivers/isimodem/network-registration.c \
+@ISIMODEM_TRUE@        drivers/isimodem/network.h \
+@ISIMODEM_TRUE@        drivers/isimodem/infoserver.h \
+@ISIMODEM_TRUE@        drivers/isimodem/infoserver.c \
+@ISIMODEM_TRUE@        drivers/isimodem/voicecall.c \
+@ISIMODEM_TRUE@        drivers/isimodem/call.h drivers/isimodem/sms.c \
+@ISIMODEM_TRUE@        drivers/isimodem/sms.h drivers/isimodem/cbs.c \
+@ISIMODEM_TRUE@        drivers/isimodem/sim.c drivers/isimodem/sim.h \
+@ISIMODEM_TRUE@        drivers/isimodem/ussd.c \
+@ISIMODEM_TRUE@        drivers/isimodem/call-forwarding.c \
+@ISIMODEM_TRUE@        drivers/isimodem/call-settings.c \
+@ISIMODEM_TRUE@        drivers/isimodem/call-barring.c \
+@ISIMODEM_TRUE@        drivers/isimodem/call-meter.c \
+@ISIMODEM_TRUE@        drivers/isimodem/ss.h \
+@ISIMODEM_TRUE@        drivers/isimodem/radio-settings.c \
+@ISIMODEM_TRUE@        drivers/isimodem/gss.h drivers/isimodem/gprs.c \
+@ISIMODEM_TRUE@        drivers/isimodem/gprs-context.c \
+@ISIMODEM_TRUE@        drivers/isimodem/gpds.h \
+@ISIMODEM_TRUE@        drivers/isimodem/audio-settings.c \
+@ISIMODEM_TRUE@        drivers/isimodem/uicc.h drivers/isimodem/uicc.c \
+@ISIMODEM_TRUE@        drivers/isimodem/uicc-util.h \
+@ISIMODEM_TRUE@        drivers/isimodem/uicc-util.c plugins/isiusb.c \
+@ISIMODEM_TRUE@        plugins/n900.c plugins/nokia-gpio.h \
+@ISIMODEM_TRUE@        plugins/nokia-gpio.c plugins/u8500.c
+@ATMODEM_TRUE@am__append_7 = atmodem nwmodem huaweimodem calypsomodem \
+@ATMODEM_TRUE@ mbmmodem hsomodem ifxmodem stemodem dunmodem \
+@ATMODEM_TRUE@ hfpmodem
+@ATMODEM_TRUE@am__append_8 = $(gatchat_sources) \
+@ATMODEM_TRUE@ drivers/atmodem/atmodem.h \
+@ATMODEM_TRUE@ drivers/atmodem/atmodem.c \
+@ATMODEM_TRUE@ drivers/atmodem/call-settings.c \
+@ATMODEM_TRUE@ drivers/atmodem/sms.c drivers/atmodem/cbs.c \
+@ATMODEM_TRUE@ drivers/atmodem/call-forwarding.c \
+@ATMODEM_TRUE@ drivers/atmodem/call-meter.c \
+@ATMODEM_TRUE@ drivers/atmodem/network-registration.c \
+@ATMODEM_TRUE@ drivers/atmodem/sim.c drivers/atmodem/stk.c \
+@ATMODEM_TRUE@ drivers/atmodem/stk.h drivers/atmodem/ussd.c \
+@ATMODEM_TRUE@ drivers/atmodem/voicecall.c \
+@ATMODEM_TRUE@ drivers/atmodem/call-barring.c \
+@ATMODEM_TRUE@ drivers/atmodem/phonebook.c \
+@ATMODEM_TRUE@ drivers/atmodem/devinfo.c \
+@ATMODEM_TRUE@ drivers/atmodem/call-volume.c \
+@ATMODEM_TRUE@ drivers/atmodem/vendor.h \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.c drivers/atmodem/gprs.c \
+@ATMODEM_TRUE@ drivers/atmodem/gprs-context.c \
+@ATMODEM_TRUE@ drivers/atmodem/sim-auth.c \
+@ATMODEM_TRUE@ drivers/atmodem/gnss.c drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/nwmodem/nwmodem.h \
+@ATMODEM_TRUE@ drivers/nwmodem/nwmodem.c \
+@ATMODEM_TRUE@ drivers/nwmodem/radio-settings.c \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/huaweimodem/huaweimodem.h \
+@ATMODEM_TRUE@ drivers/huaweimodem/huaweimodem.c \
+@ATMODEM_TRUE@ drivers/huaweimodem/ussd.c \
+@ATMODEM_TRUE@ drivers/huaweimodem/voicecall.c \
+@ATMODEM_TRUE@ drivers/huaweimodem/audio-settings.c \
+@ATMODEM_TRUE@ drivers/huaweimodem/gprs-context.c \
+@ATMODEM_TRUE@ drivers/huaweimodem/radio-settings.c \
+@ATMODEM_TRUE@ drivers/huaweimodem/cdma-netreg.c \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/calypsomodem/calypsomodem.h \
+@ATMODEM_TRUE@ drivers/calypsomodem/calypsomodem.c \
+@ATMODEM_TRUE@ drivers/calypsomodem/voicecall.c \
+@ATMODEM_TRUE@ drivers/calypsomodem/stk.c \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/mbmmodem/mbmmodem.h \
+@ATMODEM_TRUE@ drivers/mbmmodem/mbmmodem.c \
+@ATMODEM_TRUE@ drivers/mbmmodem/gprs-context.c \
+@ATMODEM_TRUE@ drivers/mbmmodem/stk.c \
+@ATMODEM_TRUE@ drivers/mbmmodem/location-reporting.c \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/hsomodem/hsomodem.h \
+@ATMODEM_TRUE@ drivers/hsomodem/hsomodem.c \
+@ATMODEM_TRUE@ drivers/hsomodem/gprs-context.c \
+@ATMODEM_TRUE@ drivers/hsomodem/radio-settings.c \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/ifxmodem/ifxmodem.h \
+@ATMODEM_TRUE@ drivers/ifxmodem/ifxmodem.c \
+@ATMODEM_TRUE@ drivers/ifxmodem/voicecall.c \
+@ATMODEM_TRUE@ drivers/ifxmodem/audio-settings.c \
+@ATMODEM_TRUE@ drivers/ifxmodem/radio-settings.c \
+@ATMODEM_TRUE@ drivers/ifxmodem/gprs-context.c \
+@ATMODEM_TRUE@ drivers/ifxmodem/stk.c drivers/ifxmodem/ctm.c \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/stemodem/stemodem.h \
+@ATMODEM_TRUE@ drivers/stemodem/stemodem.c \
+@ATMODEM_TRUE@ drivers/stemodem/voicecall.c \
+@ATMODEM_TRUE@ drivers/stemodem/radio-settings.c \
+@ATMODEM_TRUE@ drivers/stemodem/caif_rtnl.c \
+@ATMODEM_TRUE@ drivers/stemodem/caif_rtnl.h \
+@ATMODEM_TRUE@ drivers/stemodem/gprs-context.c \
+@ATMODEM_TRUE@ drivers/stemodem/caif_socket.h \
+@ATMODEM_TRUE@ drivers/stemodem/if_caif.h \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/dunmodem/dunmodem.h \
+@ATMODEM_TRUE@ drivers/dunmodem/dunmodem.c \
+@ATMODEM_TRUE@ drivers/dunmodem/network-registration.c \
+@ATMODEM_TRUE@ drivers/dunmodem/gprs.c drivers/atmodem/atutil.h \
+@ATMODEM_TRUE@ drivers/hfpmodem/hfpmodem.h \
+@ATMODEM_TRUE@ drivers/hfpmodem/hfpmodem.c \
+@ATMODEM_TRUE@ drivers/hfpmodem/slc.h drivers/hfpmodem/slc.c \
+@ATMODEM_TRUE@ drivers/hfpmodem/voicecall.c \
+@ATMODEM_TRUE@ drivers/hfpmodem/network-registration.c \
+@ATMODEM_TRUE@ drivers/hfpmodem/call-volume.c \
+@ATMODEM_TRUE@ drivers/hfpmodem/devinfo.c \
+@ATMODEM_TRUE@ drivers/hfpmodem/handsfree.c
+@ATMODEM_TRUE@@PHONESIM_TRUE@am__append_9 = phonesim
+@ATMODEM_TRUE@@PHONESIM_TRUE@am__append_10 = plugins/phonesim.c
+@ATMODEM_TRUE@@DATAFILES_TRUE@@PHONESIM_TRUE@am__append_11 = plugins/phonesim.conf
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@am__append_12 = cdmamodem
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@am__append_13 = drivers/cdmamodem/cdmamodem.h \
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@                 drivers/cdmamodem/cdmamodem.c \
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@                 drivers/cdmamodem/voicecall.c \
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@                 drivers/cdmamodem/devinfo.c \
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@                 drivers/cdmamodem/connman.c
+
+@ATMODEM_TRUE@am__append_14 = g1 gobi wavecom calypso mbm hso zte \
+@ATMODEM_TRUE@ huawei sierra novatel palmpre ifx ste stemgr \
+@ATMODEM_TRUE@ caif tc65 nokia nokiacdma linktop alcatel \
+@ATMODEM_TRUE@ speedup speedupcdma samsung sim900
+@ATMODEM_TRUE@am__append_15 = plugins/g1.c plugins/gobi.c \
+@ATMODEM_TRUE@ plugins/wavecom.c plugins/calypso.c \
+@ATMODEM_TRUE@ plugins/mbm.c plugins/hso.c plugins/zte.c \
+@ATMODEM_TRUE@ plugins/huawei.c plugins/sierra.c \
+@ATMODEM_TRUE@ plugins/novatel.c plugins/palmpre.c \
+@ATMODEM_TRUE@ plugins/ifx.c plugins/ste.c plugins/stemgr.c \
+@ATMODEM_TRUE@ plugins/caif.c plugins/tc65.c plugins/nokia.c \
+@ATMODEM_TRUE@ plugins/nokiacdma.c plugins/linktop.c \
+@ATMODEM_TRUE@ plugins/alcatel.c plugins/speedup.c \
+@ATMODEM_TRUE@ plugins/speedupcdma.c plugins/samsung.c \
+@ATMODEM_TRUE@ plugins/sim900.c
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@am__append_16 = bluetooth telit sap hfp \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ hfp_ag dun_gw connman
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@am__append_17 = plugins/bluetooth.c \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/bluetooth.h \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/telit.c \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/bluetooth.h \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/sap.c \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/bluetooth.h \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/hfp_hf.c \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/bluetooth.h \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/hfp_ag.c \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/bluetooth.h \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/dun_gw.c \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/bluetooth.h \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/connman.c \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ $(btio_sources)
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@am__append_18 = @BLUEZ_CFLAGS@
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@am__append_19 = @BLUEZ_LIBS@
+@PROVISION_TRUE@am__append_20 = plugins/mbpi.h plugins/mbpi.c \
+@PROVISION_TRUE@       plugins/provision.c plugins/cdma-provision.c
+@PROVISION_TRUE@am__append_21 = provision cdma_provision
+@MAINTAINER_MODE_TRUE@am__append_22 = example_history example_nettime \
+@MAINTAINER_MODE_TRUE@ example_provision example_emulator \
+@MAINTAINER_MODE_TRUE@ example_private_network
+@MAINTAINER_MODE_TRUE@am__append_23 = examples/history.c \
+@MAINTAINER_MODE_TRUE@ examples/nettime.c examples/provision.c \
+@MAINTAINER_MODE_TRUE@ examples/emulator.c \
+@MAINTAINER_MODE_TRUE@ examples/private-network.c
+sbin_PROGRAMS = src/ofonod$(EXEEXT)
+noinst_PROGRAMS = $(am__EXEEXT_1) unit/test-sms-root$(EXEEXT) \
+       unit/test-mux$(EXEEXT) unit/test-caif$(EXEEXT) $(am__EXEEXT_2) \
+       gatchat/gsmdial$(EXEEXT) gatchat/test-server$(EXEEXT) \
+       gatchat/test-qcdm$(EXEEXT)
+TESTS = $(am__EXEEXT_1)
+@TOOLS_TRUE@am__append_24 = tools/huawei-audio tools/auto-enable \
+@TOOLS_TRUE@                   tools/get-location tools/lookup-apn \
+@TOOLS_TRUE@                   tools/lookup-provider-name
+
+subdir = .
+DIST_COMMON = README $(am__configure_deps) $(am__dist_conf_DATA_DIST) \
+       $(am__dist_dbusconf_DATA_DIST) $(dist_man_MANS) \
+       $(pkginclude_HEADERS) $(srcdir)/Makefile.am \
+       $(srcdir)/Makefile.in $(srcdir)/config.h.in \
+       $(srcdir)/ofono.pc.in $(top_srcdir)/configure \
+       $(top_srcdir)/include/version.h.in \
+       $(top_srcdir)/src/ofono.service.in AUTHORS COPYING ChangeLog \
+       INSTALL NEWS TODO compile config.guess config.sub depcomp \
+       install-sh ltmain.sh missing
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+       $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = include/version.h src/ofono.service ofono.pc
+CONFIG_CLEAN_VPATH_FILES =
+am__EXEEXT_1 = unit/test-common$(EXEEXT) unit/test-util$(EXEEXT) \
+       unit/test-idmap$(EXEEXT) unit/test-simutil$(EXEEXT) \
+       unit/test-stkutil$(EXEEXT) unit/test-sms$(EXEEXT) \
+       unit/test-cdmasms$(EXEEXT)
+@TOOLS_TRUE@am__EXEEXT_2 = tools/huawei-audio$(EXEEXT) \
+@TOOLS_TRUE@   tools/auto-enable$(EXEEXT) \
+@TOOLS_TRUE@   tools/get-location$(EXEEXT) \
+@TOOLS_TRUE@   tools/lookup-apn$(EXEEXT) \
+@TOOLS_TRUE@   tools/lookup-provider-name$(EXEEXT)
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(testdir)" \
+       "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" \
+       "$(DESTDIR)$(dbusconfdir)" "$(DESTDIR)$(pkgconfigdir)" \
+       "$(DESTDIR)$(statedir)" "$(DESTDIR)$(systemdunitdir)" \
+       "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)"
+PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS)
+am__dirstamp = $(am__leading_dot)dirstamp
+am__objects_1 = gatchat/gatchat.$(OBJEXT) gatchat/gatresult.$(OBJEXT) \
+       gatchat/gatsyntax.$(OBJEXT) gatchat/ringbuffer.$(OBJEXT) \
+       gatchat/gatio.$(OBJEXT) gatchat/crc-ccitt.$(OBJEXT) \
+       gatchat/gatmux.$(OBJEXT) gatchat/gsm0710.$(OBJEXT) \
+       gatchat/gattty.$(OBJEXT) gatchat/gatutil.$(OBJEXT) \
+       gatchat/gatserver.$(OBJEXT) gatchat/gatrawip.$(OBJEXT) \
+       gatchat/gathdlc.$(OBJEXT) gatchat/gatppp.$(OBJEXT) \
+       gatchat/ppp_cp.$(OBJEXT) gatchat/ppp_lcp.$(OBJEXT) \
+       gatchat/ppp_auth.$(OBJEXT) gatchat/ppp_net.$(OBJEXT) \
+       gatchat/ppp_ipcp.$(OBJEXT) gatchat/ppp_ipv6cp.$(OBJEXT)
+am_gatchat_gsmdial_OBJECTS = gatchat/gsmdial.$(OBJEXT) \
+       $(am__objects_1)
+gatchat_gsmdial_OBJECTS = $(am_gatchat_gsmdial_OBJECTS)
+gatchat_gsmdial_DEPENDENCIES =
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+am_gatchat_test_qcdm_OBJECTS = gatchat/test-qcdm.$(OBJEXT) \
+       $(am__objects_1)
+gatchat_test_qcdm_OBJECTS = $(am_gatchat_test_qcdm_OBJECTS)
+gatchat_test_qcdm_DEPENDENCIES =
+am_gatchat_test_server_OBJECTS = gatchat/test-server.$(OBJEXT) \
+       $(am__objects_1)
+gatchat_test_server_OBJECTS = $(am_gatchat_test_server_OBJECTS)
+gatchat_test_server_DEPENDENCIES =
+am__src_ofonod_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
+       gdbus/watch.c gdbus/object.c gdbus/polkit.c plugins/udev.c \
+       plugins/udevng.c gisi/client.c gisi/client.h gisi/common.h \
+       gisi/iter.c gisi/iter.h gisi/message.c gisi/message.h \
+       gisi/modem.c gisi/modem.h gisi/netlink.c gisi/netlink.h \
+       gisi/pep.c gisi/pep.h gisi/phonet.h gisi/pipe.c gisi/pipe.h \
+       gisi/server.c gisi/server.h gisi/socket.c gisi/socket.h \
+       drivers/isimodem/isimodem.h drivers/isimodem/isimodem.c \
+       drivers/isimodem/mtc.h drivers/isimodem/debug.h \
+       drivers/isimodem/isiutil.h drivers/isimodem/debug.c \
+       drivers/isimodem/phonebook.c drivers/isimodem/devinfo.c \
+       drivers/isimodem/info.h \
+       drivers/isimodem/network-registration.c \
+       drivers/isimodem/network.h drivers/isimodem/infoserver.h \
+       drivers/isimodem/infoserver.c drivers/isimodem/voicecall.c \
+       drivers/isimodem/call.h drivers/isimodem/sms.c \
+       drivers/isimodem/sms.h drivers/isimodem/cbs.c \
+       drivers/isimodem/sim.c drivers/isimodem/sim.h \
+       drivers/isimodem/ussd.c drivers/isimodem/call-forwarding.c \
+       drivers/isimodem/call-settings.c \
+       drivers/isimodem/call-barring.c drivers/isimodem/call-meter.c \
+       drivers/isimodem/ss.h drivers/isimodem/radio-settings.c \
+       drivers/isimodem/gss.h drivers/isimodem/gprs.c \
+       drivers/isimodem/gprs-context.c drivers/isimodem/gpds.h \
+       drivers/isimodem/audio-settings.c drivers/isimodem/uicc.h \
+       drivers/isimodem/uicc.c drivers/isimodem/uicc-util.h \
+       drivers/isimodem/uicc-util.c plugins/isiusb.c plugins/n900.c \
+       plugins/nokia-gpio.h plugins/nokia-gpio.c plugins/u8500.c \
+       gatchat/gatchat.h gatchat/gatchat.c gatchat/gatresult.h \
+       gatchat/gatresult.c gatchat/gatsyntax.h gatchat/gatsyntax.c \
+       gatchat/ringbuffer.h gatchat/ringbuffer.c gatchat/gatio.h \
+       gatchat/gatio.c gatchat/crc-ccitt.h gatchat/crc-ccitt.c \
+       gatchat/gatmux.h gatchat/gatmux.c gatchat/gsm0710.h \
+       gatchat/gsm0710.c gatchat/gattty.h gatchat/gattty.c \
+       gatchat/gatutil.h gatchat/gatutil.c gatchat/gat.h \
+       gatchat/gatserver.h gatchat/gatserver.c gatchat/gatrawip.h \
+       gatchat/gatrawip.c gatchat/gathdlc.c gatchat/gathdlc.h \
+       gatchat/gatppp.c gatchat/gatppp.h gatchat/ppp.h \
+       gatchat/ppp_cp.h gatchat/ppp_cp.c gatchat/ppp_lcp.c \
+       gatchat/ppp_auth.c gatchat/ppp_net.c gatchat/ppp_ipcp.c \
+       gatchat/ppp_ipv6cp.c drivers/atmodem/atmodem.h \
+       drivers/atmodem/atmodem.c drivers/atmodem/call-settings.c \
+       drivers/atmodem/sms.c drivers/atmodem/cbs.c \
+       drivers/atmodem/call-forwarding.c drivers/atmodem/call-meter.c \
+       drivers/atmodem/network-registration.c drivers/atmodem/sim.c \
+       drivers/atmodem/stk.c drivers/atmodem/stk.h \
+       drivers/atmodem/ussd.c drivers/atmodem/voicecall.c \
+       drivers/atmodem/call-barring.c drivers/atmodem/phonebook.c \
+       drivers/atmodem/devinfo.c drivers/atmodem/call-volume.c \
+       drivers/atmodem/vendor.h drivers/atmodem/atutil.h \
+       drivers/atmodem/atutil.c drivers/atmodem/gprs.c \
+       drivers/atmodem/gprs-context.c drivers/atmodem/sim-auth.c \
+       drivers/atmodem/gnss.c drivers/nwmodem/nwmodem.h \
+       drivers/nwmodem/nwmodem.c drivers/nwmodem/radio-settings.c \
+       drivers/huaweimodem/huaweimodem.h \
+       drivers/huaweimodem/huaweimodem.c drivers/huaweimodem/ussd.c \
+       drivers/huaweimodem/voicecall.c \
+       drivers/huaweimodem/audio-settings.c \
+       drivers/huaweimodem/gprs-context.c \
+       drivers/huaweimodem/radio-settings.c \
+       drivers/huaweimodem/cdma-netreg.c \
+       drivers/calypsomodem/calypsomodem.h \
+       drivers/calypsomodem/calypsomodem.c \
+       drivers/calypsomodem/voicecall.c drivers/calypsomodem/stk.c \
+       drivers/mbmmodem/mbmmodem.h drivers/mbmmodem/mbmmodem.c \
+       drivers/mbmmodem/gprs-context.c drivers/mbmmodem/stk.c \
+       drivers/mbmmodem/location-reporting.c \
+       drivers/hsomodem/hsomodem.h drivers/hsomodem/hsomodem.c \
+       drivers/hsomodem/gprs-context.c \
+       drivers/hsomodem/radio-settings.c drivers/ifxmodem/ifxmodem.h \
+       drivers/ifxmodem/ifxmodem.c drivers/ifxmodem/voicecall.c \
+       drivers/ifxmodem/audio-settings.c \
+       drivers/ifxmodem/radio-settings.c \
+       drivers/ifxmodem/gprs-context.c drivers/ifxmodem/stk.c \
+       drivers/ifxmodem/ctm.c drivers/stemodem/stemodem.h \
+       drivers/stemodem/stemodem.c drivers/stemodem/voicecall.c \
+       drivers/stemodem/radio-settings.c drivers/stemodem/caif_rtnl.c \
+       drivers/stemodem/caif_rtnl.h drivers/stemodem/gprs-context.c \
+       drivers/stemodem/caif_socket.h drivers/stemodem/if_caif.h \
+       drivers/dunmodem/dunmodem.h drivers/dunmodem/dunmodem.c \
+       drivers/dunmodem/network-registration.c \
+       drivers/dunmodem/gprs.c drivers/hfpmodem/hfpmodem.h \
+       drivers/hfpmodem/hfpmodem.c drivers/hfpmodem/slc.h \
+       drivers/hfpmodem/slc.c drivers/hfpmodem/voicecall.c \
+       drivers/hfpmodem/network-registration.c \
+       drivers/hfpmodem/call-volume.c drivers/hfpmodem/devinfo.c \
+       drivers/hfpmodem/handsfree.c plugins/phonesim.c \
+       drivers/cdmamodem/cdmamodem.h drivers/cdmamodem/cdmamodem.c \
+       drivers/cdmamodem/voicecall.c drivers/cdmamodem/devinfo.c \
+       drivers/cdmamodem/connman.c plugins/g1.c plugins/gobi.c \
+       plugins/wavecom.c plugins/calypso.c plugins/mbm.c \
+       plugins/hso.c plugins/zte.c plugins/huawei.c plugins/sierra.c \
+       plugins/novatel.c plugins/palmpre.c plugins/ifx.c \
+       plugins/ste.c plugins/stemgr.c plugins/caif.c plugins/tc65.c \
+       plugins/nokia.c plugins/nokiacdma.c plugins/linktop.c \
+       plugins/alcatel.c plugins/speedup.c plugins/speedupcdma.c \
+       plugins/samsung.c plugins/sim900.c plugins/bluetooth.c \
+       plugins/bluetooth.h plugins/telit.c plugins/sap.c \
+       plugins/hfp_hf.c plugins/hfp_ag.c plugins/dun_gw.c \
+       plugins/connman.c btio/btio.h btio/btio.c plugins/mbpi.h \
+       plugins/mbpi.c plugins/provision.c plugins/cdma-provision.c \
+       examples/history.c examples/nettime.c examples/provision.c \
+       examples/emulator.c examples/private-network.c \
+       plugins/smart-messaging.c plugins/push-notification.c \
+       src/ofono.ver src/main.c src/ofono.h src/log.c src/plugin.c \
+       src/modem.c src/common.h src/common.c src/manager.c src/dbus.c \
+       src/util.h src/util.c src/network.c src/voicecall.c src/ussd.c \
+       src/sms.c src/call-settings.c src/call-forwarding.c \
+       src/call-meter.c src/smsutil.h src/smsutil.c \
+       src/call-barring.c src/sim.c src/stk.c src/phonebook.c \
+       src/history.c src/message-waiting.c src/simutil.h \
+       src/simutil.c src/storage.h src/storage.c src/cbs.c \
+       src/watch.c src/call-volume.c src/gprs.c src/idmap.h \
+       src/idmap.c src/radio-settings.c src/stkutil.h src/stkutil.c \
+       src/nettime.c src/stkagent.c src/stkagent.h src/simfs.c \
+       src/simfs.h src/audio-settings.c src/smsagent.c src/smsagent.h \
+       src/ctm.c src/cdma-voicecall.c src/sim-auth.c src/message.h \
+       src/message.c src/gprs-provision.c src/emulator.c \
+       src/location-reporting.c src/cdma-connman.c src/gnss.c \
+       src/gnssagent.c src/gnssagent.h src/cdma-smsutil.h \
+       src/cdma-smsutil.c src/cdma-sms.c src/private-network.c \
+       src/cdma-netreg.c src/cdma-provision.c src/handsfree.c
+am__objects_2 = gdbus/mainloop.$(OBJEXT) gdbus/watch.$(OBJEXT) \
+       gdbus/object.$(OBJEXT) gdbus/polkit.$(OBJEXT)
+@UDEV_TRUE@am__objects_3 = plugins/udev.$(OBJEXT) \
+@UDEV_TRUE@    plugins/udevng.$(OBJEXT)
+am__objects_4 = gisi/client.$(OBJEXT) gisi/iter.$(OBJEXT) \
+       gisi/message.$(OBJEXT) gisi/modem.$(OBJEXT) \
+       gisi/netlink.$(OBJEXT) gisi/pep.$(OBJEXT) gisi/pipe.$(OBJEXT) \
+       gisi/server.$(OBJEXT) gisi/socket.$(OBJEXT)
+@ISIMODEM_TRUE@am__objects_5 = $(am__objects_4) \
+@ISIMODEM_TRUE@        drivers/isimodem/isimodem.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/debug.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/phonebook.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/devinfo.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/network-registration.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/infoserver.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/voicecall.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/sms.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/cbs.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/sim.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/ussd.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/call-forwarding.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/call-settings.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/call-barring.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/call-meter.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/radio-settings.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/gprs.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/gprs-context.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/audio-settings.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/uicc.$(OBJEXT) \
+@ISIMODEM_TRUE@        drivers/isimodem/uicc-util.$(OBJEXT) \
+@ISIMODEM_TRUE@        plugins/isiusb.$(OBJEXT) plugins/n900.$(OBJEXT) \
+@ISIMODEM_TRUE@        plugins/nokia-gpio.$(OBJEXT) \
+@ISIMODEM_TRUE@        plugins/u8500.$(OBJEXT)
+@ATMODEM_TRUE@am__objects_6 = $(am__objects_1) \
+@ATMODEM_TRUE@ drivers/atmodem/atmodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/call-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/sms.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/cbs.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/call-forwarding.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/call-meter.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/network-registration.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/sim.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/stk.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/ussd.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/call-barring.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/phonebook.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/devinfo.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/call-volume.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/atutil.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/gprs.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/gprs-context.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/sim-auth.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/atmodem/gnss.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/nwmodem/nwmodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/nwmodem/radio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/huaweimodem/huaweimodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/huaweimodem/ussd.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/huaweimodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/huaweimodem/audio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/huaweimodem/gprs-context.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/huaweimodem/radio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/huaweimodem/cdma-netreg.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/calypsomodem/calypsomodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/calypsomodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/calypsomodem/stk.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/mbmmodem/mbmmodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/mbmmodem/gprs-context.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/mbmmodem/stk.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/mbmmodem/location-reporting.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hsomodem/hsomodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hsomodem/gprs-context.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hsomodem/radio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/ifxmodem/ifxmodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/ifxmodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/ifxmodem/audio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/ifxmodem/radio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/ifxmodem/gprs-context.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/ifxmodem/stk.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/ifxmodem/ctm.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/stemodem/stemodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/stemodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/stemodem/radio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/stemodem/caif_rtnl.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/stemodem/gprs-context.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/dunmodem/dunmodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/dunmodem/network-registration.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/dunmodem/gprs.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hfpmodem/hfpmodem.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hfpmodem/slc.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hfpmodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hfpmodem/network-registration.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hfpmodem/call-volume.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hfpmodem/devinfo.$(OBJEXT) \
+@ATMODEM_TRUE@ drivers/hfpmodem/handsfree.$(OBJEXT)
+@ATMODEM_TRUE@@PHONESIM_TRUE@am__objects_7 =  \
+@ATMODEM_TRUE@@PHONESIM_TRUE@  plugins/phonesim.$(OBJEXT)
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@am__objects_8 = drivers/cdmamodem/cdmamodem.$(OBJEXT) \
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@ drivers/cdmamodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@ drivers/cdmamodem/devinfo.$(OBJEXT) \
+@ATMODEM_TRUE@@CDMAMODEM_TRUE@ drivers/cdmamodem/connman.$(OBJEXT)
+@ATMODEM_TRUE@am__objects_9 = plugins/g1.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/gobi.$(OBJEXT) plugins/wavecom.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/calypso.$(OBJEXT) plugins/mbm.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/hso.$(OBJEXT) plugins/zte.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/huawei.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/sierra.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/novatel.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/palmpre.$(OBJEXT) plugins/ifx.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/ste.$(OBJEXT) plugins/stemgr.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/caif.$(OBJEXT) plugins/tc65.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/nokia.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/nokiacdma.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/linktop.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/alcatel.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/speedup.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/speedupcdma.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/samsung.$(OBJEXT) \
+@ATMODEM_TRUE@ plugins/sim900.$(OBJEXT)
+am__objects_10 = btio/btio.$(OBJEXT)
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@am__objects_11 =  \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/bluetooth.$(OBJEXT) \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/telit.$(OBJEXT) \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/sap.$(OBJEXT) \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/hfp_hf.$(OBJEXT) \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/hfp_ag.$(OBJEXT) \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/dun_gw.$(OBJEXT) \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ plugins/connman.$(OBJEXT) \
+@ATMODEM_TRUE@@BLUETOOTH_TRUE@ $(am__objects_10)
+@PROVISION_TRUE@am__objects_12 = plugins/mbpi.$(OBJEXT) \
+@PROVISION_TRUE@       plugins/provision.$(OBJEXT) \
+@PROVISION_TRUE@       plugins/cdma-provision.$(OBJEXT)
+@MAINTAINER_MODE_TRUE@am__objects_13 = examples/history.$(OBJEXT) \
+@MAINTAINER_MODE_TRUE@ examples/nettime.$(OBJEXT) \
+@MAINTAINER_MODE_TRUE@ examples/provision.$(OBJEXT) \
+@MAINTAINER_MODE_TRUE@ examples/emulator.$(OBJEXT) \
+@MAINTAINER_MODE_TRUE@ examples/private-network.$(OBJEXT)
+am__objects_14 = $(am__objects_3) $(am__objects_5) $(am__objects_6) \
+       $(am__objects_7) $(am__objects_8) $(am__objects_9) \
+       $(am__objects_11) $(am__objects_12) $(am__objects_13) \
+       plugins/smart-messaging.$(OBJEXT) \
+       plugins/push-notification.$(OBJEXT)
+am_src_ofonod_OBJECTS = $(am__objects_2) $(am__objects_14) \
+       src/main.$(OBJEXT) src/log.$(OBJEXT) src/plugin.$(OBJEXT) \
+       src/modem.$(OBJEXT) src/common.$(OBJEXT) src/manager.$(OBJEXT) \
+       src/dbus.$(OBJEXT) src/util.$(OBJEXT) src/network.$(OBJEXT) \
+       src/voicecall.$(OBJEXT) src/ussd.$(OBJEXT) src/sms.$(OBJEXT) \
+       src/call-settings.$(OBJEXT) src/call-forwarding.$(OBJEXT) \
+       src/call-meter.$(OBJEXT) src/smsutil.$(OBJEXT) \
+       src/call-barring.$(OBJEXT) src/sim.$(OBJEXT) src/stk.$(OBJEXT) \
+       src/phonebook.$(OBJEXT) src/history.$(OBJEXT) \
+       src/message-waiting.$(OBJEXT) src/simutil.$(OBJEXT) \
+       src/storage.$(OBJEXT) src/cbs.$(OBJEXT) src/watch.$(OBJEXT) \
+       src/call-volume.$(OBJEXT) src/gprs.$(OBJEXT) \
+       src/idmap.$(OBJEXT) src/radio-settings.$(OBJEXT) \
+       src/stkutil.$(OBJEXT) src/nettime.$(OBJEXT) \
+       src/stkagent.$(OBJEXT) src/simfs.$(OBJEXT) \
+       src/audio-settings.$(OBJEXT) src/smsagent.$(OBJEXT) \
+       src/ctm.$(OBJEXT) src/cdma-voicecall.$(OBJEXT) \
+       src/sim-auth.$(OBJEXT) src/message.$(OBJEXT) \
+       src/gprs-provision.$(OBJEXT) src/emulator.$(OBJEXT) \
+       src/location-reporting.$(OBJEXT) src/cdma-connman.$(OBJEXT) \
+       src/gnss.$(OBJEXT) src/gnssagent.$(OBJEXT) \
+       src/cdma-smsutil.$(OBJEXT) src/cdma-sms.$(OBJEXT) \
+       src/private-network.$(OBJEXT) src/cdma-netreg.$(OBJEXT) \
+       src/cdma-provision.$(OBJEXT) src/handsfree.$(OBJEXT)
+src_ofonod_OBJECTS = $(am_src_ofonod_OBJECTS)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+src_ofonod_DEPENDENCIES = $(am__DEPENDENCIES_2)
+src_ofonod_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(src_ofonod_LDFLAGS) $(LDFLAGS) -o $@
+am__tools_auto_enable_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
+       gdbus/watch.c gdbus/object.c gdbus/polkit.c \
+       tools/auto-enable.c
+@TOOLS_TRUE@am_tools_auto_enable_OBJECTS = $(am__objects_2) \
+@TOOLS_TRUE@   tools/auto-enable.$(OBJEXT)
+tools_auto_enable_OBJECTS = $(am_tools_auto_enable_OBJECTS)
+tools_auto_enable_DEPENDENCIES =
+am__tools_get_location_SOURCES_DIST = tools/get-location.c
+@TOOLS_TRUE@am_tools_get_location_OBJECTS =  \
+@TOOLS_TRUE@   tools/get-location.$(OBJEXT)
+tools_get_location_OBJECTS = $(am_tools_get_location_OBJECTS)
+tools_get_location_DEPENDENCIES =
+am__tools_huawei_audio_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
+       gdbus/watch.c gdbus/object.c gdbus/polkit.c \
+       tools/huawei-audio.c
+@TOOLS_TRUE@am_tools_huawei_audio_OBJECTS = $(am__objects_2) \
+@TOOLS_TRUE@   tools/huawei-audio.$(OBJEXT)
+tools_huawei_audio_OBJECTS = $(am_tools_huawei_audio_OBJECTS)
+tools_huawei_audio_DEPENDENCIES =
+am__tools_lookup_apn_SOURCES_DIST = plugins/mbpi.c plugins/mbpi.h \
+       tools/lookup-apn.c
+@TOOLS_TRUE@am_tools_lookup_apn_OBJECTS = plugins/mbpi.$(OBJEXT) \
+@TOOLS_TRUE@   tools/lookup-apn.$(OBJEXT)
+tools_lookup_apn_OBJECTS = $(am_tools_lookup_apn_OBJECTS)
+tools_lookup_apn_DEPENDENCIES =
+am__tools_lookup_provider_name_SOURCES_DIST = plugins/mbpi.c \
+       plugins/mbpi.h tools/lookup-provider-name.c
+@TOOLS_TRUE@am_tools_lookup_provider_name_OBJECTS =  \
+@TOOLS_TRUE@   plugins/mbpi.$(OBJEXT) \
+@TOOLS_TRUE@   tools/lookup-provider-name.$(OBJEXT)
+tools_lookup_provider_name_OBJECTS =  \
+       $(am_tools_lookup_provider_name_OBJECTS)
+tools_lookup_provider_name_DEPENDENCIES =
+am_unit_test_caif_OBJECTS = unit/test-caif.$(OBJEXT) $(am__objects_1)
+unit_test_caif_OBJECTS = $(am_unit_test_caif_OBJECTS)
+unit_test_caif_DEPENDENCIES =
+am_unit_test_cdmasms_OBJECTS = unit/test-cdmasms.$(OBJEXT) \
+       src/cdma-smsutil.$(OBJEXT)
+unit_test_cdmasms_OBJECTS = $(am_unit_test_cdmasms_OBJECTS)
+unit_test_cdmasms_DEPENDENCIES =
+am_unit_test_common_OBJECTS = unit/test-common.$(OBJEXT) \
+       src/common.$(OBJEXT) src/util.$(OBJEXT)
+unit_test_common_OBJECTS = $(am_unit_test_common_OBJECTS)
+unit_test_common_DEPENDENCIES =
+am_unit_test_idmap_OBJECTS = unit/test-idmap.$(OBJEXT) \
+       src/idmap.$(OBJEXT)
+unit_test_idmap_OBJECTS = $(am_unit_test_idmap_OBJECTS)
+unit_test_idmap_DEPENDENCIES =
+am_unit_test_mux_OBJECTS = unit/test-mux.$(OBJEXT) $(am__objects_1)
+unit_test_mux_OBJECTS = $(am_unit_test_mux_OBJECTS)
+unit_test_mux_DEPENDENCIES =
+am_unit_test_simutil_OBJECTS = unit/test-simutil.$(OBJEXT) \
+       src/util.$(OBJEXT) src/simutil.$(OBJEXT) src/smsutil.$(OBJEXT) \
+       src/storage.$(OBJEXT)
+unit_test_simutil_OBJECTS = $(am_unit_test_simutil_OBJECTS)
+unit_test_simutil_DEPENDENCIES =
+am_unit_test_sms_OBJECTS = unit/test-sms.$(OBJEXT) src/util.$(OBJEXT) \
+       src/smsutil.$(OBJEXT) src/storage.$(OBJEXT)
+unit_test_sms_OBJECTS = $(am_unit_test_sms_OBJECTS)
+unit_test_sms_DEPENDENCIES =
+am_unit_test_sms_root_OBJECTS = unit/test-sms.$(OBJEXT) \
+       src/util.$(OBJEXT) src/smsutil.$(OBJEXT) src/storage.$(OBJEXT)
+unit_test_sms_root_OBJECTS = $(am_unit_test_sms_root_OBJECTS)
+unit_test_sms_root_DEPENDENCIES =
+am_unit_test_stkutil_OBJECTS = unit/test-stkutil.$(OBJEXT) \
+       src/util.$(OBJEXT) src/storage.$(OBJEXT) src/smsutil.$(OBJEXT) \
+       src/simutil.$(OBJEXT) src/stkutil.$(OBJEXT)
+unit_test_stkutil_OBJECTS = $(am_unit_test_stkutil_OBJECTS)
+unit_test_stkutil_DEPENDENCIES =
+am_unit_test_util_OBJECTS = unit/test-util.$(OBJEXT) \
+       src/util.$(OBJEXT)
+unit_test_util_OBJECTS = $(am_unit_test_util_OBJECTS)
+unit_test_util_DEPENDENCIES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+SCRIPTS = $(test_SCRIPTS)
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+       $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo "  CC    " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo "  CCLD  " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo "  GEN   " $@;
+SOURCES = $(gatchat_gsmdial_SOURCES) $(gatchat_test_qcdm_SOURCES) \
+       $(gatchat_test_server_SOURCES) $(src_ofonod_SOURCES) \
+       $(tools_auto_enable_SOURCES) $(tools_get_location_SOURCES) \
+       $(tools_huawei_audio_SOURCES) $(tools_lookup_apn_SOURCES) \
+       $(tools_lookup_provider_name_SOURCES) \
+       $(unit_test_caif_SOURCES) $(unit_test_cdmasms_SOURCES) \
+       $(unit_test_common_SOURCES) $(unit_test_idmap_SOURCES) \
+       $(unit_test_mux_SOURCES) $(unit_test_simutil_SOURCES) \
+       $(unit_test_sms_SOURCES) $(unit_test_sms_root_SOURCES) \
+       $(unit_test_stkutil_SOURCES) $(unit_test_util_SOURCES)
+DIST_SOURCES = $(gatchat_gsmdial_SOURCES) $(gatchat_test_qcdm_SOURCES) \
+       $(gatchat_test_server_SOURCES) $(am__src_ofonod_SOURCES_DIST) \
+       $(am__tools_auto_enable_SOURCES_DIST) \
+       $(am__tools_get_location_SOURCES_DIST) \
+       $(am__tools_huawei_audio_SOURCES_DIST) \
+       $(am__tools_lookup_apn_SOURCES_DIST) \
+       $(am__tools_lookup_provider_name_SOURCES_DIST) \
+       $(unit_test_caif_SOURCES) $(unit_test_cdmasms_SOURCES) \
+       $(unit_test_common_SOURCES) $(unit_test_idmap_SOURCES) \
+       $(unit_test_mux_SOURCES) $(unit_test_simutil_SOURCES) \
+       $(unit_test_sms_SOURCES) $(unit_test_sms_root_SOURCES) \
+       $(unit_test_stkutil_SOURCES) $(unit_test_util_SOURCES)
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(dist_man_MANS)
+am__dist_conf_DATA_DIST = plugins/phonesim.conf
+am__dist_dbusconf_DATA_DIST = src/ofono.conf
+DATA = $(dist_conf_DATA) $(dist_dbusconf_DATA) $(pkgconfig_DATA) \
+       $(state_DATA) $(systemdunit_DATA)
+HEADERS = $(nodist_pkginclude_HEADERS) $(pkginclude_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+# If stdout is a non-dumb tty, use colors.  If test -t is not supported,
+# then this fails; a conservative approach.  Of course do not redirect
+# stdout here, just stderr.
+am__tty_colors = \
+red=; grn=; lgn=; blu=; std=; \
+test "X$(AM_COLOR_TESTS)" != Xno \
+&& test "X$$TERM" != Xdumb \
+&& { test "X$(AM_COLOR_TESTS)" = Xalways || test -t 1 2>/dev/null; } \
+&& { \
+  red='\e[0;31m'; \
+  grn='\e[0;32m'; \
+  lgn='\e[1;32m'; \
+  blu='\e[1;34m'; \
+  std='\e[m'; \
+}
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  { test ! -d "$(distdir)" \
+    || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+         && rm -fr "$(distdir)"; }; }
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BLUEZ_CFLAGS = @BLUEZ_CFLAGS@
+BLUEZ_LIBS = @BLUEZ_LIBS@
+CAPNG_CFLAGS = @CAPNG_CFLAGS@
+CAPNG_LIBS = @CAPNG_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_CONFDIR = @DBUS_CONFDIR@
+DBUS_DATADIR = @DBUS_DATADIR@
+DBUS_LIBS = @DBUS_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GREP = @GREP@
+GTHREAD_CFLAGS = @GTHREAD_CFLAGS@
+GTHREAD_LIBS = @GTHREAD_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_UNITDIR = @SYSTEMD_UNITDIR@
+UDEV_CFLAGS = @UDEV_CFLAGS@
+UDEV_DATADIR = @UDEV_DATADIR@
+UDEV_LIBS = @UDEV_LIBS@
+USB_CFLAGS = @USB_CFLAGS@
+USB_LIBS = @USB_LIBS@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_MAKEFLAGS = --no-print-directory
+pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
+                       include/dbus.h include/modem.h include/types.h \
+                       include/call-barring.h include/call-forwarding.h \
+                       include/call-meter.h include/call-settings.h \
+                       include/phonebook.h include/ussd.h \
+                       include/sms.h include/sim.h include/message-waiting.h \
+                       include/netreg.h include/voicecall.h include/devinfo.h \
+                       include/cbs.h include/call-volume.h \
+                       include/gprs.h include/gprs-context.h \
+                       include/radio-settings.h include/stk.h \
+                       include/audio-settings.h include/nettime.h \
+                       include/ctm.h include/cdma-voicecall.h \
+                       include/cdma-sms.h include/sim-auth.h \
+                       include/gprs-provision.h include/emulator.h \
+                       include/location-reporting.h \
+                       include/cdma-connman.h include/gnss.h \
+                       include/private-network.h include/cdma-netreg.h \
+                       include/cdma-provision.h include/handsfree.h
+
+nodist_pkginclude_HEADERS = include/version.h
+local_headers = $(foreach file,$(pkginclude_HEADERS) \
+                               $(nodist_pkginclude_HEADERS), \
+                                       include/ofono/$(notdir $(file)))
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = ofono.pc
+@DATAFILES_TRUE@dbusconfdir = @DBUS_CONFDIR@
+@DATAFILES_TRUE@dist_dbusconf_DATA = src/ofono.conf
+@DATAFILES_TRUE@@SYSTEMD_TRUE@systemdunitdir = @SYSTEMD_UNITDIR@
+@DATAFILES_TRUE@@SYSTEMD_TRUE@systemdunit_DATA = src/ofono.service
+@DATAFILES_TRUE@confdir = $(sysconfdir)/ofono
+@DATAFILES_TRUE@dist_conf_DATA = $(am__append_11)
+@DATAFILES_TRUE@statedir = $(localstatedir)/lib/ofono
+@DATAFILES_TRUE@state_DATA = 
+builtin_modules = $(am__append_1) $(am__append_5) $(am__append_7) \
+       $(am__append_9) $(am__append_12) $(am__append_14) \
+       $(am__append_16) $(am__append_21) $(am__append_22) \
+       smart_messaging push_notification
+builtin_sources = $(am__append_2) $(am__append_6) $(am__append_8) \
+       $(am__append_10) $(am__append_13) $(am__append_15) \
+       $(am__append_17) $(am__append_20) $(am__append_23) \
+       plugins/smart-messaging.c plugins/push-notification.c
+builtin_libadd = $(am__append_4) $(am__append_19)
+builtin_cflags = $(am__append_3) $(am__append_18)
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+                                       gdbus/object.c gdbus/polkit.c
+
+gatchat_sources = gatchat/gatchat.h gatchat/gatchat.c \
+                               gatchat/gatresult.h gatchat/gatresult.c \
+                               gatchat/gatsyntax.h gatchat/gatsyntax.c \
+                               gatchat/ringbuffer.h gatchat/ringbuffer.c \
+                               gatchat/gatio.h gatchat/gatio.c \
+                               gatchat/crc-ccitt.h gatchat/crc-ccitt.c \
+                               gatchat/gatmux.h gatchat/gatmux.c \
+                               gatchat/gsm0710.h gatchat/gsm0710.c \
+                               gatchat/gattty.h gatchat/gattty.c \
+                               gatchat/gatutil.h gatchat/gatutil.c \
+                               gatchat/gat.h \
+                               gatchat/gatserver.h gatchat/gatserver.c \
+                               gatchat/gatrawip.h gatchat/gatrawip.c \
+                               gatchat/gathdlc.c gatchat/gathdlc.h \
+                               gatchat/gatppp.c gatchat/gatppp.h \
+                               gatchat/ppp.h gatchat/ppp_cp.h \
+                               gatchat/ppp_cp.c gatchat/ppp_lcp.c \
+                               gatchat/ppp_auth.c gatchat/ppp_net.c \
+                               gatchat/ppp_ipcp.c gatchat/ppp_ipv6cp.c
+
+gisi_sources = gisi/client.c gisi/client.h gisi/common.h \
+                               gisi/iter.c gisi/iter.h \
+                               gisi/message.c gisi/message.h \
+                               gisi/modem.c gisi/modem.h \
+                               gisi/netlink.c gisi/netlink.h \
+                               gisi/pep.c gisi/pep.h \
+                               gisi/phonet.h \
+                               gisi/pipe.c gisi/pipe.h \
+                               gisi/server.c gisi/server.h \
+                               gisi/socket.c gisi/socket.h
+
+btio_sources = btio/btio.h btio/btio.c
+src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) src/ofono.ver \
+                       src/main.c src/ofono.h src/log.c src/plugin.c \
+                       src/modem.c src/common.h src/common.c \
+                       src/manager.c src/dbus.c src/util.h src/util.c \
+                       src/network.c src/voicecall.c src/ussd.c src/sms.c \
+                       src/call-settings.c src/call-forwarding.c \
+                       src/call-meter.c src/smsutil.h src/smsutil.c \
+                       src/call-barring.c src/sim.c src/stk.c \
+                       src/phonebook.c src/history.c src/message-waiting.c \
+                       src/simutil.h src/simutil.c src/storage.h \
+                       src/storage.c src/cbs.c src/watch.c src/call-volume.c \
+                       src/gprs.c src/idmap.h src/idmap.c \
+                       src/radio-settings.c src/stkutil.h src/stkutil.c \
+                       src/nettime.c src/stkagent.c src/stkagent.h \
+                       src/simfs.c src/simfs.h src/audio-settings.c \
+                       src/smsagent.c src/smsagent.h src/ctm.c \
+                       src/cdma-voicecall.c src/sim-auth.c \
+                       src/message.h src/message.c src/gprs-provision.c \
+                       src/emulator.c src/location-reporting.c \
+                       src/cdma-connman.c src/gnss.c \
+                       src/gnssagent.c src/gnssagent.h \
+                       src/cdma-smsutil.h src/cdma-smsutil.c \
+                       src/cdma-sms.c src/private-network.c src/cdma-netreg.c \
+                       src/cdma-provision.c src/handsfree.c
+
+src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
+src_ofonod_LDFLAGS = -Wl,--export-dynamic \
+                               -Wl,--version-script=$(srcdir)/src/ofono.ver
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+CLEANFILES = $(BUILT_SOURCES) $(rules_DATA)
+plugindir = $(pkglibdir)/plugins
+@MAINTAINER_MODE_FALSE@build_plugindir = $(plugindir)
+@MAINTAINER_MODE_TRUE@build_plugindir = $(abs_top_srcdir)/plugins/.libs
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ @USB_CFLAGS@ \
+                                       $(builtin_cflags) \
+                                       -DOFONO_PLUGIN_BUILTIN \
+                                       -DPLUGINDIR=\""$(build_plugindir)"\"
+
+INCLUDES = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/src \
+                       -I$(srcdir)/gdbus -I$(srcdir)/gisi -I$(srcdir)/gatchat \
+                       -I$(srcdir)/btio
+
+doc_files = doc/overview.txt doc/ofono-paper.txt doc/release-faq.txt \
+               doc/manager-api.txt doc/modem-api.txt doc/network-api.txt \
+                       doc/voicecallmanager-api.txt doc/voicecall-api.txt \
+                       doc/call-forwarding-api.txt doc/call-settings-api.txt \
+                       doc/call-meter-api.txt doc/call-barring-api.txt \
+                       doc/supplementaryservices-api.txt \
+                       doc/connman-api.txt doc/features.txt \
+                       doc/pushnotification-api.txt \
+                       doc/smartmessaging-api.txt \
+                       doc/call-volume-api.txt doc/cell-broadcast-api.txt \
+                       doc/messagemanager-api.txt doc/message-waiting-api.txt \
+                       doc/phonebook-api.txt doc/radio-settings-api.txt \
+                       doc/sim-api.txt doc/stk-api.txt \
+                       doc/audio-settings-api.txt doc/text-telephony-api.txt \
+                       doc/calypso-modem.txt doc/message-api.txt \
+                       doc/location-reporting-api.txt
+
+test_scripts = test/backtrace \
+               test/create-internet-context \
+               test/create-mms-context \
+               test/activate-context \
+               test/deactivate-context \
+               test/deactivate-all \
+               test/dial-number \
+               test/list-calls \
+               test/answer-calls \
+               test/reject-calls \
+               test/create-multiparty \
+               test/private-chat \
+               test/disable-modem \
+               test/enable-modem \
+               test/enter-pin \
+               test/reset-pin \
+               test/hangup-all \
+               test/hangup-active \
+               test/set-mms-details \
+               test/set-roaming-allowed \
+               test/list-contexts \
+               test/list-modems \
+               test/list-operators \
+               test/scan-for-operators \
+               test/get-operators\
+               test/monitor-ofono \
+               test/process-context-settings \
+               test/receive-sms \
+               test/remove-contexts \
+               test/send-sms \
+               test/set-mic-volume \
+               test/set-speaker-volume \
+               test/test-stk-menu \
+               test/test-advice-of-charge \
+               test/test-call-barring \
+               test/test-call-forwarding \
+               test/test-call-settings \
+               test/test-modem \
+               test/test-network-registration \
+               test/test-phonebook \
+               test/test-cbs \
+               test/test-ss-control-cb \
+               test/test-ss-control-cf \
+               test/test-ss-control-cs \
+               test/test-voicecall \
+               test/test-ussd \
+               test/cancel-ussd \
+               test/initiate-ussd \
+               test/offline-modem \
+               test/online-modem \
+               test/get-tech-preference \
+               test/set-tech-preference \
+               test/set-use-sms-reports \
+               test/set-cbs-topics \
+               test/enable-cbs \
+               test/lock-pin \
+               test/unlock-pin \
+               test/enable-gprs \
+               test/disable-gprs \
+               test/get-icon \
+               test/set-fast-dormancy \
+               test/test-push-notification \
+               test/test-smart-messaging \
+               test/send-vcard \
+               test/send-vcal \
+               test/set-tty \
+               test/set-gsm-band \
+               test/set-umts-band \
+               test/lockdown-modem \
+               test/set-call-forwarding \
+               test/cdma-list-call \
+               test/cdma-dial-number \
+               test/cdma-hangup \
+               test/cdma-set-credentials \
+               test/disable-call-forwarding \
+               test/list-messages \
+               test/test-sms \
+               test/test-message-waiting \
+               test/cdma-connman-disable \
+               test/cdma-connman-enable \
+               test/set-context-property \
+               test/test-gnss \
+               test/swap-calls
+
+@TEST_TRUE@testdir = $(pkglibdir)/test
+@TEST_TRUE@test_SCRIPTS = $(test_scripts)
+EXTRA_DIST = src/genbuiltin plugins/ofono.rules plugins/ofono-speedup.rules \
+                               $(doc_files) $(test_scripts)
+
+dist_man_MANS = doc/ofonod.8
+unit_objects = $(unit_test_common_OBJECTS) $(unit_test_utils_OBJECTS) \
+       $(unit_test_idmap_OBJECTS) $(unit_test_simutil_OBJECTS) \
+       $(unit_test_stkutil_OBJECTS) $(unit_test_sms_OBJECTS) \
+       $(unit_test_cdmasms_OBJECTS) $(unit_test_sms_root_OBJECTS) \
+       $(unit_test_mux_OBJECTS) $(unit_test_caif_OBJECTS)
+unit_tests = unit/test-common unit/test-util unit/test-idmap \
+                               unit/test-simutil unit/test-stkutil \
+                               unit/test-sms unit/test-cdmasms
+
+unit_test_common_SOURCES = unit/test-common.c src/common.c src/util.c
+unit_test_common_LDADD = @GLIB_LIBS@
+unit_test_util_SOURCES = unit/test-util.c src/util.c
+unit_test_util_LDADD = @GLIB_LIBS@
+unit_test_idmap_SOURCES = unit/test-idmap.c src/idmap.c
+unit_test_idmap_LDADD = @GLIB_LIBS@
+unit_test_simutil_SOURCES = unit/test-simutil.c src/util.c \
+                                src/simutil.c src/smsutil.c src/storage.c
+
+unit_test_simutil_LDADD = @GLIB_LIBS@
+unit_test_stkutil_SOURCES = unit/test-stkutil.c src/util.c \
+                                src/storage.c src/smsutil.c \
+                                src/simutil.c src/stkutil.c
+
+unit_test_stkutil_LDADD = @GLIB_LIBS@
+unit_test_sms_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
+unit_test_sms_LDADD = @GLIB_LIBS@
+unit_test_cdmasms_SOURCES = unit/test-cdmasms.c src/cdma-smsutil.c
+unit_test_cdmasms_LDADD = @GLIB_LIBS@
+unit_test_sms_root_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
+unit_test_sms_root_LDADD = @GLIB_LIBS@
+unit_test_mux_SOURCES = unit/test-mux.c $(gatchat_sources)
+unit_test_mux_LDADD = @GLIB_LIBS@
+unit_test_caif_SOURCES = unit/test-caif.c $(gatchat_sources) \
+                                       drivers/stemodem/caif_socket.h \
+                                       drivers/stemodem/if_caif.h
+
+unit_test_caif_LDADD = @GLIB_LIBS@
+@TOOLS_TRUE@tools_huawei_audio_SOURCES = $(gdbus_sources) tools/huawei-audio.c
+@TOOLS_TRUE@tools_huawei_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+@TOOLS_TRUE@tools_auto_enable_SOURCES = $(gdbus_sources) tools/auto-enable.c
+@TOOLS_TRUE@tools_auto_enable_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+@TOOLS_TRUE@tools_get_location_SOURCES = tools/get-location.c
+@TOOLS_TRUE@tools_get_location_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+@TOOLS_TRUE@tools_lookup_apn_SOURCES = plugins/mbpi.c plugins/mbpi.h tools/lookup-apn.c
+@TOOLS_TRUE@tools_lookup_apn_LDADD = @GLIB_LIBS@
+@TOOLS_TRUE@tools_lookup_provider_name_SOURCES = plugins/mbpi.c plugins/mbpi.h \
+@TOOLS_TRUE@                           tools/lookup-provider-name.c
+
+@TOOLS_TRUE@tools_lookup_provider_name_LDADD = @GLIB_LIBS@
+gatchat_gsmdial_SOURCES = gatchat/gsmdial.c $(gatchat_sources)
+gatchat_gsmdial_LDADD = @GLIB_LIBS@
+gatchat_test_server_SOURCES = gatchat/test-server.c $(gatchat_sources)
+gatchat_test_server_LDADD = @GLIB_LIBS@ -lutil
+gatchat_test_qcdm_SOURCES = gatchat/test-qcdm.c $(gatchat_sources)
+gatchat_test_qcdm_LDADD = @GLIB_LIBS@
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles
+MAINTAINERCLEANFILES = Makefile.in \
+       aclocal.m4 configure config.h.in config.sub config.guess \
+       ltmain.sh depcomp compile missing install-sh mkinstalldirs
+
+all: $(BUILT_SOURCES) config.h
+       $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+am--refresh:
+       @:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+             $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           echo ' $(SHELL) ./config.status'; \
+           $(SHELL) ./config.status;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+       $(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+       $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+       @if test ! -f $@; then \
+         rm -f stamp-h1; \
+         $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+       else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+       @rm -f stamp-h1
+       cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
+       ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+       rm -f stamp-h1
+       touch $@
+
+distclean-hdr:
+       -rm -f config.h stamp-h1
+include/version.h: $(top_builddir)/config.status $(top_srcdir)/include/version.h.in
+       cd $(top_builddir) && $(SHELL) ./config.status $@
+src/ofono.service: $(top_builddir)/config.status $(top_srcdir)/src/ofono.service.in
+       cd $(top_builddir) && $(SHELL) ./config.status $@
+ofono.pc: $(top_builddir)/config.status $(srcdir)/ofono.pc.in
+       cd $(top_builddir) && $(SHELL) ./config.status $@
+
+clean-noinstPROGRAMS:
+       @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+       @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+       for p in $$list; do echo "$$p $$p"; done | \
+       sed 's/$(EXEEXT)$$//' | \
+       while read p p1; do if test -f $$p || test -f $$p1; \
+         then echo "$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+           -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+       sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) files[d] = files[d] " " $$1; \
+           else { print "f", $$3 "/" $$4, $$1; } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+           if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+           test -z "$$files" || { \
+           echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+           $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+           } \
+       ; done
+
+uninstall-sbinPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+       files=`for p in $$list; do echo "$$p"; done | \
+         sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+             -e 's/$$/$(EXEEXT)/' `; \
+       test -n "$$list" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+       @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+gatchat/$(am__dirstamp):
+       @$(MKDIR_P) gatchat
+       @: > gatchat/$(am__dirstamp)
+gatchat/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) gatchat/$(DEPDIR)
+       @: > gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gsmdial.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatchat.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatresult.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatsyntax.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/ringbuffer.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatio.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/crc-ccitt.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatmux.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gsm0710.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gattty.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatutil.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatserver.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatrawip.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gathdlc.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatppp.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/ppp_cp.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/ppp_lcp.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/ppp_auth.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/ppp_net.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/ppp_ipcp.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/ppp_ipv6cp.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gsmdial$(EXEEXT): $(gatchat_gsmdial_OBJECTS) $(gatchat_gsmdial_DEPENDENCIES) gatchat/$(am__dirstamp)
+       @rm -f gatchat/gsmdial$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(gatchat_gsmdial_OBJECTS) $(gatchat_gsmdial_LDADD) $(LIBS)
+gatchat/test-qcdm.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/test-qcdm$(EXEEXT): $(gatchat_test_qcdm_OBJECTS) $(gatchat_test_qcdm_DEPENDENCIES) gatchat/$(am__dirstamp)
+       @rm -f gatchat/test-qcdm$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(gatchat_test_qcdm_OBJECTS) $(gatchat_test_qcdm_LDADD) $(LIBS)
+gatchat/test-server.$(OBJEXT): gatchat/$(am__dirstamp) \
+       gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/test-server$(EXEEXT): $(gatchat_test_server_OBJECTS) $(gatchat_test_server_DEPENDENCIES) gatchat/$(am__dirstamp)
+       @rm -f gatchat/test-server$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(gatchat_test_server_OBJECTS) $(gatchat_test_server_LDADD) $(LIBS)
+gdbus/$(am__dirstamp):
+       @$(MKDIR_P) gdbus
+       @: > gdbus/$(am__dirstamp)
+gdbus/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) gdbus/$(DEPDIR)
+       @: > gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/mainloop.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/watch.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/object.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/polkit.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+plugins/$(am__dirstamp):
+       @$(MKDIR_P) plugins
+       @: > plugins/$(am__dirstamp)
+plugins/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) plugins/$(DEPDIR)
+       @: > plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/udev.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/udevng.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+gisi/$(am__dirstamp):
+       @$(MKDIR_P) gisi
+       @: > gisi/$(am__dirstamp)
+gisi/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) gisi/$(DEPDIR)
+       @: > gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/client.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/iter.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/message.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/modem.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/netlink.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/pep.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/pipe.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/server.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+gisi/socket.$(OBJEXT): gisi/$(am__dirstamp) \
+       gisi/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/isimodem
+       @: > drivers/isimodem/$(am__dirstamp)
+drivers/isimodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/isimodem/$(DEPDIR)
+       @: > drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/isimodem.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/debug.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/phonebook.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/devinfo.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/network-registration.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/infoserver.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/voicecall.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/sms.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/cbs.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/sim.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/ussd.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/call-forwarding.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/call-settings.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/call-barring.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/call-meter.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/radio-settings.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/gprs.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/gprs-context.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/audio-settings.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/uicc.$(OBJEXT): drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/isimodem/uicc-util.$(OBJEXT):  \
+       drivers/isimodem/$(am__dirstamp) \
+       drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+plugins/isiusb.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/n900.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/nokia-gpio.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/u8500.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/atmodem
+       @: > drivers/atmodem/$(am__dirstamp)
+drivers/atmodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/atmodem/$(DEPDIR)
+       @: > drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/atmodem.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/call-settings.$(OBJEXT):  \
+       drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/sms.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/cbs.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/call-forwarding.$(OBJEXT):  \
+       drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/call-meter.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/network-registration.$(OBJEXT):  \
+       drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/sim.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/stk.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/ussd.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/voicecall.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/call-barring.$(OBJEXT):  \
+       drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/phonebook.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/devinfo.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/call-volume.$(OBJEXT):  \
+       drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/atutil.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/gprs.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/gprs-context.$(OBJEXT):  \
+       drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/sim-auth.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/atmodem/gnss.$(OBJEXT): drivers/atmodem/$(am__dirstamp) \
+       drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/nwmodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/nwmodem
+       @: > drivers/nwmodem/$(am__dirstamp)
+drivers/nwmodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/nwmodem/$(DEPDIR)
+       @: > drivers/nwmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/nwmodem/nwmodem.$(OBJEXT): drivers/nwmodem/$(am__dirstamp) \
+       drivers/nwmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/nwmodem/radio-settings.$(OBJEXT):  \
+       drivers/nwmodem/$(am__dirstamp) \
+       drivers/nwmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/huaweimodem
+       @: > drivers/huaweimodem/$(am__dirstamp)
+drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/huaweimodem/$(DEPDIR)
+       @: > drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/huaweimodem.$(OBJEXT):  \
+       drivers/huaweimodem/$(am__dirstamp) \
+       drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/ussd.$(OBJEXT):  \
+       drivers/huaweimodem/$(am__dirstamp) \
+       drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/voicecall.$(OBJEXT):  \
+       drivers/huaweimodem/$(am__dirstamp) \
+       drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/audio-settings.$(OBJEXT):  \
+       drivers/huaweimodem/$(am__dirstamp) \
+       drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/gprs-context.$(OBJEXT):  \
+       drivers/huaweimodem/$(am__dirstamp) \
+       drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/radio-settings.$(OBJEXT):  \
+       drivers/huaweimodem/$(am__dirstamp) \
+       drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/huaweimodem/cdma-netreg.$(OBJEXT):  \
+       drivers/huaweimodem/$(am__dirstamp) \
+       drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+drivers/calypsomodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/calypsomodem
+       @: > drivers/calypsomodem/$(am__dirstamp)
+drivers/calypsomodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/calypsomodem/$(DEPDIR)
+       @: > drivers/calypsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/calypsomodem/calypsomodem.$(OBJEXT):  \
+       drivers/calypsomodem/$(am__dirstamp) \
+       drivers/calypsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/calypsomodem/voicecall.$(OBJEXT):  \
+       drivers/calypsomodem/$(am__dirstamp) \
+       drivers/calypsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/calypsomodem/stk.$(OBJEXT):  \
+       drivers/calypsomodem/$(am__dirstamp) \
+       drivers/calypsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/mbmmodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/mbmmodem
+       @: > drivers/mbmmodem/$(am__dirstamp)
+drivers/mbmmodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/mbmmodem/$(DEPDIR)
+       @: > drivers/mbmmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/mbmmodem/mbmmodem.$(OBJEXT): drivers/mbmmodem/$(am__dirstamp) \
+       drivers/mbmmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/mbmmodem/gprs-context.$(OBJEXT):  \
+       drivers/mbmmodem/$(am__dirstamp) \
+       drivers/mbmmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/mbmmodem/stk.$(OBJEXT): drivers/mbmmodem/$(am__dirstamp) \
+       drivers/mbmmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/mbmmodem/location-reporting.$(OBJEXT):  \
+       drivers/mbmmodem/$(am__dirstamp) \
+       drivers/mbmmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hsomodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/hsomodem
+       @: > drivers/hsomodem/$(am__dirstamp)
+drivers/hsomodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/hsomodem/$(DEPDIR)
+       @: > drivers/hsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hsomodem/hsomodem.$(OBJEXT): drivers/hsomodem/$(am__dirstamp) \
+       drivers/hsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hsomodem/gprs-context.$(OBJEXT):  \
+       drivers/hsomodem/$(am__dirstamp) \
+       drivers/hsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hsomodem/radio-settings.$(OBJEXT):  \
+       drivers/hsomodem/$(am__dirstamp) \
+       drivers/hsomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/ifxmodem
+       @: > drivers/ifxmodem/$(am__dirstamp)
+drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/ifxmodem/$(DEPDIR)
+       @: > drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/ifxmodem.$(OBJEXT): drivers/ifxmodem/$(am__dirstamp) \
+       drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/voicecall.$(OBJEXT):  \
+       drivers/ifxmodem/$(am__dirstamp) \
+       drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/audio-settings.$(OBJEXT):  \
+       drivers/ifxmodem/$(am__dirstamp) \
+       drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/radio-settings.$(OBJEXT):  \
+       drivers/ifxmodem/$(am__dirstamp) \
+       drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/gprs-context.$(OBJEXT):  \
+       drivers/ifxmodem/$(am__dirstamp) \
+       drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/stk.$(OBJEXT): drivers/ifxmodem/$(am__dirstamp) \
+       drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/ctm.$(OBJEXT): drivers/ifxmodem/$(am__dirstamp) \
+       drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/stemodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/stemodem
+       @: > drivers/stemodem/$(am__dirstamp)
+drivers/stemodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/stemodem/$(DEPDIR)
+       @: > drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+drivers/stemodem/stemodem.$(OBJEXT): drivers/stemodem/$(am__dirstamp) \
+       drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+drivers/stemodem/voicecall.$(OBJEXT):  \
+       drivers/stemodem/$(am__dirstamp) \
+       drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+drivers/stemodem/radio-settings.$(OBJEXT):  \
+       drivers/stemodem/$(am__dirstamp) \
+       drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+drivers/stemodem/caif_rtnl.$(OBJEXT):  \
+       drivers/stemodem/$(am__dirstamp) \
+       drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+drivers/stemodem/gprs-context.$(OBJEXT):  \
+       drivers/stemodem/$(am__dirstamp) \
+       drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+drivers/dunmodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/dunmodem
+       @: > drivers/dunmodem/$(am__dirstamp)
+drivers/dunmodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/dunmodem/$(DEPDIR)
+       @: > drivers/dunmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/dunmodem/dunmodem.$(OBJEXT): drivers/dunmodem/$(am__dirstamp) \
+       drivers/dunmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/dunmodem/network-registration.$(OBJEXT):  \
+       drivers/dunmodem/$(am__dirstamp) \
+       drivers/dunmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/dunmodem/gprs.$(OBJEXT): drivers/dunmodem/$(am__dirstamp) \
+       drivers/dunmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/hfpmodem
+       @: > drivers/hfpmodem/$(am__dirstamp)
+drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/hfpmodem/$(DEPDIR)
+       @: > drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/hfpmodem.$(OBJEXT): drivers/hfpmodem/$(am__dirstamp) \
+       drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/slc.$(OBJEXT): drivers/hfpmodem/$(am__dirstamp) \
+       drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/voicecall.$(OBJEXT):  \
+       drivers/hfpmodem/$(am__dirstamp) \
+       drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/network-registration.$(OBJEXT):  \
+       drivers/hfpmodem/$(am__dirstamp) \
+       drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/call-volume.$(OBJEXT):  \
+       drivers/hfpmodem/$(am__dirstamp) \
+       drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/devinfo.$(OBJEXT): drivers/hfpmodem/$(am__dirstamp) \
+       drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/hfpmodem/handsfree.$(OBJEXT):  \
+       drivers/hfpmodem/$(am__dirstamp) \
+       drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+plugins/phonesim.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+drivers/cdmamodem/$(am__dirstamp):
+       @$(MKDIR_P) drivers/cdmamodem
+       @: > drivers/cdmamodem/$(am__dirstamp)
+drivers/cdmamodem/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) drivers/cdmamodem/$(DEPDIR)
+       @: > drivers/cdmamodem/$(DEPDIR)/$(am__dirstamp)
+drivers/cdmamodem/cdmamodem.$(OBJEXT):  \
+       drivers/cdmamodem/$(am__dirstamp) \
+       drivers/cdmamodem/$(DEPDIR)/$(am__dirstamp)
+drivers/cdmamodem/voicecall.$(OBJEXT):  \
+       drivers/cdmamodem/$(am__dirstamp) \
+       drivers/cdmamodem/$(DEPDIR)/$(am__dirstamp)
+drivers/cdmamodem/devinfo.$(OBJEXT):  \
+       drivers/cdmamodem/$(am__dirstamp) \
+       drivers/cdmamodem/$(DEPDIR)/$(am__dirstamp)
+drivers/cdmamodem/connman.$(OBJEXT):  \
+       drivers/cdmamodem/$(am__dirstamp) \
+       drivers/cdmamodem/$(DEPDIR)/$(am__dirstamp)
+plugins/g1.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/gobi.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/wavecom.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/calypso.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/mbm.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/hso.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/zte.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/huawei.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/sierra.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/novatel.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/palmpre.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/ifx.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/ste.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/stemgr.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/caif.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/tc65.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/nokia.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/nokiacdma.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/linktop.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/alcatel.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/speedup.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/speedupcdma.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/samsung.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/sim900.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetooth.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/telit.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/sap.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/hfp_hf.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/hfp_ag.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/dun_gw.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/connman.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+btio/$(am__dirstamp):
+       @$(MKDIR_P) btio
+       @: > btio/$(am__dirstamp)
+btio/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) btio/$(DEPDIR)
+       @: > btio/$(DEPDIR)/$(am__dirstamp)
+btio/btio.$(OBJEXT): btio/$(am__dirstamp) \
+       btio/$(DEPDIR)/$(am__dirstamp)
+plugins/mbpi.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/provision.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/cdma-provision.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+examples/$(am__dirstamp):
+       @$(MKDIR_P) examples
+       @: > examples/$(am__dirstamp)
+examples/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) examples/$(DEPDIR)
+       @: > examples/$(DEPDIR)/$(am__dirstamp)
+examples/history.$(OBJEXT): examples/$(am__dirstamp) \
+       examples/$(DEPDIR)/$(am__dirstamp)
+examples/nettime.$(OBJEXT): examples/$(am__dirstamp) \
+       examples/$(DEPDIR)/$(am__dirstamp)
+examples/provision.$(OBJEXT): examples/$(am__dirstamp) \
+       examples/$(DEPDIR)/$(am__dirstamp)
+examples/emulator.$(OBJEXT): examples/$(am__dirstamp) \
+       examples/$(DEPDIR)/$(am__dirstamp)
+examples/private-network.$(OBJEXT): examples/$(am__dirstamp) \
+       examples/$(DEPDIR)/$(am__dirstamp)
+plugins/smart-messaging.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/push-notification.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+src/$(am__dirstamp):
+       @$(MKDIR_P) src
+       @: > src/$(am__dirstamp)
+src/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) src/$(DEPDIR)
+       @: > src/$(DEPDIR)/$(am__dirstamp)
+src/main.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/log.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/plugin.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/modem.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/common.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/manager.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/dbus.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/util.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/network.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/voicecall.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/ussd.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/sms.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/call-settings.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/call-forwarding.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/call-meter.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/smsutil.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/call-barring.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/sim.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/stk.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/phonebook.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/history.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/message-waiting.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/simutil.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/storage.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/cbs.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/watch.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/call-volume.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/gprs.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/idmap.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/radio-settings.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/stkutil.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/nettime.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/stkagent.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/simfs.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/audio-settings.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/smsagent.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/ctm.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/cdma-voicecall.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/sim-auth.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/message.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/gprs-provision.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/emulator.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/location-reporting.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/cdma-connman.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/gnss.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/gnssagent.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/cdma-smsutil.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/cdma-sms.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/private-network.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/cdma-netreg.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/cdma-provision.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/handsfree.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/ofonod$(EXEEXT): $(src_ofonod_OBJECTS) $(src_ofonod_DEPENDENCIES) src/$(am__dirstamp)
+       @rm -f src/ofonod$(EXEEXT)
+       $(AM_V_CCLD)$(src_ofonod_LINK) $(src_ofonod_OBJECTS) $(src_ofonod_LDADD) $(LIBS)
+tools/$(am__dirstamp):
+       @$(MKDIR_P) tools
+       @: > tools/$(am__dirstamp)
+tools/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) tools/$(DEPDIR)
+       @: > tools/$(DEPDIR)/$(am__dirstamp)
+tools/auto-enable.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/auto-enable$(EXEEXT): $(tools_auto_enable_OBJECTS) $(tools_auto_enable_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/auto-enable$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_auto_enable_OBJECTS) $(tools_auto_enable_LDADD) $(LIBS)
+tools/get-location.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/get-location$(EXEEXT): $(tools_get_location_OBJECTS) $(tools_get_location_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/get-location$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_get_location_OBJECTS) $(tools_get_location_LDADD) $(LIBS)
+tools/huawei-audio.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/huawei-audio$(EXEEXT): $(tools_huawei_audio_OBJECTS) $(tools_huawei_audio_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/huawei-audio$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_huawei_audio_OBJECTS) $(tools_huawei_audio_LDADD) $(LIBS)
+tools/lookup-apn.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/lookup-apn$(EXEEXT): $(tools_lookup_apn_OBJECTS) $(tools_lookup_apn_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/lookup-apn$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_lookup_apn_OBJECTS) $(tools_lookup_apn_LDADD) $(LIBS)
+tools/lookup-provider-name.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/lookup-provider-name$(EXEEXT): $(tools_lookup_provider_name_OBJECTS) $(tools_lookup_provider_name_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/lookup-provider-name$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_lookup_provider_name_OBJECTS) $(tools_lookup_provider_name_LDADD) $(LIBS)
+unit/$(am__dirstamp):
+       @$(MKDIR_P) unit
+       @: > unit/$(am__dirstamp)
+unit/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) unit/$(DEPDIR)
+       @: > unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-caif.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-caif$(EXEEXT): $(unit_test_caif_OBJECTS) $(unit_test_caif_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-caif$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_caif_OBJECTS) $(unit_test_caif_LDADD) $(LIBS)
+unit/test-cdmasms.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-cdmasms$(EXEEXT): $(unit_test_cdmasms_OBJECTS) $(unit_test_cdmasms_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-cdmasms$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_cdmasms_OBJECTS) $(unit_test_cdmasms_LDADD) $(LIBS)
+unit/test-common.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-common$(EXEEXT): $(unit_test_common_OBJECTS) $(unit_test_common_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-common$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_common_OBJECTS) $(unit_test_common_LDADD) $(LIBS)
+unit/test-idmap.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-idmap$(EXEEXT): $(unit_test_idmap_OBJECTS) $(unit_test_idmap_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-idmap$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_idmap_OBJECTS) $(unit_test_idmap_LDADD) $(LIBS)
+unit/test-mux.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-mux$(EXEEXT): $(unit_test_mux_OBJECTS) $(unit_test_mux_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-mux$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_mux_OBJECTS) $(unit_test_mux_LDADD) $(LIBS)
+unit/test-simutil.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-simutil$(EXEEXT): $(unit_test_simutil_OBJECTS) $(unit_test_simutil_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-simutil$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_simutil_OBJECTS) $(unit_test_simutil_LDADD) $(LIBS)
+unit/test-sms.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-sms$(EXEEXT): $(unit_test_sms_OBJECTS) $(unit_test_sms_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-sms$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_sms_OBJECTS) $(unit_test_sms_LDADD) $(LIBS)
+unit/test-sms-root$(EXEEXT): $(unit_test_sms_root_OBJECTS) $(unit_test_sms_root_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-sms-root$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_sms_root_OBJECTS) $(unit_test_sms_root_LDADD) $(LIBS)
+unit/test-stkutil.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-stkutil$(EXEEXT): $(unit_test_stkutil_OBJECTS) $(unit_test_stkutil_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-stkutil$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_stkutil_OBJECTS) $(unit_test_stkutil_LDADD) $(LIBS)
+unit/test-util.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-util$(EXEEXT): $(unit_test_util_OBJECTS) $(unit_test_util_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-util$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_util_OBJECTS) $(unit_test_util_LDADD) $(LIBS)
+install-testSCRIPTS: $(test_SCRIPTS)
+       @$(NORMAL_INSTALL)
+       test -z "$(testdir)" || $(MKDIR_P) "$(DESTDIR)$(testdir)"
+       @list='$(test_SCRIPTS)'; test -n "$(testdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n' \
+           -e 'h;s|.*|.|' \
+           -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+             if (++n[d] == $(am__install_max)) { \
+               print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+           else { print "f", d "/" $$4, $$1 } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+            if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+            test -z "$$files" || { \
+              echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(testdir)$$dir'"; \
+              $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(testdir)$$dir" || exit $$?; \
+            } \
+       ; done
+
+uninstall-testSCRIPTS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(test_SCRIPTS)'; test -n "$(testdir)" || exit 0; \
+       files=`for p in $$list; do echo "$$p"; done | \
+              sed -e 's,.*/,,;$(transform)'`; \
+       test -n "$$list" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(testdir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(testdir)" && rm -f $$files
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+       -rm -f btio/btio.$(OBJEXT)
+       -rm -f drivers/atmodem/atmodem.$(OBJEXT)
+       -rm -f drivers/atmodem/atutil.$(OBJEXT)
+       -rm -f drivers/atmodem/call-barring.$(OBJEXT)
+       -rm -f drivers/atmodem/call-forwarding.$(OBJEXT)
+       -rm -f drivers/atmodem/call-meter.$(OBJEXT)
+       -rm -f drivers/atmodem/call-settings.$(OBJEXT)
+       -rm -f drivers/atmodem/call-volume.$(OBJEXT)
+       -rm -f drivers/atmodem/cbs.$(OBJEXT)
+       -rm -f drivers/atmodem/devinfo.$(OBJEXT)
+       -rm -f drivers/atmodem/gnss.$(OBJEXT)
+       -rm -f drivers/atmodem/gprs-context.$(OBJEXT)
+       -rm -f drivers/atmodem/gprs.$(OBJEXT)
+       -rm -f drivers/atmodem/network-registration.$(OBJEXT)
+       -rm -f drivers/atmodem/phonebook.$(OBJEXT)
+       -rm -f drivers/atmodem/sim-auth.$(OBJEXT)
+       -rm -f drivers/atmodem/sim.$(OBJEXT)
+       -rm -f drivers/atmodem/sms.$(OBJEXT)
+       -rm -f drivers/atmodem/stk.$(OBJEXT)
+       -rm -f drivers/atmodem/ussd.$(OBJEXT)
+       -rm -f drivers/atmodem/voicecall.$(OBJEXT)
+       -rm -f drivers/calypsomodem/calypsomodem.$(OBJEXT)
+       -rm -f drivers/calypsomodem/stk.$(OBJEXT)
+       -rm -f drivers/calypsomodem/voicecall.$(OBJEXT)
+       -rm -f drivers/cdmamodem/cdmamodem.$(OBJEXT)
+       -rm -f drivers/cdmamodem/connman.$(OBJEXT)
+       -rm -f drivers/cdmamodem/devinfo.$(OBJEXT)
+       -rm -f drivers/cdmamodem/voicecall.$(OBJEXT)
+       -rm -f drivers/dunmodem/dunmodem.$(OBJEXT)
+       -rm -f drivers/dunmodem/gprs.$(OBJEXT)
+       -rm -f drivers/dunmodem/network-registration.$(OBJEXT)
+       -rm -f drivers/hfpmodem/call-volume.$(OBJEXT)
+       -rm -f drivers/hfpmodem/devinfo.$(OBJEXT)
+       -rm -f drivers/hfpmodem/handsfree.$(OBJEXT)
+       -rm -f drivers/hfpmodem/hfpmodem.$(OBJEXT)
+       -rm -f drivers/hfpmodem/network-registration.$(OBJEXT)
+       -rm -f drivers/hfpmodem/slc.$(OBJEXT)
+       -rm -f drivers/hfpmodem/voicecall.$(OBJEXT)
+       -rm -f drivers/hsomodem/gprs-context.$(OBJEXT)
+       -rm -f drivers/hsomodem/hsomodem.$(OBJEXT)
+       -rm -f drivers/hsomodem/radio-settings.$(OBJEXT)
+       -rm -f drivers/huaweimodem/audio-settings.$(OBJEXT)
+       -rm -f drivers/huaweimodem/cdma-netreg.$(OBJEXT)
+       -rm -f drivers/huaweimodem/gprs-context.$(OBJEXT)
+       -rm -f drivers/huaweimodem/huaweimodem.$(OBJEXT)
+       -rm -f drivers/huaweimodem/radio-settings.$(OBJEXT)
+       -rm -f drivers/huaweimodem/ussd.$(OBJEXT)
+       -rm -f drivers/huaweimodem/voicecall.$(OBJEXT)
+       -rm -f drivers/ifxmodem/audio-settings.$(OBJEXT)
+       -rm -f drivers/ifxmodem/ctm.$(OBJEXT)
+       -rm -f drivers/ifxmodem/gprs-context.$(OBJEXT)
+       -rm -f drivers/ifxmodem/ifxmodem.$(OBJEXT)
+       -rm -f drivers/ifxmodem/radio-settings.$(OBJEXT)
+       -rm -f drivers/ifxmodem/stk.$(OBJEXT)
+       -rm -f drivers/ifxmodem/voicecall.$(OBJEXT)
+       -rm -f drivers/isimodem/audio-settings.$(OBJEXT)
+       -rm -f drivers/isimodem/call-barring.$(OBJEXT)
+       -rm -f drivers/isimodem/call-forwarding.$(OBJEXT)
+       -rm -f drivers/isimodem/call-meter.$(OBJEXT)
+       -rm -f drivers/isimodem/call-settings.$(OBJEXT)
+       -rm -f drivers/isimodem/cbs.$(OBJEXT)
+       -rm -f drivers/isimodem/debug.$(OBJEXT)
+       -rm -f drivers/isimodem/devinfo.$(OBJEXT)
+       -rm -f drivers/isimodem/gprs-context.$(OBJEXT)
+       -rm -f drivers/isimodem/gprs.$(OBJEXT)
+       -rm -f drivers/isimodem/infoserver.$(OBJEXT)
+       -rm -f drivers/isimodem/isimodem.$(OBJEXT)
+       -rm -f drivers/isimodem/network-registration.$(OBJEXT)
+       -rm -f drivers/isimodem/phonebook.$(OBJEXT)
+       -rm -f drivers/isimodem/radio-settings.$(OBJEXT)
+       -rm -f drivers/isimodem/sim.$(OBJEXT)
+       -rm -f drivers/isimodem/sms.$(OBJEXT)
+       -rm -f drivers/isimodem/uicc-util.$(OBJEXT)
+       -rm -f drivers/isimodem/uicc.$(OBJEXT)
+       -rm -f drivers/isimodem/ussd.$(OBJEXT)
+       -rm -f drivers/isimodem/voicecall.$(OBJEXT)
+       -rm -f drivers/mbmmodem/gprs-context.$(OBJEXT)
+       -rm -f drivers/mbmmodem/location-reporting.$(OBJEXT)
+       -rm -f drivers/mbmmodem/mbmmodem.$(OBJEXT)
+       -rm -f drivers/mbmmodem/stk.$(OBJEXT)
+       -rm -f drivers/nwmodem/nwmodem.$(OBJEXT)
+       -rm -f drivers/nwmodem/radio-settings.$(OBJEXT)
+       -rm -f drivers/stemodem/caif_rtnl.$(OBJEXT)
+       -rm -f drivers/stemodem/gprs-context.$(OBJEXT)
+       -rm -f drivers/stemodem/radio-settings.$(OBJEXT)
+       -rm -f drivers/stemodem/stemodem.$(OBJEXT)
+       -rm -f drivers/stemodem/voicecall.$(OBJEXT)
+       -rm -f examples/emulator.$(OBJEXT)
+       -rm -f examples/history.$(OBJEXT)
+       -rm -f examples/nettime.$(OBJEXT)
+       -rm -f examples/private-network.$(OBJEXT)
+       -rm -f examples/provision.$(OBJEXT)
+       -rm -f gatchat/crc-ccitt.$(OBJEXT)
+       -rm -f gatchat/gatchat.$(OBJEXT)
+       -rm -f gatchat/gathdlc.$(OBJEXT)
+       -rm -f gatchat/gatio.$(OBJEXT)
+       -rm -f gatchat/gatmux.$(OBJEXT)
+       -rm -f gatchat/gatppp.$(OBJEXT)
+       -rm -f gatchat/gatrawip.$(OBJEXT)
+       -rm -f gatchat/gatresult.$(OBJEXT)
+       -rm -f gatchat/gatserver.$(OBJEXT)
+       -rm -f gatchat/gatsyntax.$(OBJEXT)
+       -rm -f gatchat/gattty.$(OBJEXT)
+       -rm -f gatchat/gatutil.$(OBJEXT)
+       -rm -f gatchat/gsm0710.$(OBJEXT)
+       -rm -f gatchat/gsmdial.$(OBJEXT)
+       -rm -f gatchat/ppp_auth.$(OBJEXT)
+       -rm -f gatchat/ppp_cp.$(OBJEXT)
+       -rm -f gatchat/ppp_ipcp.$(OBJEXT)
+       -rm -f gatchat/ppp_ipv6cp.$(OBJEXT)
+       -rm -f gatchat/ppp_lcp.$(OBJEXT)
+       -rm -f gatchat/ppp_net.$(OBJEXT)
+       -rm -f gatchat/ringbuffer.$(OBJEXT)
+       -rm -f gatchat/test-qcdm.$(OBJEXT)
+       -rm -f gatchat/test-server.$(OBJEXT)
+       -rm -f gdbus/mainloop.$(OBJEXT)
+       -rm -f gdbus/object.$(OBJEXT)
+       -rm -f gdbus/polkit.$(OBJEXT)
+       -rm -f gdbus/watch.$(OBJEXT)
+       -rm -f gisi/client.$(OBJEXT)
+       -rm -f gisi/iter.$(OBJEXT)
+       -rm -f gisi/message.$(OBJEXT)
+       -rm -f gisi/modem.$(OBJEXT)
+       -rm -f gisi/netlink.$(OBJEXT)
+       -rm -f gisi/pep.$(OBJEXT)
+       -rm -f gisi/pipe.$(OBJEXT)
+       -rm -f gisi/server.$(OBJEXT)
+       -rm -f gisi/socket.$(OBJEXT)
+       -rm -f plugins/alcatel.$(OBJEXT)
+       -rm -f plugins/bluetooth.$(OBJEXT)
+       -rm -f plugins/caif.$(OBJEXT)
+       -rm -f plugins/calypso.$(OBJEXT)
+       -rm -f plugins/cdma-provision.$(OBJEXT)
+       -rm -f plugins/connman.$(OBJEXT)
+       -rm -f plugins/dun_gw.$(OBJEXT)
+       -rm -f plugins/g1.$(OBJEXT)
+       -rm -f plugins/gobi.$(OBJEXT)
+       -rm -f plugins/hfp_ag.$(OBJEXT)
+       -rm -f plugins/hfp_hf.$(OBJEXT)
+       -rm -f plugins/hso.$(OBJEXT)
+       -rm -f plugins/huawei.$(OBJEXT)
+       -rm -f plugins/ifx.$(OBJEXT)
+       -rm -f plugins/isiusb.$(OBJEXT)
+       -rm -f plugins/linktop.$(OBJEXT)
+       -rm -f plugins/mbm.$(OBJEXT)
+       -rm -f plugins/mbpi.$(OBJEXT)
+       -rm -f plugins/n900.$(OBJEXT)
+       -rm -f plugins/nokia-gpio.$(OBJEXT)
+       -rm -f plugins/nokia.$(OBJEXT)
+       -rm -f plugins/nokiacdma.$(OBJEXT)
+       -rm -f plugins/novatel.$(OBJEXT)
+       -rm -f plugins/palmpre.$(OBJEXT)
+       -rm -f plugins/phonesim.$(OBJEXT)
+       -rm -f plugins/provision.$(OBJEXT)
+       -rm -f plugins/push-notification.$(OBJEXT)
+       -rm -f plugins/samsung.$(OBJEXT)
+       -rm -f plugins/sap.$(OBJEXT)
+       -rm -f plugins/sierra.$(OBJEXT)
+       -rm -f plugins/sim900.$(OBJEXT)
+       -rm -f plugins/smart-messaging.$(OBJEXT)
+       -rm -f plugins/speedup.$(OBJEXT)
+       -rm -f plugins/speedupcdma.$(OBJEXT)
+       -rm -f plugins/ste.$(OBJEXT)
+       -rm -f plugins/stemgr.$(OBJEXT)
+       -rm -f plugins/tc65.$(OBJEXT)
+       -rm -f plugins/telit.$(OBJEXT)
+       -rm -f plugins/u8500.$(OBJEXT)
+       -rm -f plugins/udev.$(OBJEXT)
+       -rm -f plugins/udevng.$(OBJEXT)
+       -rm -f plugins/wavecom.$(OBJEXT)
+       -rm -f plugins/zte.$(OBJEXT)
+       -rm -f src/audio-settings.$(OBJEXT)
+       -rm -f src/call-barring.$(OBJEXT)
+       -rm -f src/call-forwarding.$(OBJEXT)
+       -rm -f src/call-meter.$(OBJEXT)
+       -rm -f src/call-settings.$(OBJEXT)
+       -rm -f src/call-volume.$(OBJEXT)
+       -rm -f src/cbs.$(OBJEXT)
+       -rm -f src/cdma-connman.$(OBJEXT)
+       -rm -f src/cdma-netreg.$(OBJEXT)
+       -rm -f src/cdma-provision.$(OBJEXT)
+       -rm -f src/cdma-sms.$(OBJEXT)
+       -rm -f src/cdma-smsutil.$(OBJEXT)
+       -rm -f src/cdma-voicecall.$(OBJEXT)
+       -rm -f src/common.$(OBJEXT)
+       -rm -f src/ctm.$(OBJEXT)
+       -rm -f src/dbus.$(OBJEXT)
+       -rm -f src/emulator.$(OBJEXT)
+       -rm -f src/gnss.$(OBJEXT)
+       -rm -f src/gnssagent.$(OBJEXT)
+       -rm -f src/gprs-provision.$(OBJEXT)
+       -rm -f src/gprs.$(OBJEXT)
+       -rm -f src/handsfree.$(OBJEXT)
+       -rm -f src/history.$(OBJEXT)
+       -rm -f src/idmap.$(OBJEXT)
+       -rm -f src/location-reporting.$(OBJEXT)
+       -rm -f src/log.$(OBJEXT)
+       -rm -f src/main.$(OBJEXT)
+       -rm -f src/manager.$(OBJEXT)
+       -rm -f src/message-waiting.$(OBJEXT)
+       -rm -f src/message.$(OBJEXT)
+       -rm -f src/modem.$(OBJEXT)
+       -rm -f src/nettime.$(OBJEXT)
+       -rm -f src/network.$(OBJEXT)
+       -rm -f src/phonebook.$(OBJEXT)
+       -rm -f src/plugin.$(OBJEXT)
+       -rm -f src/private-network.$(OBJEXT)
+       -rm -f src/radio-settings.$(OBJEXT)
+       -rm -f src/sim-auth.$(OBJEXT)
+       -rm -f src/sim.$(OBJEXT)
+       -rm -f src/simfs.$(OBJEXT)
+       -rm -f src/simutil.$(OBJEXT)
+       -rm -f src/sms.$(OBJEXT)
+       -rm -f src/smsagent.$(OBJEXT)
+       -rm -f src/smsutil.$(OBJEXT)
+       -rm -f src/stk.$(OBJEXT)
+       -rm -f src/stkagent.$(OBJEXT)
+       -rm -f src/stkutil.$(OBJEXT)
+       -rm -f src/storage.$(OBJEXT)
+       -rm -f src/ussd.$(OBJEXT)
+       -rm -f src/util.$(OBJEXT)
+       -rm -f src/voicecall.$(OBJEXT)
+       -rm -f src/watch.$(OBJEXT)
+       -rm -f tools/auto-enable.$(OBJEXT)
+       -rm -f tools/get-location.$(OBJEXT)
+       -rm -f tools/huawei-audio.$(OBJEXT)
+       -rm -f tools/lookup-apn.$(OBJEXT)
+       -rm -f tools/lookup-provider-name.$(OBJEXT)
+       -rm -f unit/test-caif.$(OBJEXT)
+       -rm -f unit/test-cdmasms.$(OBJEXT)
+       -rm -f unit/test-common.$(OBJEXT)
+       -rm -f unit/test-idmap.$(OBJEXT)
+       -rm -f unit/test-mux.$(OBJEXT)
+       -rm -f unit/test-simutil.$(OBJEXT)
+       -rm -f unit/test-sms.$(OBJEXT)
+       -rm -f unit/test-stkutil.$(OBJEXT)
+       -rm -f unit/test-util.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/btio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/atmodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/atutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/call-barring.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/call-forwarding.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/call-meter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/call-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/call-volume.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/cbs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/devinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/gnss.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/gprs-context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/gprs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/network-registration.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/phonebook.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/sim-auth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/sim.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/sms.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/stk.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/ussd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/atmodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/calypsomodem/$(DEPDIR)/calypsomodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/calypsomodem/$(DEPDIR)/stk.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/calypsomodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/cdmamodem/$(DEPDIR)/cdmamodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/cdmamodem/$(DEPDIR)/connman.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/cdmamodem/$(DEPDIR)/devinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/cdmamodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/dunmodem/$(DEPDIR)/dunmodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/dunmodem/$(DEPDIR)/gprs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/dunmodem/$(DEPDIR)/network-registration.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/call-volume.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/devinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/handsfree.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/hfpmodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/network-registration.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/slc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hsomodem/$(DEPDIR)/gprs-context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hsomodem/$(DEPDIR)/hsomodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/hsomodem/$(DEPDIR)/radio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/huaweimodem/$(DEPDIR)/audio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/huaweimodem/$(DEPDIR)/cdma-netreg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/huaweimodem/$(DEPDIR)/gprs-context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/huaweimodem/$(DEPDIR)/huaweimodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/huaweimodem/$(DEPDIR)/radio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/huaweimodem/$(DEPDIR)/ussd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/huaweimodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/ifxmodem/$(DEPDIR)/audio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/ifxmodem/$(DEPDIR)/ctm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/ifxmodem/$(DEPDIR)/gprs-context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/ifxmodem/$(DEPDIR)/ifxmodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/ifxmodem/$(DEPDIR)/radio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/ifxmodem/$(DEPDIR)/stk.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/ifxmodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/audio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/call-barring.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/call-forwarding.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/call-meter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/call-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/cbs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/debug.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/devinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/gprs-context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/gprs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/infoserver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/isimodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/network-registration.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/phonebook.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/radio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/sim.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/sms.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/uicc-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/uicc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/ussd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/isimodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/mbmmodem/$(DEPDIR)/gprs-context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/mbmmodem/$(DEPDIR)/location-reporting.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/mbmmodem/$(DEPDIR)/mbmmodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/mbmmodem/$(DEPDIR)/stk.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/nwmodem/$(DEPDIR)/nwmodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/nwmodem/$(DEPDIR)/radio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/stemodem/$(DEPDIR)/caif_rtnl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/stemodem/$(DEPDIR)/gprs-context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/stemodem/$(DEPDIR)/radio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/stemodem/$(DEPDIR)/stemodem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/stemodem/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@examples/$(DEPDIR)/emulator.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@examples/$(DEPDIR)/history.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@examples/$(DEPDIR)/nettime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@examples/$(DEPDIR)/private-network.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@examples/$(DEPDIR)/provision.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/crc-ccitt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatchat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gathdlc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatmux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatppp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatrawip.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatresult.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatserver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatsyntax.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gattty.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gatutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gsm0710.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/gsmdial.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/ppp_auth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/ppp_cp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/ppp_ipcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/ppp_ipv6cp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/ppp_lcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/ppp_net.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/ringbuffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/test-qcdm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gatchat/$(DEPDIR)/test-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/object.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/polkit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/iter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/message.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/modem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/netlink.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/pep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/pipe.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gisi/$(DEPDIR)/socket.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/alcatel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetooth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/caif.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/calypso.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/cdma-provision.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/connman.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/dun_gw.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/g1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/gobi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/hfp_ag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/hfp_hf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/hso.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/huawei.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/ifx.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/isiusb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/linktop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/mbm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/mbpi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/n900.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/nokia-gpio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/nokia.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/nokiacdma.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/novatel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/palmpre.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/phonesim.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/provision.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/push-notification.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/samsung.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/sap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/sierra.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/sim900.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/smart-messaging.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/speedup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/speedupcdma.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/ste.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/stemgr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/tc65.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/telit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/u8500.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/udev.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/udevng.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/wavecom.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/zte.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/audio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/call-barring.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/call-forwarding.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/call-meter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/call-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/call-volume.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cbs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cdma-connman.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cdma-netreg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cdma-provision.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cdma-sms.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cdma-smsutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cdma-voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/ctm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/dbus.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/emulator.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/gnss.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/gnssagent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/gprs-provision.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/gprs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/handsfree.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/history.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/idmap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/location-reporting.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/message-waiting.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/message.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/modem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/nettime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/network.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/phonebook.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/plugin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/private-network.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/radio-settings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sim-auth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sim.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/simfs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/simutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sms.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/smsagent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/smsutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/stk.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/stkagent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/stkutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/storage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/ussd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/voicecall.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/auto-enable.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/get-location.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/huawei-audio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lookup-apn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lookup-provider-name.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-caif.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-cdmasms.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-idmap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-mux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-simutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-sms.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-stkutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-util.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Po
+@am__fastdepCC_FALSE@  $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Po
+@am__fastdepCC_FALSE@  $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@   $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Plo
+@am__fastdepCC_FALSE@  $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+       -rm -rf gatchat/.libs gatchat/_libs
+       -rm -rf src/.libs src/_libs
+       -rm -rf tools/.libs tools/_libs
+       -rm -rf unit/.libs unit/_libs
+
+distclean-libtool:
+       -rm -f libtool config.lt
+install-man8: $(dist_man_MANS)
+       @$(NORMAL_INSTALL)
+       test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+       @list=''; test -n "$(man8dir)" || exit 0; \
+       { for i in $$list; do echo "$$i"; done; \
+       l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+         sed -n '/\.8[a-z]*$$/p'; \
+       } | while read p; do \
+         if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; echo "$$p"; \
+       done | \
+       sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+       sed 'N;N;s,\n, ,g' | { \
+       list=; while read file base inst; do \
+         if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+           echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+           $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+         fi; \
+       done; \
+       for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+       while read files; do \
+         test -z "$$files" || { \
+           echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+           $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+       done; }
+
+uninstall-man8:
+       @$(NORMAL_UNINSTALL)
+       @list=''; test -n "$(man8dir)" || exit 0; \
+       files=`{ for i in $$list; do echo "$$i"; done; \
+       l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+         sed -n '/\.8[a-z]*$$/p'; \
+       } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+       test -z "$$files" || { \
+         echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
+         cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+install-dist_confDATA: $(dist_conf_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(confdir)" || $(MKDIR_P) "$(DESTDIR)$(confdir)"
+       @list='$(dist_conf_DATA)'; test -n "$(confdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(confdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(confdir)" || exit $$?; \
+       done
+
+uninstall-dist_confDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dist_conf_DATA)'; test -n "$(confdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(confdir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(confdir)" && rm -f $$files
+install-dist_dbusconfDATA: $(dist_dbusconf_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(dbusconfdir)" || $(MKDIR_P) "$(DESTDIR)$(dbusconfdir)"
+       @list='$(dist_dbusconf_DATA)'; test -n "$(dbusconfdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusconfdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusconfdir)" || exit $$?; \
+       done
+
+uninstall-dist_dbusconfDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dist_dbusconf_DATA)'; test -n "$(dbusconfdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(dbusconfdir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(dbusconfdir)" && rm -f $$files
+install-pkgconfigDATA: $(pkgconfig_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(pkgconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)"
+       @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+       done
+
+uninstall-pkgconfigDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(pkgconfigdir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(pkgconfigdir)" && rm -f $$files
+install-stateDATA: $(state_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(statedir)" || $(MKDIR_P) "$(DESTDIR)$(statedir)"
+       @list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(statedir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(statedir)" || exit $$?; \
+       done
+
+uninstall-stateDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(statedir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(statedir)" && rm -f $$files
+install-systemdunitDATA: $(systemdunit_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(systemdunitdir)" || $(MKDIR_P) "$(DESTDIR)$(systemdunitdir)"
+       @list='$(systemdunit_DATA)'; test -n "$(systemdunitdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdunitdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdunitdir)" || exit $$?; \
+       done
+
+uninstall-systemdunitDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(systemdunit_DATA)'; test -n "$(systemdunitdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(systemdunitdir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(systemdunitdir)" && rm -f $$files
+install-nodist_pkgincludeHEADERS: $(nodist_pkginclude_HEADERS)
+       @$(NORMAL_INSTALL)
+       test -z "$(pkgincludedir)" || $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)"
+       @list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \
+         $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \
+       done
+
+uninstall-nodist_pkgincludeHEADERS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(pkgincludedir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(pkgincludedir)" && rm -f $$files
+install-pkgincludeHEADERS: $(pkginclude_HEADERS)
+       @$(NORMAL_INSTALL)
+       test -z "$(pkgincludedir)" || $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)"
+       @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \
+         $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \
+       done
+
+uninstall-pkgincludeHEADERS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       test -n "$$files" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(pkgincludedir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(pkgincludedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       set x; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       shift; \
+       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         if test $$# -gt 0; then \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             "$$@" $$unique; \
+         else \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             $$unique; \
+         fi; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       test -z "$(CTAGS_ARGS)$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && $(am__cd) $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+       @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+       srcdir=$(srcdir); export srcdir; \
+       list=' $(TESTS) '; \
+       $(am__tty_colors); \
+       if test -n "$$list"; then \
+         for tst in $$list; do \
+           if test -f ./$$tst; then dir=./; \
+           elif test -f $$tst; then dir=; \
+           else dir="$(srcdir)/"; fi; \
+           if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *[\ \     ]$$tst[\ \      ]*) \
+               xpass=`expr $$xpass + 1`; \
+               failed=`expr $$failed + 1`; \
+               col=$$red; res=XPASS; \
+             ;; \
+             *) \
+               col=$$grn; res=PASS; \
+             ;; \
+             esac; \
+           elif test $$? -ne 77; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *[\ \     ]$$tst[\ \      ]*) \
+               xfail=`expr $$xfail + 1`; \
+               col=$$lgn; res=XFAIL; \
+             ;; \
+             *) \
+               failed=`expr $$failed + 1`; \
+               col=$$red; res=FAIL; \
+             ;; \
+             esac; \
+           else \
+             skip=`expr $$skip + 1`; \
+             col=$$blu; res=SKIP; \
+           fi; \
+           echo "$${col}$$res$${std}: $$tst"; \
+         done; \
+         if test "$$all" -eq 1; then \
+           tests="test"; \
+           All=""; \
+         else \
+           tests="tests"; \
+           All="All "; \
+         fi; \
+         if test "$$failed" -eq 0; then \
+           if test "$$xfail" -eq 0; then \
+             banner="$$All$$all $$tests passed"; \
+           else \
+             if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+             banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+           fi; \
+         else \
+           if test "$$xpass" -eq 0; then \
+             banner="$$failed of $$all $$tests failed"; \
+           else \
+             if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+             banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+           fi; \
+         fi; \
+         dashes="$$banner"; \
+         skipped=""; \
+         if test "$$skip" -ne 0; then \
+           if test "$$skip" -eq 1; then \
+             skipped="($$skip test was not run)"; \
+           else \
+             skipped="($$skip tests were not run)"; \
+           fi; \
+           test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$skipped"; \
+         fi; \
+         report=""; \
+         if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+           report="Please report to $(PACKAGE_BUGREPORT)"; \
+           test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$report"; \
+         fi; \
+         dashes=`echo "$$dashes" | sed s/./=/g`; \
+         if test "$$failed" -eq 0; then \
+           echo "$$grn$$dashes"; \
+         else \
+           echo "$$red$$dashes"; \
+         fi; \
+         echo "$$banner"; \
+         test -z "$$skipped" || echo "$$skipped"; \
+         test -z "$$report" || echo "$$report"; \
+         echo "$$dashes$$std"; \
+         test "$$failed" -eq 0; \
+       else :; fi
+
+distdir: $(DISTFILES)
+       @list='$(MANS)'; if test -n "$$list"; then \
+         list=`for p in $$list; do \
+           if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+           if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+         if test -n "$$list" && \
+           grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+           echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+           grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/         /' >&2; \
+           echo "       to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+           echo "       typically \`make maintainer-clean' will remove them" >&2; \
+           exit 1; \
+         else :; fi; \
+       else :; fi
+       $(am__remove_distdir)
+       test -d "$(distdir)" || mkdir "$(distdir)"
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d "$(distdir)/$$file"; then \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+         else \
+           test -f "$(distdir)/$$file" \
+           || cp -p $$d/$$file "$(distdir)/$$file" \
+           || exit 1; \
+         fi; \
+       done
+       -test -n "$(am__skip_mode_fix)" \
+       || find "$(distdir)" -type d ! -perm -755 \
+               -exec chmod u+rwx,go+rx {} \; -o \
+         ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+         ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+         ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+       || chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+       $(am__remove_distdir)
+
+dist-bzip2: distdir
+       tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+       $(am__remove_distdir)
+
+dist-lzma: distdir
+       tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+       $(am__remove_distdir)
+
+dist-xz: distdir
+       tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz
+       $(am__remove_distdir)
+
+dist-tarZ: distdir
+       tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+       $(am__remove_distdir)
+
+dist-shar: distdir
+       shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+       $(am__remove_distdir)
+
+dist-zip: distdir
+       -rm -f $(distdir).zip
+       zip -rq $(distdir).zip $(distdir)
+       $(am__remove_distdir)
+
+dist dist-all: distdir
+       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+       $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+       case '$(DIST_ARCHIVES)' in \
+       *.tar.gz*) \
+         GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+       *.tar.bz2*) \
+         bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+       *.tar.lzma*) \
+         lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
+       *.tar.xz*) \
+         xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+       *.tar.Z*) \
+         uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+       *.shar.gz*) \
+         GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+       *.zip*) \
+         unzip $(distdir).zip ;;\
+       esac
+       chmod -R a-w $(distdir); chmod a+w $(distdir)
+       mkdir $(distdir)/_build
+       mkdir $(distdir)/_inst
+       chmod a-w $(distdir)
+       test -d $(distdir)/_build || exit 0; \
+       dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+         && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+         && am__cwd=`pwd` \
+         && $(am__cd) $(distdir)/_build \
+         && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+           $(DISTCHECK_CONFIGURE_FLAGS) \
+         && $(MAKE) $(AM_MAKEFLAGS) \
+         && $(MAKE) $(AM_MAKEFLAGS) dvi \
+         && $(MAKE) $(AM_MAKEFLAGS) check \
+         && $(MAKE) $(AM_MAKEFLAGS) install \
+         && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+         && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+         && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+               distuninstallcheck \
+         && chmod -R a-w "$$dc_install_base" \
+         && ({ \
+              (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+                   distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+             } || { rm -rf "$$dc_destdir"; exit 1; }) \
+         && rm -rf "$$dc_destdir" \
+         && $(MAKE) $(AM_MAKEFLAGS) dist \
+         && rm -rf $(DIST_ARCHIVES) \
+         && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+         && cd "$$am__cwd" \
+         || exit 1
+       $(am__remove_distdir)
+       @(echo "$(distdir) archives ready for distribution: "; \
+         list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+         sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+       @$(am__cd) '$(distuninstallcheck_dir)' \
+       && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+          || { echo "ERROR: files left after uninstall:" ; \
+               if test -n "$(DESTDIR)"; then \
+                 echo "  (check DESTDIR support)"; \
+               fi ; \
+               $(distuninstallcheck_listfiles) ; \
+               exit 1; } >&2
+distcleancheck: distclean
+       @if test '$(srcdir)' = . ; then \
+         echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+         exit 1 ; \
+       fi
+       @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+         || { echo "ERROR: files left in build directory after distclean:" ; \
+              $(distcleancheck_listfiles) ; \
+              exit 1; } >&2
+check-am: all-am
+       $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA) $(HEADERS) \
+               config.h
+installdirs:
+       for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(testdir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusconfdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(statedir)" "$(DESTDIR)$(systemdunitdir)" "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+       -rm -f btio/$(DEPDIR)/$(am__dirstamp)
+       -rm -f btio/$(am__dirstamp)
+       -rm -f drivers/atmodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/atmodem/$(am__dirstamp)
+       -rm -f drivers/calypsomodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/calypsomodem/$(am__dirstamp)
+       -rm -f drivers/cdmamodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/cdmamodem/$(am__dirstamp)
+       -rm -f drivers/dunmodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/dunmodem/$(am__dirstamp)
+       -rm -f drivers/hfpmodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/hfpmodem/$(am__dirstamp)
+       -rm -f drivers/hsomodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/hsomodem/$(am__dirstamp)
+       -rm -f drivers/huaweimodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/huaweimodem/$(am__dirstamp)
+       -rm -f drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/ifxmodem/$(am__dirstamp)
+       -rm -f drivers/isimodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/isimodem/$(am__dirstamp)
+       -rm -f drivers/mbmmodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/mbmmodem/$(am__dirstamp)
+       -rm -f drivers/nwmodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/nwmodem/$(am__dirstamp)
+       -rm -f drivers/stemodem/$(DEPDIR)/$(am__dirstamp)
+       -rm -f drivers/stemodem/$(am__dirstamp)
+       -rm -f examples/$(DEPDIR)/$(am__dirstamp)
+       -rm -f examples/$(am__dirstamp)
+       -rm -f gatchat/$(DEPDIR)/$(am__dirstamp)
+       -rm -f gatchat/$(am__dirstamp)
+       -rm -f gdbus/$(DEPDIR)/$(am__dirstamp)
+       -rm -f gdbus/$(am__dirstamp)
+       -rm -f gisi/$(DEPDIR)/$(am__dirstamp)
+       -rm -f gisi/$(am__dirstamp)
+       -rm -f plugins/$(DEPDIR)/$(am__dirstamp)
+       -rm -f plugins/$(am__dirstamp)
+       -rm -f src/$(DEPDIR)/$(am__dirstamp)
+       -rm -f src/$(am__dirstamp)
+       -rm -f tools/$(DEPDIR)/$(am__dirstamp)
+       -rm -f tools/$(am__dirstamp)
+       -rm -f unit/$(DEPDIR)/$(am__dirstamp)
+       -rm -f unit/$(am__dirstamp)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+       -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local clean-noinstPROGRAMS \
+       clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+       -rm -rf btio/$(DEPDIR) drivers/atmodem/$(DEPDIR) drivers/calypsomodem/$(DEPDIR) drivers/cdmamodem/$(DEPDIR) drivers/dunmodem/$(DEPDIR) drivers/hfpmodem/$(DEPDIR) drivers/hsomodem/$(DEPDIR) drivers/huaweimodem/$(DEPDIR) drivers/ifxmodem/$(DEPDIR) drivers/isimodem/$(DEPDIR) drivers/mbmmodem/$(DEPDIR) drivers/nwmodem/$(DEPDIR) drivers/stemodem/$(DEPDIR) examples/$(DEPDIR) gatchat/$(DEPDIR) gdbus/$(DEPDIR) gisi/$(DEPDIR) plugins/$(DEPDIR) src/$(DEPDIR) tools/$(DEPDIR) unit/$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-hdr distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_confDATA install-dist_dbusconfDATA \
+       install-man install-nodist_pkgincludeHEADERS \
+       install-pkgconfigDATA install-pkgincludeHEADERS \
+       install-stateDATA install-systemdunitDATA install-testSCRIPTS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+       -rm -rf $(top_srcdir)/autom4te.cache
+       -rm -rf btio/$(DEPDIR) drivers/atmodem/$(DEPDIR) drivers/calypsomodem/$(DEPDIR) drivers/cdmamodem/$(DEPDIR) drivers/dunmodem/$(DEPDIR) drivers/hfpmodem/$(DEPDIR) drivers/hsomodem/$(DEPDIR) drivers/huaweimodem/$(DEPDIR) drivers/ifxmodem/$(DEPDIR) drivers/isimodem/$(DEPDIR) drivers/mbmmodem/$(DEPDIR) drivers/nwmodem/$(DEPDIR) drivers/stemodem/$(DEPDIR) examples/$(DEPDIR) gatchat/$(DEPDIR) gdbus/$(DEPDIR) gisi/$(DEPDIR) plugins/$(DEPDIR) src/$(DEPDIR) tools/$(DEPDIR) unit/$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_confDATA uninstall-dist_dbusconfDATA \
+       uninstall-man uninstall-nodist_pkgincludeHEADERS \
+       uninstall-pkgconfigDATA uninstall-pkgincludeHEADERS \
+       uninstall-sbinPROGRAMS uninstall-stateDATA \
+       uninstall-systemdunitDATA uninstall-testSCRIPTS
+
+uninstall-man: uninstall-man8
+
+.MAKE: all check check-am install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am am--refresh check check-TESTS check-am \
+       clean clean-generic clean-libtool clean-local \
+       clean-noinstPROGRAMS clean-sbinPROGRAMS ctags dist dist-all \
+       dist-bzip2 dist-gzip dist-lzma dist-shar dist-tarZ dist-xz \
+       dist-zip distcheck distclean distclean-compile \
+       distclean-generic distclean-hdr distclean-libtool \
+       distclean-tags distcleancheck distdir distuninstallcheck dvi \
+       dvi-am html html-am info info-am install install-am \
+       install-data install-data-am install-dist_confDATA \
+       install-dist_dbusconfDATA install-dvi install-dvi-am \
+       install-exec install-exec-am install-html install-html-am \
+       install-info install-info-am install-man install-man8 \
+       install-nodist_pkgincludeHEADERS install-pdf install-pdf-am \
+       install-pkgconfigDATA install-pkgincludeHEADERS install-ps \
+       install-ps-am install-sbinPROGRAMS install-stateDATA \
+       install-strip install-systemdunitDATA install-testSCRIPTS \
+       installcheck installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-dist_confDATA \
+       uninstall-dist_dbusconfDATA uninstall-man uninstall-man8 \
+       uninstall-nodist_pkgincludeHEADERS uninstall-pkgconfigDATA \
+       uninstall-pkgincludeHEADERS uninstall-sbinPROGRAMS \
+       uninstall-stateDATA uninstall-systemdunitDATA \
+       uninstall-testSCRIPTS
+
+
+src/builtin.h: src/genbuiltin config.status
+       $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+plugins/%.rules:
+       $(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@
+
+include/ofono/version.h: include/version.h
+       $(AM_V_at)$(MKDIR_P) include/ofono
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+include/ofono/%.h: $(abs_top_srcdir)/include/%.h
+       $(AM_V_at)$(MKDIR_P) include/ofono
+       $(AM_V_GEN)$(LN_S) $< $@
+
+clean-local:
+       @$(RM) -rf include/ofono
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..413d789
--- /dev/null
+++ b/README
@@ -0,0 +1,29 @@
+oFono - Open Source Telephony
+*****************************
+
+Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+
+
+Compilation and installation
+============================
+
+In order to compile telephony stack you need following software packages:
+       - GCC compiler
+       - GLib library
+       - D-Bus library
+
+To configure run:
+       ./configure --prefix=/usr --mandir=/usr/share/man \
+                               --sysconfdir=/etc --localstatedir=/var
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+       make && make install
+
+
+Kernel Dependencies
+===================
+
+In order to have the PPP stack working in oFono you need to enable CONFIG_TUN
+(Universal TUN/TAP device driver support) in your kernel .config.
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..1fef651
--- /dev/null
+++ b/TODO
@@ -0,0 +1,529 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8.  The complexity scale is exponential,
+  with complexity 1 being the lowest complexity.  Complexity is a function
+  of both task 'complexity' and task 'scope'.
+
+  The general rule of thumb is that a complexity 1 task should take 1-2 weeks
+  for a person very familiar with oFono codebase.  Higher complexity tasks
+  require more time and have higher uncertainty.
+
+  Higher complexity tasks should be refined into several lower complexity tasks
+  once the task is better understood.
+
+
+SMS
+===
+
+- Add support for Enhanced SMS (EMS) from 3GPP 23.040.  These SMS messages
+  support a richer set of formatting elements, including bold/italic and
+  font size selection.  Embedding images and sounds into the SMS is also
+  supported.  This task should add support for receiving such SMS messages.
+
+  Proposed solution is to convert such messages to HTML and embed image /
+  sound data as embedded MIME in the generated HTML stream.  The EMS messages
+  will have to be detected (by the presence of particular tags in the SMS)
+  and signaled separately from simple SMS messages.
+
+  Priority: Low
+  Complexity: C8
+
+- Asynchronously acknowledge SMS DELIVER messages sent by the SMS driver
+  to core using ofono_sms_deliver_notify().  This may require the struct
+  ofono_sms_driver to be extended with one more function pointer like:
+    void (*deliver_ack)(unsigned char *pdu, int len, cb_t cb, void *data)
+  because currently messages are automatically acknowledged by either the
+  modem (this is the case of some AT modems) or the driver right after
+  ofono_sms_deliver_notify() and a failure to deliver at an upper level is
+  ignored.  The PDU can be an RP-ACK or RP-ERROR message with optional
+  TP-User-Content element, for example if returned from USIM Data Download.
+
+  Priority: Low
+  Complexity: C2
+
+
+CBS
+===
+
+- Support UMTS format for CBS messages.  This might be needed by some hardware
+  which does not convert UMTS-formatted cell broadcasts to GSM-formatted cell
+  broadcasts.  The UMTS CBS format can be found in 3GPP 25.324 Section 11.1.
+
+  Priority: Low
+  Complexity: C2
+
+
+SIM / SIM File system
+=====================
+
+- SIM Call History plugin.  New UICCs support four new SIM elementary files
+  for storing call history information on the SIM: EFici, EFict, EFoci, EFoct.
+  A plugin should be developed for oFono that will write to these files.
+
+  Priority: Low
+  Complexity: C2
+
+- Add support for SIM 'ready' notifications from the driver to the core.  Most
+  modem manufacturers initialize the SIM (e.g. cache SIM file system, STK
+  initialization, etc) internally before allowing the telephony stack to
+  access these portions.  When the PIN is locked, this can lead to oFono being
+  too fast for the modem and asking it for things before the firmware is ready.
+
+  The proposal is to introduce a new sim function:
+    void ofono_sim_ready_notify(struct ofono_sim *sim);
+
+  When oFono determines the SIM PIN is READY, it checks whether
+  ofono_sim_ready_notify has been called.  If it hasn't, then it stalls the
+  initialization procedure (and calling post_sim) until
+  ofono_sim_ready_notify is called.
+
+  Priority: High
+  Complexity: C2
+
+- Support SIM authentication: SIM and AKA suites.
+
+  Priority: Medium
+  Complexity: C4
+
+- Support SIM authentication: GBA_U suite.
+
+  Priority: Low
+  Complexity: C4
+
+- ISIM support
+
+  ISIM is the SIM application for IP Multimedia Subsystem, specified in
+  3GPP TS 31.103. The UICCs can support multiple ISIMs for different IMS
+  identities.
+
+  Priority: Low
+  Complexity: C4
+
+
+Modem Emulator
+==============
+
+- Support CE4A extensions to HFP AG emulator. CE4A defines additional
+  AT command extensions to the Bluetooth HFP AG standard. Refer to CE4A
+  white paper: "AT-commands for Automotive Premium Phone Integration".
+  Plugins can register additional command handlers appropriately to handle
+  such commands.
+
+  Complexity: C4
+  Priority: Low
+  Depends: HFP AG emulator
+
+- Support HSP AG.  Similar to HFP AG emulator but implements the much reduced
+  Bluetooth HSP AG profile.
+
+  Priority: Low
+  Complexity: C1
+
+- Support DUN networking over the USB transport.  This might require extra
+  AT commands to be implemented in order to comply with general USB DUN
+  expectations as there is no standard for the same.
+
+  Complexity: C4
+  Priority: Low
+
+- Support Bluetooth SPP profile.
+
+  Complexity: C4
+  Priority: Medium
+
+- Support new HFP 1.6 AG commands allowing to publish, select and connect audio
+  codecs (AT+BAC, AT+BCS, +BCS, AT+BCC). This will need to interact with audio
+  framework.
+
+  Complexity: C4
+  Priority: Low
+  Depends: HFP AG emulator
+
+- Integrate HFP AG emulator as a BlueZ service.
+  Replace direct access to Bluetooth library by usage of the BlueZ service
+  architecture.
+
+  Complexity: C2
+  Priority: Medium
+  Depends: HFP AG emulator
+
+- Add audio management to HFP AG emulator.
+  Integrate HFP AG emulator to BlueZ and Pulse Audio.
+  Add audio related AT commands support: remote audio volume control and in-band
+  ring tone management.
+
+  Complexity: C4
+  Priority: Medium
+  Depends: HFP AG emulator as BlueZ service
+
+PPP
+===
+
+- IPv6 CP support.  To support IPv6 based GPRS contexts via PPP, GAtPPP
+  needs to be updated to support IPv6CP from RFC 2472.
+
+  Priority: Low
+  Complexity: C4
+
+
+Location Services
+=================
+
+- Neighbor Cell Info.  Add dedicated atom, D-Bus API and atom driver(s) for
+  Neighbor Cell information.
+
+  This feature is not discussed in 27.007, thus manufacturer specific commands
+  are required.
+
+  Complexity: C4
+  Priority: Medium
+
+
+Supplementary Services
+======================
+
+- Closed User Group (CUG) support.
+
+  Priority: Low
+  Complexity: C8
+
+- Call Completion to Busy Subscriber (CCBS) support
+
+  This feature is not discussed in 27.007, thus manufacturer specific commands
+  are required.
+
+  Priority: Low
+  Complexity: C8
+
+- User to User Signaling (UUS) support
+
+  Priority: Low
+  Complexity: C8
+
+- Multiple Subscriber Profile (MSP) support
+
+  Priority: Low
+  Complexity: C2
+
+- CPHS Support.  This includes ALS and CPHS specific elementary files.
+
+  Priority: Low
+  Complexity: C8
+
+- Call forwarding state handling change
+
+  At the moment call forwarding states are not always correct. Any active
+  conditional call forwarding should become quiescent while unconditional call
+  forwarding is activate. If call forwarding unconditional is subsequently
+  deactivated, all the quiescent forwardings should become operative again.
+  I.e. No conditional call forwarding string should be returned while
+  unconditional call forwarding is active even if they exist.
+
+  If there is an successful attempt to activate/deactivate conditional call
+  forwarding while unconditional call forwarding is active the conditional cache
+  flag should cleared.
+
+  Priority: High
+  Complexity: C1
+  Owner: Nicolas Bertrand <nicolas.bertrand@linux.intel.com>
+
+
+Voicecall
+=========
+
+- Dial strings. Include CLIR prefixes and 2nd stage dial strings in the
+  DialString call property. Add dialstring accessor method to C API.
+
+  Priority: Medium
+  Complexity: C4
+
+- Provide feedback of sent DTMF tones. Emit SendingTones signal if modem can
+  provide approximate starting and stopping times for DTMF tones. Signal
+  argument contains a string of DTMF tones to be sent, or empty string when
+  all tones has been sent.
+
+  Priority: Medium
+  Complexity: C2
+
+- Blacklisting. According to 3GPP TS 22.001 annex E, the TE must provide
+  automatic calling repeat call attempt restrictions.
+
+  There should be a method to manually reset blacklisting.
+
+  Priority: Medium
+  Complexity: C1
+
+
+Sim Toolkit
+===========
+
+- Support of the BIP (Bearer Independent Protocol) proactive commands.
+  The specification defines several bearer types. For now, only the packet data
+  service bearer is considered.
+
+       - OPEN CHANNEL: requests the terminal to open a data channel with
+       parameters indicated in the command. A user confirmation may be
+       requested by the SimToolkitAgent.
+       - CLOSE CHANNEL:requests the terminal to close the specified data
+       channel.
+       - RECEIVE DATA:requests the terminal to return to the UICC data
+       received on the specified channel.
+       - SEND DATA:requests the terminal to send on the specified channel data
+       provided by the UICC.
+       - GET CHANNEL STATUS: requests the terminal to return the current
+       status of all available data channels.
+
+  Priority: Medium
+  Complexity: C4
+  Owner: Philippe Nunes <philippe.nunes@linux.intel.com>
+
+- Support Setup Event List proactive command.
+  To fully support the class 'e', the following events
+       -Data Available event
+       -Channel status event
+  shall be monitored by oFono if part of the current event list.
+  This list is supplied by the last SETUP EVENT LIST command.
+
+  Priority: Medium
+  Complexity: C2
+  Owner: Philippe Nunes <philippe.nunes@linux.intel.com>
+
+Miscellaneous
+=============
+
+- PolicyKit support.  Add support for PolicyKit checking of all oFono D-Bus
+  interfaces.
+
+  Complexity: C4
+  Priority: Low
+
+- Add Location Service API for providing basic E911 support.
+  This will be based on the 27.007 defined AT commands using
+  XML for transport of positioning request and responses.
+
+  Priority: Medium
+  Complexity: C2
+
+
+CDMA Voicecall
+==============
+
+- Add support for Mobile Originated and Mobile Terminated Voice Call over
+  a CDMA network. This includes management of call state and providing
+  appropriate values for the LineIdentification in each case.
+
+  Priority: High
+  Complexity: C2
+
+- Add support for Three-Way Calling over a CDMA network. Three-Way Calling
+  provides the subscriber with the capability to add a third party to an
+  established two-party call, so that all three parties may communicate in a
+  three-way call.
+
+  In CDMA mode, the originating subscriber of a current conversation can
+  request for a third party to be added to a conversation by sending a Flash
+  With Information Message (FWIM) with dialed digits to the network. Upon
+  setting up a two-way conversation with the added party, the originating
+  subscriber can request to establish a three-way conversation by sending
+  another Flash With Information Message. Upon receiving the second Flash With
+  Information Message, the MSC reconnects the original party to the
+  conversation thus completing the setup of a three-way conversation.
+
+  CDMA Three-Way Calling is described by Figure B-5 in 3GPP2 C.S0005-E Version
+  2.0.
+
+  Priority: High
+  Complexity: C2
+
+- Add support for Call Waiting over a CDMA network. Call Waiting (CW) provides
+  notification of an incoming call to an originating subscriber, while the
+  subscriber's call is in the 2-way state. Subsequently, the originating
+  subscriber can either answer or ignore the incoming call. If the originating
+  subscriber answers the second call, it may alternate between the two calls.
+
+  In CDMA mode, the originating subscriber of a current conversation will
+  receive either a Flash With Information Message or an Alert With Information
+  Message from the network if there is an additional mobile terminated voice
+  call incoming. The originating subscriber can change conversation parties by
+  sending a Flash With Information Message to the network and the MSC will
+  toggle the speech path between the two conversations.
+
+  CDMA Call Waiting is described by Figure B-6 in 3GPP2 C.S0005-E Version
+  2.0.
+
+  Priority: High
+  Complexity: C2
+
+- Support sending DTMF tones over CDMA network.
+
+  Priority: High
+  Complexity: C2
+
+- Support optional network-based Plus Code Dialing for international calls over
+  a CDMA network. An input key, e.g. the "+" key, or a functional equivalent
+  can be used to replace the international access prefix when dialing. When
+  received, transmitted or stored, an international indicator can be included
+  with the address digits although it is the responsibility of the network to
+  ignore the international indicator when attached to a national number. This
+  is described in Section 2.7.1.3.2.4 of 3GPP2 C.S0005-E v2.0 and Section 1.2
+  of 3GPP2 N.S0027 v1.0.
+
+  Priority: High
+  Complexity: C2
+
+CDMA SMS
+==============
+
+- Support CDMA SMS stack in PDU mode. This includes basic support of
+  SMS Point-to-Point Message, SMS Broadcast Message and SMS Acknowledge
+  Message as per 3GPP2 C.S0015-B version 2.0.
+
+  Priority: High
+  Complexity: C4
+
+- Support sending Wireless Messaging Teleservice (WMT) Submit Message and
+  receiving WMT Deliver Message as defined 3GPP2 C.S0015-B version 2.0.
+
+  Priority: High
+  Complexity: C4
+
+- Support Delivery Acknowledgment. oFono allows requesting of CDMA SMS
+  Delivery Acknowledgment via the MessageManager's
+  UseDeliveryAcknowledgement property. If enabled, oFono's CDMA SMS stack
+  will encode the Reply Option subparameter in the Submit message and
+  process incoming SMS Delivery Acknowledgment Message. oFono will notify
+  UI either via DBus or history plugin API.
+
+  Priority: Medium
+  Complexity: C2
+
+- Support receiving Voice Mail Notification (VMN) Teleservice Deliver
+  message. CDMA network uses VMN Teleservice to deliver the number of
+  messages stored at the Voice Mail System to the CDMA mobile subscriber.
+
+  Priority: High
+  Complexity: C4
+
+- Support sending Wireless Enhanced Messaging Teleservice (WEMT) Submit
+  Message and receiving WEMT Deliver Messsage as defined 3GPP2 C.S0015-B
+  version 2.0.
+
+  WMT does not support message fragmentation thus can not be used to for
+  long message. WEMT is devised to support long message and Enhanced
+  Messaging Service (EMS). The WEMT SMS message's CHARi field of the
+  subparameter User Data encapsulate GSM-SMS TP-User Data as defined in
+  Section 9.2.3.24 of 3GPP TS 23.040.
+
+  Priority: Medium
+  Complexity: C4
+
+- Support sending Wireless Application Protocol (WAP) Teleservice Submit
+  Message and receiving WAP Deliver Messsage as defined 3GPP2 C.S0015-B
+  version 2.0.
+
+  Priority: Medium
+  Complexity: C4
+
+- Support Call-Back Number. The Call-Back Number subparameter indicates
+  the number to be dialed in reply to a received SMS message.
+
+  In transmit direction, oFono allows setting of Call-Back Number. If the
+  Call Back Number property is set, CDMA SMS stack will encode Call-Back
+  Number subparameter in the Submit Message.
+
+  In receiving direction, oFono will process the Call-Back Number
+  subparameter in the incoming Deliver Message and notify UI of the
+  Call-Back Number together with the newly received text message.
+
+  Priority: Medium
+  Complexity: C2
+
+- Support immediately displayed message. oFono CDMA SMS stack will
+  process the optional Message Display Mode subparameter in the incoming
+  SMS message. If Message Display Mode subparameter indicates the
+  message display mode is Immediate Display, oFono will send
+  ImmediateMessage signal, otherwise oFono will send IncomingMessage
+  signal.
+
+  Priority: Medium
+  Complexity: C2
+
+
+CDMA CMAS
+==============
+
+- Support Commercial Mobile Alert Service (CMAS) over CDMA systems. CMAS
+  over CDMA system is defined in TIA-1149. The CMAS message is carried in
+  the CHARi field of the User Data subparameter of CDMA SMS Broadcast
+  message.
+
+  Priority: Medium
+  Complexity: C4
+
+CDMA Network Acquisition
+========================
+
+- Support reporting of the pilot energy ratio (Ec/Io) measurement for the
+  currently acquired CDMA network.
+
+  Priority: Medium
+  Complexity: C1
+
+- Support of the signal to interference-plus-noise ratio (SINR)
+  measurement for the currently acquired 1xEV-DO data network.
+
+  Priority: Medium
+  Complexity: C1
+
+- Support reporting of the Enhanced Roaming Indicators (ERI) to indicate the
+  current roaming condition of the CDMA mobile device. Each indicator maps to
+  a unique display number within the Standard and Non-Standard service ranges,
+  as described in Section 8 of 3GPP2 C.R1001-C v1.0.
+  These numbers are stored on the device in the (Enhanced) Preferred Roaming
+  List (PRL) and it is the responsibility of the modem to broadcast the
+  relevant indicator for a currently acquired system. Further details of the
+  system acquisition process are described in 3GPP2 C.S0016-B v1.0.
+
+  Priority: Medium
+  Complexity: C2
+
+- Support reporting of identifiers of the currently acquired CDMA network,
+  including the System Identifier (SID) and the Network Identifier (NID),
+  It is the responsibility of the modem to broadcast the relevant identifiers
+  for a currently acquired system, and these identifiers are provided by the
+  network. This is described in 3GPP2 C.S0005-E v2.0.
+
+  Priority: Medium
+  Complexity: C2
+
+- Support International Roaming, including support for reporting the Mobile
+  Country Code (MCC) and the Mobile Network Code (MNC) for the currently
+  acquired network. International Roaming is provided via enhancements to the
+  PRL by encoding the MCC and the (two digit only) MNC in existing SID/NID
+  fields, as described in the CDMA Development Group standards Document "IPRL
+  Enhancements for International Roaming - CDG Doc #86". It is the
+  responsibility of the modem to broadcast the values for the currently
+  acquired system.
+
+  Priority: Medium
+  Complexity: C2
+
+- Support reporting of the current registered operator name in long alphanumeric
+  format. Based on the System Identifier (SID) broadcasted by the modem, the
+  CDMA network name is retrieved from a look-up table (aka the 'mobile
+  broadband provider info' database).
+
+  Priority: Medium
+  Complexity: C2
+
+CDMA Connection Manager
+=======================
+
+- Support Packet Data Service over CDMA (1xRTT and 1xEV-DO) systems. This
+  includes Mobile Originated connection and disconnection features.
+
+  Priority: Medium
+  Complexity: C4
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..ac29c2b
--- /dev/null
@@ -0,0 +1,27 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+       AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+               echo 'void f(){}' > conftest.c
+               if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+                       ac_cv_prog_cc_pie=yes
+               else
+                       ac_cv_prog_cc_pie=no
+               fi
+               rm -rf conftest*
+       ])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+       if (test "${CFLAGS}" = ""); then
+               CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2"
+       fi
+       if (test "$USE_MAINTAINER_MODE" = "yes"); then
+               CFLAGS="$CFLAGS -Werror -Wextra"
+               CFLAGS="$CFLAGS -Wno-unused-parameter"
+               CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+               CFLAGS="$CFLAGS -Wdeclaration-after-statement"
+               CFLAGS="$CFLAGS -Wmissing-declarations"
+               CFLAGS="$CFLAGS -Wredundant-decls"
+               CFLAGS="$CFLAGS -Wcast-align"
+               CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
+       fi
+])
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..ea380ab
--- /dev/null
@@ -0,0 +1,9171 @@
+# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008, 2009  Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.63],,
+[m4_warning([this file was generated for autoconf 2.63.
+You have another version of autoconf.  It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008 Free Software Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008 Free Software Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+])
+
+# serial 56 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+       [m4_default([$3],
+                  [m4_fatal([Libtool version $1 or higher is required],
+                            63)])],
+       [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+  *\ * | *\    *)
+    AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
+m4_defun([_LT_CC_BASENAME],
+[for cc_temp in $1""; do
+  case $cc_temp in
+    compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+    distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+_LT_PROG_ECHO_BACKSLASH
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    _LT_PATH_MAGIC
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from `configure', and `config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool.  Notably,
+# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain="$ac_aux_dir/ltmain.sh"
+])# _LT_PROG_LTMAIN
+
+
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the `libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+              [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME.  Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+    [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+       [m4_ifval([$1], [$1], [$2])])
+    lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+    m4_ifval([$4],
+       [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+    lt_dict_add_subkey([lt_decl_dict], [$2],
+       [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+  [0], [m4_fatal([$0: too few arguments: $#])],
+  [1], [m4_fatal([$0: too few arguments: $#: $1])],
+  [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+  [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+  [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+    m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+    m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+     m4_if([$2], [],
+          m4_quote(lt_decl_varnames),
+       m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+                       lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to `config.status' so that its
+# declaration there will have the same value as in `configure'.  VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly.  In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+#    <var>='`$ECHO "X$<var>" | $Xsed -e "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+    [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags="_LT_TAGS"dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+#    # Some comment about what VAR is for.
+#    visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+                                          [description])))[]dnl
+m4_pushdef([_libtool_name],
+    m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+    [0], [_libtool_name=[$]$1],
+    [1], [_libtool_name=$lt_[]$1],
+    [2], [_libtool_name=$lt_[]$1],
+    [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# script.  Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+    m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS.  Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into `config.status', and then the shell code to quote escape them in
+# for loops in `config.status'.  Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+       dnl If the libtool generation code has been placed in $CONFIG_LT,
+       dnl instead of duplicating it all over again into config.status,
+       dnl then we will have config.status run $CONFIG_LT later, so it
+       dnl needs to know what name is stored there:
+        [AC_CONFIG_COMMANDS([libtool],
+            [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+    dnl If the libtool generation code is destined for config.status,
+    dnl expand the accumulated commands and init code now:
+    [AC_CONFIG_COMMANDS([libtool],
+        [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+    case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+    case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\[$]0 --fallback-echo"')dnl "
+  lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\`
+  ;;
+esac
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+cat >"$CONFIG_LT" <<_LTEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate a libtool stub with the current configuration.
+
+lt_cl_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AS_SHELL_SANITIZE
+_AS_PREPARE
+
+exec AS_MESSAGE_FD>&1
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+  echo
+  AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+  -h, --help      print this help, then exit
+  -V, --version   print version number, then exit
+  -q, --quiet     do not print progress messages
+  -d, --debug     don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+  case $[1] in
+    --version | --v* | -V )
+      echo "$lt_cl_version"; exit 0 ;;
+    --help | --h* | -h )
+      echo "$lt_cl_help"; exit 0 ;;
+    --debug | --d* | -d )
+      debug=: ;;
+    --quiet | --q* | --silent | --s* | -q )
+      lt_cl_silent=: ;;
+
+    -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+    *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+  esac
+  shift
+done
+
+if $lt_cl_silent; then
+  exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure.  Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+if test "$no_create" != yes; then
+  lt_cl_success=:
+  test "$silent" = yes &&
+    lt_config_lt_args="$lt_config_lt_args --quiet"
+  exec AS_MESSAGE_LOG_FD>/dev/null
+  $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+  exec AS_MESSAGE_LOG_FD>>config.log
+  $lt_cl_success || AS_EXIT(1)
+fi
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars.  Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+  m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+  m4_if(_LT_TAG, [C], [
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+  _LT_PROG_LTMAIN
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+    || (rm -f "$cfgfile"; exit 1)
+
+  _LT_PROG_XSI_SHELLFNS
+
+  sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+    || (rm -f "$cfgfile"; exit 1)
+
+  mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+#    autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+  [C],                 [_LT_LANG(C)],
+  [C++],               [_LT_LANG(CXX)],
+  [Java],              [_LT_LANG(GCJ)],
+  [Fortran 77],                [_LT_LANG(F77)],
+  [Fortran],           [_LT_LANG(FC)],
+  [Windows Resource],  [_LT_LANG(RC)],
+  [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+    [_LT_LANG($1)],
+    [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+  [LT_SUPPORTED_TAG([$1])dnl
+  m4_append([_LT_TAGS], [$1 ])dnl
+  m4_define([_LT_LANG_]$1[_enabled], [])dnl
+  _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+  [LT_LANG(CXX)],
+  [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+  [LT_LANG(F77)],
+  [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+  [LT_LANG(FC)],
+  [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+  [LT_LANG(GCJ)],
+  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+    [LT_LANG(GCJ)],
+    [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+      [LT_LANG(GCJ)],
+      [m4_ifdef([AC_PROG_GCJ],
+       [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([A][M_PROG_GCJ],
+       [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([LT_PROG_GCJ],
+       [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+  [LT_LANG(RC)],
+  [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+  case $host_os in
+    rhapsody* | darwin*)
+    AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+    AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+    AC_CHECK_TOOL([LIPO], [lipo], [:])
+    AC_CHECK_TOOL([OTOOL], [otool], [:])
+    AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+    _LT_DECL([], [DSYMUTIL], [1],
+      [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+    _LT_DECL([], [NMEDIT], [1],
+      [Tool to change global to local symbols on Mac OS X])
+    _LT_DECL([], [LIPO], [1],
+      [Tool to manipulate fat objects and archives on Mac OS X])
+    _LT_DECL([], [OTOOL], [1],
+      [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+    _LT_DECL([], [OTOOL64], [1],
+      [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+    AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+      [lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+       # By default we will add the -single_module flag. You can override
+       # by either setting the environment variable LT_MULTI_MODULE
+       # non-empty at configure time, or by adding -multi_module to the
+       # link flags.
+       rm -rf libconftest.dylib*
+       echo "int foo(void){return 1;}" > conftest.c
+       echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+       $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+         -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+       if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+         lt_cv_apple_cc_single_mod=yes
+       else
+         cat conftest.err >&AS_MESSAGE_LOG_FD
+       fi
+       rm -rf libconftest.dylib*
+       rm -f conftest.*
+      fi])
+    AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+      [lt_cv_ld_exported_symbols_list],
+      [lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+       [lt_cv_ld_exported_symbols_list=yes],
+       [lt_cv_ld_exported_symbols_list=no])
+       LDFLAGS="$save_LDFLAGS"
+    ])
+    case $host_os in
+    rhapsody* | darwin1.[[012]])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+       10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+       10.[[012]]*)
+         _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+       10.*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES
+# --------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+  m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_automatic, $1)=yes
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+  _LT_TAGVAR(link_all_deplibs, $1)=yes
+  _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=echo
+    _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+    m4_if([$1], [CXX],
+[   if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+      _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+      _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+    fi
+],[])
+  else
+  _LT_TAGVAR(ld_shlibs, $1)=no
+  fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX
+# -----------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+AC_LINK_IFELSE(AC_LANG_PROGRAM,[
+lt_aix_libpath_sed='
+    /Import File Strings/,/^$/ {
+       /^0/ {
+           s/^0  *\(.*\)$/\1/
+           p
+       }
+    }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+  aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi],[])
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[ifdef([AC_DIVERSION_NOTICE],
+            [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+        [AC_DIVERT_PUSH(NOTICE)])
+$1
+AC_DIVERT_POP
+])# _LT_SHELL_INIT
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Add some code to the start of the generated configure script which
+# will find an echo command which doesn't interpret backslashes.
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[_LT_SHELL_INIT([
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+  # Remove one level of quotation (which was required for Make).
+  ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
+  ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X[$]1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X[$]1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+  # Yippee, $ECHO works!
+  :
+else
+  # Restart under the correct shell.
+  exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
+fi
+
+if test "X[$]1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<_LT_EOF
+[$]*
+_LT_EOF
+  exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+  if test "X${echo_test_string+set}" != Xset; then
+    # find a string as large as possible, as long as the shell can cope with it
+    for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
+      # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+      if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+        { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+      then
+        break
+      fi
+    done
+  fi
+
+  if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+     echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+     test "X$echo_testing_string" = "X$echo_test_string"; then
+    :
+  else
+    # The Solaris, AIX, and Digital Unix default echo programs unquote
+    # backslashes.  This makes it impossible to quote backslashes using
+    #   echo "$something" | sed 's/\\/\\\\/g'
+    #
+    # So, first we look for a working echo in the user's PATH.
+
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for dir in $PATH /usr/ucb; do
+      IFS="$lt_save_ifs"
+      if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+         test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+         echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+         test "X$echo_testing_string" = "X$echo_test_string"; then
+        ECHO="$dir/echo"
+        break
+      fi
+    done
+    IFS="$lt_save_ifs"
+
+    if test "X$ECHO" = Xecho; then
+      # We didn't find a better echo, so look for alternatives.
+      if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+         echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+         test "X$echo_testing_string" = "X$echo_test_string"; then
+        # This shell has a builtin print -r that does the trick.
+        ECHO='print -r'
+      elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+          test "X$CONFIG_SHELL" != X/bin/ksh; then
+        # If we have ksh, try running configure again with it.
+        ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+        export ORIGINAL_CONFIG_SHELL
+        CONFIG_SHELL=/bin/ksh
+        export CONFIG_SHELL
+        exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
+      else
+        # Try using printf.
+        ECHO='printf %s\n'
+        if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+          echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+          test "X$echo_testing_string" = "X$echo_test_string"; then
+         # Cool, printf works
+         :
+        elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+            test "X$echo_testing_string" = 'X\t' &&
+            echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+            test "X$echo_testing_string" = "X$echo_test_string"; then
+         CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+         export CONFIG_SHELL
+         SHELL="$CONFIG_SHELL"
+         export SHELL
+         ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+        elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+            test "X$echo_testing_string" = 'X\t' &&
+            echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+            test "X$echo_testing_string" = "X$echo_test_string"; then
+         ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+        else
+         # maybe with a smaller string...
+         prev=:
+
+         for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
+           if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+           then
+             break
+           fi
+           prev="$cmd"
+         done
+
+         if test "$prev" != 'sed 50q "[$]0"'; then
+           echo_test_string=`eval $prev`
+           export echo_test_string
+           exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
+         else
+           # Oops.  We lost completely, so just stick with echo.
+           ECHO=echo
+         fi
+        fi
+      fi
+    fi
+  fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
+   lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
+fi
+
+AC_SUBST(lt_ECHO)
+])
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1],
+    [An echo program that does not interpret backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+  [AS_HELP_STRING([--disable-libtool-lock],
+    [avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+       HPUX_IA64_MODE="32"
+       ;;
+      *ELF-64*)
+       HPUX_IA64_MODE="64"
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '[#]line __oline__ "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -melf32bsmip"
+         ;;
+       *N32*)
+         LD="${LD-ld} -melf32bmipn32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -melf64bmip"
+       ;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -32"
+         ;;
+       *N32*)
+         LD="${LD-ld} -n32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -64"
+         ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_i386_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_i386"
+           ;;
+         ppc64-*linux*|powerpc64-*linux*)
+           LD="${LD-ld} -m elf32ppclinux"
+           ;;
+         s390x-*linux*)
+           LD="${LD-ld} -m elf_s390"
+           ;;
+         sparc64-*linux*)
+           LD="${LD-ld} -m elf32_sparc"
+           ;;
+       esac
+       ;;
+      *64-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_x86_64_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_x86_64"
+           ;;
+         ppc*-*linux*|powerpc*-*linux*)
+           LD="${LD-ld} -m elf64ppc"
+           ;;
+         s390*-*linux*|s390*-*tpf*)
+           LD="${LD-ld} -m elf64_s390"
+           ;;
+         sparc*-*linux*)
+           LD="${LD-ld} -m elf64_sparc"
+           ;;
+       esac
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_LANG_PUSH(C)
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+     AC_LANG_POP])
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+sparc*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*) LD="${LD-ld} -m elf64_sparc" ;;
+      *)
+       if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+         LD="${LD-ld} -64"
+       fi
+       ;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+])# _LT_ENABLE_LOCK
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[AC_CHECK_TOOL(AR, ar, false)
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1])
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+    [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+    [Commands used to build an old-style archive])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#              [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$3"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       $2=yes
+     fi
+   fi
+   $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$5], , :, [$5])
+else
+    m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#                  [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $3"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&AS_MESSAGE_LOG_FD
+       $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         $2=yes
+       fi
+     else
+       $2=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$4], , :, [$4])
+else
+    m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+  i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536      # usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[        ]]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+                = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+             test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+  AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+    [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+#                      ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+  [$4]
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+[#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL          RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL                DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL                0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW         DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW       RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW     DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW     0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}]
+_LT_EOF
+  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) $1 ;;
+      x$lt_dlneed_uscore) $2 ;;
+      x$lt_dlunknown|x*) $3 ;;
+    esac
+  else :
+    # compilation failed
+    $3
+  fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    AC_CHECK_LIB([dl], [dlopen],
+               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ])
+    ;;
+
+  *)
+    AC_CHECK_FUNC([shl_load],
+         [lt_cv_dlopen="shl_load"],
+      [AC_CHECK_LIB([dld], [shl_load],
+           [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+       [AC_CHECK_FUNC([dlopen],
+             [lt_cv_dlopen="dlopen"],
+         [AC_CHECK_LIB([dl], [dlopen],
+               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+           [AC_CHECK_LIB([svld], [dlopen],
+                 [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+             [AC_CHECK_LIB([dld], [dld_link],
+                   [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+             ])
+           ])
+         ])
+       ])
+      ])
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    AC_CACHE_CHECK([whether a program can dlopen itself],
+         lt_cv_dlopen_self, [dnl
+         _LT_TRY_DLOPEN_SELF(
+           lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+           lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+    ])
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+         lt_cv_dlopen_self_static, [dnl
+         _LT_TRY_DLOPEN_SELF(
+           lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+           lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
+      ])
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+        [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+        [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+        [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+     fi
+   fi
+   chmod u+w . 2>&AS_MESSAGE_LOG_FD
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+       [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links="nottested"
+if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  AC_MSG_CHECKING([if we can lock with hard links])
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  AC_MSG_RESULT([$hard_links])
+  if test "$hard_links" = no; then
+    AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+         [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
+  [Define to the sub-directory in which libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+   test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+   test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+     test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+    # Linking always hardcodes the temporary library directory.
+    _LT_TAGVAR(hardcode_action, $1)=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    _LT_TAGVAR(hardcode_action, $1)=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
+   test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+    [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      AC_MSG_RESULT([yes])
+    else
+      AC_MSG_RESULT([no])
+    fi
+    ;;
+  *)
+    AC_MSG_RESULT([no])
+    ;;
+  esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+       [], [
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+  if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+  else
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+  fi
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+       lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+  if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+  sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[[4-9]]*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[[01]] | aix4.[[01]].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+          echo ' yes '
+          echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+       :
+      else
+       can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[[45]]*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$host_os in
+  yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+      if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+        # It is most probably a Windows format PATH printed by
+        # mingw gcc, but we are running on Cygwin. Gcc prints its search
+        # path with ; separators, and with drive letters. We can handle the
+        # drive letters (cygwin fileutils understands them), so leave them,
+        # especially as we might pass files found there to a mingw objdump,
+        # which wouldn't understand a cygwinified path. Ahh.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    ;;
+
+  *)
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[[123]]*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+  freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+interix[[3-9]]*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+       if test "$lt_cv_prog_gnu_ld" = yes; then
+               version_type=linux
+       else
+               version_type=irix
+       fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # Some binutils ld are patched to set DT_RUNPATH
+  save_LDFLAGS=$LDFLAGS
+  save_libdir=$libdir
+  eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+       LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+    [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+       [shlibpath_overrides_runpath=yes])])
+  LDFLAGS=$save_LDFLAGS
+  libdir=$save_libdir
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Add ABI-specific directories to the system library path.
+  sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[  ]*hwcap[        ]/d;s/[:,      ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+    *)                         need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[[89]] | openbsd2.[[89]].*)
+       shlibpath_overrides_runpath=no
+       ;;
+      *)
+       shlibpath_overrides_runpath=yes
+       ;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+       ;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+    [Variables whose values should be saved in libtool wrapper scripts and
+    restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+    [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0],  [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+    [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+    [[List of archive names.  First name is the real one, the rest are links.
+    The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+    [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [postinstall_cmds], [2],
+    [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+    [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+    [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+    [[As "finish_cmds", except a single script fragment to be evaled but
+    not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+    [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+    [Compile-time system search path for libraries])
+_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
+    [Run-time system search path for libraries])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program which can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] |  ?:[\\/]*])
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word.  This closes a longstanding sh security hole.
+  ac_dummy="m4_if([$2], , $PATH, [$2])"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$1; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+      if test -n "$file_magic_test_file"; then
+       case $deplibs_check_method in
+       "file_magic "*)
+         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+           $EGREP "$file_magic_regex" > /dev/null; then
+           :
+         else
+           cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+         fi ;;
+       esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  AC_MSG_RESULT($MAGIC_CMD)
+else
+  AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+        [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program which can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+  else
+    MAGIC_CMD=:
+  fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_ARG_WITH([gnu-ld],
+    [AS_HELP_STRING([--with-gnu-ld],
+       [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+    [test "$withval" = no || with_gnu_ld=yes],
+    [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by $CC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]]* | ?:[[\\/]]*)
+      re_direlt='/[[^/]][[^/]]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+       ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+       test "$with_gnu_ld" != no && break
+       ;;
+      *)
+       test "$with_gnu_ld" != yes && break
+       ;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+#   -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+  lt_cv_ld_reload_flag,
+  [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+_LT_DECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_DECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[[45]]*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  if ( file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]']
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[[3-9]]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+])
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+    [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+    [Command to use when deplibs_check_method == "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+       # Check to see if the nm accepts a BSD-compat flag.
+       # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+       #   nm: unknown option "B" ignored
+       # Tru64's nm complains that /dev/null is an invalid object file
+       case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+       */dev/null* | *'Invalid file or object type'*)
+         lt_cv_path_NM="$tmp_nm -B"
+         break
+         ;;
+       *)
+         case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+         */dev/null*)
+           lt_cv_path_NM="$tmp_nm -p"
+           break
+           ;;
+         *)
+           lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+           continue # so that we can try to find one that supports BSD flags
+           ;;
+         esac
+         ;;
+       esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :)
+  AC_SUBST([DUMPBIN])
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+  [lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD)
+  cat conftest.out >&AS_MESSAGE_LOG_FD
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
+  # These system don't have libm, or don't need it
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, cos, LIBM="-lm")
+  ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+  _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+
+  _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+    lt_cv_prog_compiler_rtti_exceptions,
+    [-fno-rtti -fno-exceptions], [],
+    [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+       [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[[BCDT]]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[[ABCDGISTW]]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[[ABCDEGRST]]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[[BCDEGRST]]'
+  ;;
+osf*)
+  symcode='[[BCDEGQRST]]'
+  ;;
+solaris*)
+  symcode='[[BDRT]]'
+  ;;
+sco3.2v5*)
+  symcode='[[DT]]'
+  ;;
+sysv4.2uw2*)
+  symcode='[[DT]]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[[ABDT]]'
+  ;;
+sysv4)
+  symcode='[[DFNSTU]]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK ['"\
+"     {last_section=section; section=\$ 3};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx]"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[    ]]\($symcode$symcode*\)[[       ]][[    ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if AC_TRY_EVAL(ac_compile); then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+       mv -f "$nlist"T "$nlist"
+      else
+       rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+       if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+         cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+         # Now generate the symbol file.
+         eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+         cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+const struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+         $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+         cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+         # Now try linking the two files.
+         mv conftest.$ac_objext conftstm.$ac_objext
+         lt_save_LIBS="$LIBS"
+         lt_save_CFLAGS="$CFLAGS"
+         LIBS="conftstm.$ac_objext"
+         CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+         if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+           pipe_works=yes
+         fi
+         LIBS="$lt_save_LIBS"
+         CFLAGS="$lt_save_CFLAGS"
+       else
+         echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+       fi
+      else
+       echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+    fi
+  else
+    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  AC_MSG_RESULT(failed)
+else
+  AC_MSG_RESULT(ok)
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+    [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+    [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_c_name_address],
+    [lt_cv_sys_global_symbol_to_c_name_address], [1],
+    [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+    [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+    [Transform the output of nm in a C name address pair when lib prefix is needed])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+AC_MSG_CHECKING([for $compiler option to produce PIC])
+m4_if([$1], [CXX], [
+  # C++ specific cases for pic, static, wl, etc.
+  if test "$GXX" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | cygwin* | os2* | pw32* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+      ;;
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       ;;
+      esac
+      ;;
+    *qnx* | *nto*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    case $host_os in
+      aix[[4-9]]*)
+       # All AIX code is PIC.
+       if test "$host_cpu" = ia64; then
+         # AIX 5 now supports IA64 processor
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+       else
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+       fi
+       ;;
+      chorus*)
+       case $cc_basename in
+       cxch68*)
+         # Green Hills C++ Compiler
+         # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+         ;;
+       esac
+       ;;
+      dgux*)
+       case $cc_basename in
+         ec++*)
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           ;;
+         ghcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      freebsd* | dragonfly*)
+       # FreeBSD uses GNU C++
+       ;;
+      hpux9* | hpux10* | hpux11*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+           if test "$host_cpu" != ia64; then
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+           fi
+           ;;
+         aCC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+           case $host_cpu in
+           hppa*64*|ia64*)
+             # +Z the default
+             ;;
+           *)
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+             ;;
+           esac
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      interix*)
+       # This is c89, which is MS Visual C++ (no shared libs)
+       # Anyone wants to do a port?
+       ;;
+      irix5* | irix6* | nonstopux*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           # CC pic flag -KPIC is the default.
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      linux* | k*bsd*-gnu)
+       case $cc_basename in
+         KCC*)
+           # KAI C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+           ;;
+         ecpc* )
+           # old Intel C++ for x86_64 which still supported -KPIC.
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+           ;;
+         icpc* )
+           # Intel C++, used to be incompatible with GCC.
+           # ICC 10 doesn't accept -KPIC any more.
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+           ;;
+         pgCC* | pgcpp*)
+           # Portland Group C++ compiler
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+         cxx*)
+           # Compaq C++
+           # Make sure the PIC flag is empty.  It appears that all Alpha
+           # Linux and Compaq Tru64 Unix objects are PIC.
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           ;;
+         xlc* | xlC*)
+           # IBM XL 8.0 on PPC
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+           ;;
+         *)
+           case `$CC -V 2>&1 | sed 5q` in
+           *Sun\ C*)
+             # Sun C++ 5.9
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+             _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+             _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+             ;;
+           esac
+           ;;
+       esac
+       ;;
+      lynxos*)
+       ;;
+      m88k*)
+       ;;
+      mvs*)
+       case $cc_basename in
+         cxx*)
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      netbsd*)
+       ;;
+      *qnx* | *nto*)
+        # QNX uses GNU C++, but need to define -shared option too, otherwise
+        # it will coredump.
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+        ;;
+      osf3* | osf4* | osf5*)
+       case $cc_basename in
+         KCC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+           ;;
+         RCC*)
+           # Rational C++ 2.4.1
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         cxx*)
+           # Digital/Compaq C++
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           # Make sure the PIC flag is empty.  It appears that all Alpha
+           # Linux and Compaq Tru64 Unix objects are PIC.
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      psos*)
+       ;;
+      solaris*)
+       case $cc_basename in
+         CC*)
+           # Sun C++ 4.2, 5.x and Centerline C++
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+           ;;
+         gcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      sunos4*)
+       case $cc_basename in
+         CC*)
+           # Sun C++ 4.x
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+         lcc*)
+           # Lucid
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+       esac
+       ;;
+      tandem*)
+       case $cc_basename in
+         NCC*)
+           # NonStop-UX NCC 3.20
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      vxworks*)
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+       ;;
+    esac
+  fi
+],
+[
+  if test "$GCC" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       # +Z the default
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       ;;
+      esac
+      ;;
+
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      else
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+       # +Z the default
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+       ;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC (with -KPIC) is the default.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+       ;;
+      pgcc* | pgf77* | pgf90* | pgf95*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+       # which looks to be a dead project)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+        ;;
+      ccc*)
+        _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+        # All Alpha code is PIC.
+        _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+        ;;
+      xl*)
+       # IBM XL C 8.0/Fortran 10.1 on PPC
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+       ;;
+      *)
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ C*)
+         # Sun C 5.9
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+         ;;
+       *Sun\ F*)
+         # Sun Fortran 8.3 passes all unrecognized flags to the linker
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+         ;;
+       esac
+       ;;
+      esac
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # All OSF/1 code is PIC.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    rdos*)
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    unicos*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+    esac
+  fi
+])
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+    ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+    ;;
+esac
+AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+       [How to pass a linker flag through the compiler])
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+  _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+    [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+    [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+    [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+     "" | " "*) ;;
+     *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+     esac],
+    [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+     _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+       [Additional compiler flags for building library objects])
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+  _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+  $lt_tmp_static_flag,
+  [],
+  [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+       [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  case $host_os in
+  aix[[4-9]]*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to AIX nm, but means don't demangle with GNU nm
+    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    else
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+  ;;
+  cygwin* | mingw* | cegcc*)
+    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+  ;;
+  *)
+    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  ;;
+  esac
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+], [
+  runpath_var=
+  _LT_TAGVAR(allow_undefined_flag, $1)=
+  _LT_TAGVAR(always_export_symbols, $1)=no
+  _LT_TAGVAR(archive_cmds, $1)=
+  _LT_TAGVAR(archive_expsym_cmds, $1)=
+  _LT_TAGVAR(compiler_needs_object, $1)=no
+  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+  _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(hardcode_automatic, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+  _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+  _LT_TAGVAR(hardcode_libdir_separator, $1)=
+  _LT_TAGVAR(hardcode_minus_L, $1)=no
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  _LT_TAGVAR(inherit_rpath, $1)=no
+  _LT_TAGVAR(link_all_deplibs, $1)=unknown
+  _LT_TAGVAR(module_cmds, $1)=
+  _LT_TAGVAR(module_expsym_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+  _LT_TAGVAR(thread_safe_flag_spec, $1)=
+  _LT_TAGVAR(whole_archive_flag_spec, $1)=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  _LT_TAGVAR(include_expsyms, $1)=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  esac
+
+  _LT_TAGVAR(ld_shlibs, $1)=yes
+  if test "$with_gnu_ld" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[[3-9]]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+       # support --undefined.  This deserves some investigation.  FIXME
+       _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+      # as there is no search path for DLLs.
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=no
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+       # If the export-symbols file already is a .def file (1st line
+       # is EXPORTS), use it as is; otherwise, prepend...
+       _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+         cp $export_symbols $output_objdir/$soname.def;
+       else
+         echo EXPORTS > $output_objdir/$soname.def;
+         cat $export_symbols >> $output_objdir/$soname.def;
+       fi~
+       $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    interix[[3-9]]*)
+      _LT_TAGVAR(hardcode_direct, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+       case $cc_basename in
+         diet\ *) tmp_diet=yes;;       # linux-dietlibc with static linking (!diet-dyn)
+       esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+        && test "$tmp_diet" = no
+      then
+       tmp_addflag=
+       tmp_sharedflag='-shared'
+       case $cc_basename,$host_cpu in
+        pgcc*)                         # Portland Group C compiler
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag'
+         ;;
+       pgf77* | pgf90* | pgf95*)       # Portland Group f77 and f90 compilers
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag -Mnomain' ;;
+       ecc*,ia64* | icc*,ia64*)        # Intel C compiler on ia64
+         tmp_addflag=' -i_dynamic' ;;
+       efc*,ia64* | ifort*,ia64*)      # Intel Fortran compiler on ia64
+         tmp_addflag=' -i_dynamic -nofor_main' ;;
+       ifc* | ifort*)                  # Intel Fortran compiler
+         tmp_addflag=' -nofor_main' ;;
+       lf95*)                          # Lahey Fortran 8.1
+         _LT_TAGVAR(whole_archive_flag_spec, $1)=
+         tmp_sharedflag='--shared' ;;
+       xl[[cC]]*)                      # IBM XL C 8.0 on PPC (deal with xlf below)
+         tmp_sharedflag='-qmkshrobj'
+         tmp_addflag= ;;
+       esac
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ C*)                       # Sun C 5.9
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+         _LT_TAGVAR(compiler_needs_object, $1)=yes
+         tmp_sharedflag='-G' ;;
+       *Sun\ F*)                       # Sun Fortran 8.3
+         tmp_sharedflag='-G' ;;
+       esac
+       _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+           cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+           echo "local: *; };" >> $output_objdir/$libname.ver~
+           $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+       case $cc_basename in
+       xlf*)
+         # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+         _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
+         _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+         if test "x$supports_anon_versioning" = xyes; then
+           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+             cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+             echo "local: *; };" >> $output_objdir/$libname.ver~
+             $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+         fi
+         ;;
+       esac
+      else
+        _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    netbsd*)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+       wlarc=
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+       ;;
+       *)
+         # For security reasons, it is highly recommended that you always
+         # use absolute paths for naming shared libraries, and exclude the
+         # DT_RUNPATH tag from executables and libraries.  But doing so
+         # requires that you compile everything twice, which is a pain.
+         if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+         else
+           _LT_TAGVAR(ld_shlibs, $1)=no
+         fi
+       ;;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+    esac
+
+    if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+      runpath_var=
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+       # Neither direct hardcoding nor static linking is supported with a
+       # broken collect2.
+       _LT_TAGVAR(hardcode_direct, $1)=unsupported
+      fi
+      ;;
+
+    aix[[4-9]]*)
+      if test "$host_cpu" = ia64; then
+       # On IA64, the linker does run time linking by default, so we don't
+       # have to do anything special.
+       aix_use_runtimelinking=no
+       exp_sym_flag='-Bexport'
+       no_entry_flag=""
+      else
+       # If we're using GNU nm, then we don't want the "-C" option.
+       # -C means demangle to AIX nm, but means don't demangle with GNU nm
+       if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+         _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       else
+         _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       fi
+       aix_use_runtimelinking=no
+
+       # Test if we are trying to use run time linking or normal
+       # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+       # need to do runtime linking.
+       case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+         for ld_flag in $LDFLAGS; do
+         if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+           aix_use_runtimelinking=yes
+           break
+         fi
+         done
+         ;;
+       esac
+
+       exp_sym_flag='-bexport'
+       no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      _LT_TAGVAR(archive_cmds, $1)=''
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+      if test "$GCC" = yes; then
+       case $host_os in aix4.[[012]]|aix4.[[012]].*)
+       # We only want to do this on AIX 4.2 and lower, the check
+       # below for broken collect2 doesn't work under 4.3+
+         collect2name=`${CC} -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+          strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+         # We have reworked collect2
+         :
+         else
+         # We have old collect2
+         _LT_TAGVAR(hardcode_direct, $1)=unsupported
+         # It fails to find uninstalled libraries when the uninstalled
+         # path is not listed in the libpath.  Setting hardcode_minus_L
+         # to unsupported forces relinking
+         _LT_TAGVAR(hardcode_minus_L, $1)=yes
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+         _LT_TAGVAR(hardcode_libdir_separator, $1)=
+         fi
+         ;;
+       esac
+       shared_flag='-shared'
+       if test "$aix_use_runtimelinking" = yes; then
+         shared_flag="$shared_flag "'${wl}-G'
+       fi
+      else
+       # not using gcc
+       if test "$host_cpu" = ia64; then
+       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+       # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+       else
+         if test "$aix_use_runtimelinking" = yes; then
+           shared_flag='${wl}-G'
+         else
+           shared_flag='${wl}-bM:SRE'
+         fi
+       fi
+      fi
+
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      if test "$aix_use_runtimelinking" = yes; then
+       # Warning - without using the other runtime loading flags (-brtl),
+       # -berok will link without error, but may produce a broken library.
+       _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+       if test "$host_cpu" = ia64; then
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+         _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+         _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+       else
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+         # Warning - without using the other run time loading flags,
+         # -berok will link without error, but may produce a broken library.
+         _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+         _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+         # Exported symbols can be pulled into shared objects from archives
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+         _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+         # This is similar to how AIX traditionally builds its shared libraries.
+         _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+       fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[[45]]*)
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      # Tell ltmain to make .lib files, not .a files.
+      libext=lib
+      # Tell ltmain to make .dll files, not .so files.
+      shrext_cmds=".dll"
+      # FIXME: Setting linknames here is a bad hack.
+      _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+      # The linker will automatically build a .lib file if we build a DLL.
+      _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+      # FIXME: Should let the user specify the lib program.
+      _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+      _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`'
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      ;;
+
+    darwin* | rhapsody*)
+      _LT_DARWIN_LINKER_FEATURES($1)
+      ;;
+
+    dgux*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    freebsd1*)
+      _LT_TAGVAR(ld_shlibs, $1)=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+       _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+       _LT_TAGVAR(hardcode_direct, $1)=yes
+       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+       # hardcode_minus_L: Not really in the search PATH,
+       # but as the default location of the library.
+       _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+       case $host_cpu in
+       hppa*64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       esac
+      else
+       case $host_cpu in
+       hppa*64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       esac
+      fi
+      if test "$with_gnu_ld" = no; then
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+       case $host_cpu in
+       hppa*64*|ia64*)
+         _LT_TAGVAR(hardcode_direct, $1)=no
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+         ;;
+       *)
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+         # hardcode_minus_L: Not really in the search PATH,
+         # but as the default location of the library.
+         _LT_TAGVAR(hardcode_minus_L, $1)=yes
+         ;;
+       esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       # Try to use the -exported_symbol ld option, if it does not
+       # work, assume that -exports_file does not work either and
+       # implicitly export all symbols.
+        save_LDFLAGS="$LDFLAGS"
+        LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+        AC_LINK_IFELSE(int foo(void) {},
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+        )
+        LDFLAGS="$save_LDFLAGS"
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(inherit_rpath, $1)=yes
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    netbsd*)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+       _LT_TAGVAR(hardcode_direct, $1)=yes
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+       if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+       else
+         case $host_os in
+          openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+            _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+            ;;
+          *)
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+            ;;
+         esac
+       fi
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    os2*)
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    osf4* | osf5*)     # as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      else
+       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+       $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+       # Both c and cxx compiler support -rpath directly
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+      if test "$GCC" = yes; then
+       wlarc='${wl}'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+       case `$CC -V 2>&1` in
+       *"Compilers 5.0"*)
+         wlarc=''
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+         ;;
+       *)
+         wlarc='${wl}'
+         _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+         ;;
+       esac
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      case $host_os in
+      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+      *)
+       # The compiler driver will combine and reorder linker options,
+       # but understands `-z linker_flag'.  GCC discards it without `$wl',
+       # but is careful enough not to reorder.
+       # Supported since Solaris 2.6 (maybe 2.5.1?)
+       if test "$GCC" = yes; then
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+       else
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+       fi
+       ;;
+      esac
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+       # Use $CC to link under sequent, because it throws in some extra .o
+       # files that make .init and .fini sections work.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+       sni)
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+       ;;
+       siemens)
+         ## LD is ld it makes a PLAMLIB
+         ## CC just makes a GrossModule.
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+         _LT_TAGVAR(hardcode_direct, $1)=no
+        ;;
+       motorola)
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+       ;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4.3*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       runpath_var=LD_RUN_PATH
+       hardcode_runpath_var=yes
+       _LT_TAGVAR(ld_shlibs, $1)=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      _LT_TAGVAR(ld_shlibs, $1)=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+       ;;
+      esac
+    fi
+  fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+    [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+  # Assume -lc should be added
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $_LT_TAGVAR(archive_cmds, $1) in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      AC_MSG_CHECKING([whether -lc should be explicitly linked in])
+      $RM conftest*
+      echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+      if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+        soname=conftest
+        lib=conftest
+        libobjs=conftest.$ac_objext
+        deplibs=
+        wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+       pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+        compiler_flags=-v
+        linker_flags=-v
+        verstring=
+        output_objdir=.
+        libname=conftest
+        lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+        _LT_TAGVAR(allow_undefined_flag, $1)=
+        if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+        then
+         _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+        else
+         _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+        fi
+        _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+      else
+        cat conftest.err 1>&5
+      fi
+      $RM conftest*
+      AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)])
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+    [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+    [enable_shared_with_static_runtimes], [0],
+    [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+    [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+    [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+    [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+    [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+    [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+    [Commands used to build a loadable module if different from building
+    a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+    [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+    [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+    [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+    [Flag to hardcode $libdir into a binary during linking.
+    This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1],
+    [[If ld is used when linking, flag to hardcode $libdir into a binary
+    during linking.  This must work even if $libdir does not exist]])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+    [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary and the resulting library dependency is
+    "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+    library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+    [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+    [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+    [Set to "yes" if building a shared library automatically hardcodes DIR
+    into the library and all subsequent libraries and executables linked
+    against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+    [Set to yes if linker adds runtime paths of dependent libraries
+    to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+    [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [fix_srcfile_path], [1],
+    [Fix the shell variable $srcfile for the compiler])
+_LT_TAGDECL([], [always_export_symbols], [0],
+    [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+    [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+    [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+    [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+    [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [file_list_spec], [1],
+    [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl    [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_SYS_DYNAMIC_LINKER($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+  LT_SYS_DLOPEN_SELF
+  _LT_CMD_STRIPLIB
+
+  # Report which library types will actually be built
+  AC_MSG_CHECKING([if libtool supports shared libraries])
+  AC_MSG_RESULT([$can_build_shared])
+
+  AC_MSG_CHECKING([whether to build shared libraries])
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[[4-9]]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  AC_MSG_RESULT([$enable_shared])
+
+  AC_MSG_CHECKING([whether to build static libraries])
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  AC_MSG_RESULT([$enable_static])
+
+  _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC="$lt_save_CC"
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_PROG_CXX
+# ------------
+# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++
+# compiler, we have our own version here.
+m4_defun([_LT_PROG_CXX],
+[
+pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes])
+AC_PROG_CXX
+if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+    ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+    (test "X$CXX" != "Xg++"))) ; then
+  AC_PROG_CXXCPP
+else
+  _lt_caught_CXX_error=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_CXX
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_CXX], [])
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[AC_REQUIRE([_LT_PROG_CXX])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="int some_variable = 0;"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_LD=$LD
+  lt_save_GCC=$GCC
+  GCC=$GXX
+  lt_save_with_gnu_ld=$with_gnu_ld
+  lt_save_path_LD=$lt_cv_path_LD
+  if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+  else
+    $as_unset lt_cv_prog_gnu_ld
+  fi
+  if test -n "${lt_cv_path_LDCXX+set}"; then
+    lt_cv_path_LD=$lt_cv_path_LDCXX
+  else
+    $as_unset lt_cv_path_LD
+  fi
+  test -z "${LDCXX+set}" || LD=$LDCXX
+  CC=${CXX-"c++"}
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    # We don't want -fno-exception when compiling C++ code, so set the
+    # no_builtin_flag separately
+    if test "$GXX" = yes; then
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+    else
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+    fi
+
+    if test "$GXX" = yes; then
+      # Set up default GNU C++ configuration
+
+      LT_PATH_LD
+
+      # Check if GNU C++ uses GNU ld as the underlying linker, since the
+      # archiving commands below assume that GNU ld is being used.
+      if test "$with_gnu_ld" = yes; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+        # If archive_cmds runs LD, not CC, wlarc should be empty
+        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+        #     investigate it a little bit more. (MM)
+        wlarc='${wl}'
+
+        # ancient GNU ld didn't support --whole-archive et. al.
+        if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+         $GREP 'no-whole-archive' > /dev/null; then
+          _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+        else
+          _LT_TAGVAR(whole_archive_flag_spec, $1)=
+        fi
+      else
+        with_gnu_ld=no
+        wlarc=
+
+        # A generic and very simple default shared library creation
+        # command for GNU C++ for the case where it uses the native
+        # linker, instead of GNU ld.  If possible, this setting should
+        # overridden to take advantage of the native linker features on
+        # the platform it is being used on.
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+      fi
+
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+    else
+      GXX=no
+      with_gnu_ld=no
+      wlarc=
+    fi
+
+    # PORTME: fill in a description of your system's C++ link characteristics
+    AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+    _LT_TAGVAR(ld_shlibs, $1)=yes
+    case $host_os in
+      aix3*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+      aix[[4-9]]*)
+        if test "$host_cpu" = ia64; then
+          # On IA64, the linker does run time linking by default, so we don't
+          # have to do anything special.
+          aix_use_runtimelinking=no
+          exp_sym_flag='-Bexport'
+          no_entry_flag=""
+        else
+          aix_use_runtimelinking=no
+
+          # Test if we are trying to use run time linking or normal
+          # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+          # need to do runtime linking.
+          case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+           for ld_flag in $LDFLAGS; do
+             case $ld_flag in
+             *-brtl*)
+               aix_use_runtimelinking=yes
+               break
+               ;;
+             esac
+           done
+           ;;
+          esac
+
+          exp_sym_flag='-bexport'
+          no_entry_flag='-bnoentry'
+        fi
+
+        # When large executables or shared objects are built, AIX ld can
+        # have problems creating the table of contents.  If linking a library
+        # or program results in "error TOC overflow" add -mminimal-toc to
+        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+        _LT_TAGVAR(archive_cmds, $1)=''
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+        if test "$GXX" = yes; then
+          case $host_os in aix4.[[012]]|aix4.[[012]].*)
+          # We only want to do this on AIX 4.2 and lower, the check
+          # below for broken collect2 doesn't work under 4.3+
+         collect2name=`${CC} -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+            strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+           # We have reworked collect2
+           :
+         else
+           # We have old collect2
+           _LT_TAGVAR(hardcode_direct, $1)=unsupported
+           # It fails to find uninstalled libraries when the uninstalled
+           # path is not listed in the libpath.  Setting hardcode_minus_L
+           # to unsupported forces relinking
+           _LT_TAGVAR(hardcode_minus_L, $1)=yes
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=
+         fi
+          esac
+          shared_flag='-shared'
+         if test "$aix_use_runtimelinking" = yes; then
+           shared_flag="$shared_flag "'${wl}-G'
+         fi
+        else
+          # not using gcc
+          if test "$host_cpu" = ia64; then
+         # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+         # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+          else
+           if test "$aix_use_runtimelinking" = yes; then
+             shared_flag='${wl}-G'
+           else
+             shared_flag='${wl}-bM:SRE'
+           fi
+          fi
+        fi
+
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+        # It seems that -bexpall does not export symbols beginning with
+        # underscore (_), so it is better to generate a list of symbols to
+       # export.
+        _LT_TAGVAR(always_export_symbols, $1)=yes
+        if test "$aix_use_runtimelinking" = yes; then
+          # Warning - without using the other runtime loading flags (-brtl),
+          # -berok will link without error, but may produce a broken library.
+          _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+          # Determine the default libpath from the value encoded in an empty
+          # executable.
+          _LT_SYS_MODULE_PATH_AIX
+          _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+        else
+          if test "$host_cpu" = ia64; then
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+           _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+           _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+          else
+           # Determine the default libpath from the value encoded in an
+           # empty executable.
+           _LT_SYS_MODULE_PATH_AIX
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+           # Warning - without using the other run time loading flags,
+           # -berok will link without error, but may produce a broken library.
+           _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+           _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+           # Exported symbols can be pulled into shared objects from archives
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+           _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+           # This is similar to how AIX traditionally builds its shared
+           # libraries.
+           _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+          fi
+        fi
+        ;;
+
+      beos*)
+       if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+         # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+         # support --undefined.  This deserves some investigation.  FIXME
+         _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       else
+         _LT_TAGVAR(ld_shlibs, $1)=no
+       fi
+       ;;
+
+      chorus*)
+        case $cc_basename in
+          *)
+         # FIXME: insert proper C++ library support
+         _LT_TAGVAR(ld_shlibs, $1)=no
+         ;;
+        esac
+        ;;
+
+      cygwin* | mingw* | pw32* | cegcc*)
+        # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+        # as there is no search path for DLLs.
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+        _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+        _LT_TAGVAR(always_export_symbols, $1)=no
+        _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+        if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+          _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+          # If the export-symbols file already is a .def file (1st line
+          # is EXPORTS), use it as is; otherwise, prepend...
+          _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+           cp $export_symbols $output_objdir/$soname.def;
+          else
+           echo EXPORTS > $output_objdir/$soname.def;
+           cat $export_symbols >> $output_objdir/$soname.def;
+          fi~
+          $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+        else
+          _LT_TAGVAR(ld_shlibs, $1)=no
+        fi
+        ;;
+      darwin* | rhapsody*)
+        _LT_DARWIN_LINKER_FEATURES($1)
+       ;;
+
+      dgux*)
+        case $cc_basename in
+          ec++*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          ghcx*)
+           # Green Hills C++ Compiler
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      freebsd[[12]]*)
+        # C++ shared libraries reported to be fairly broken before
+       # switch to ELF
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      freebsd-elf*)
+        _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+        ;;
+
+      freebsd* | dragonfly*)
+        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+        # conventions
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+        ;;
+
+      gnu*)
+        ;;
+
+      hpux9*)
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+                                            # but as the default
+                                            # location of the library.
+
+        case $cc_basename in
+          CC*)
+            # FIXME: insert proper C++ library support
+            _LT_TAGVAR(ld_shlibs, $1)=no
+            ;;
+          aCC*)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            # Commands to make compiler produce verbose output that lists
+            # what "hidden" libraries, object files and flags are used when
+            # linking a shared library.
+            #
+            # There doesn't appear to be a way to prevent this compiler from
+            # explicitly linking system object files so we need to strip them
+            # from the output so that they don't get included in the library
+            # dependencies.
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+            ;;
+          *)
+            if test "$GXX" = yes; then
+              _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            else
+              # FIXME: insert proper C++ library support
+              _LT_TAGVAR(ld_shlibs, $1)=no
+            fi
+            ;;
+        esac
+        ;;
+
+      hpux10*|hpux11*)
+        if test $with_gnu_ld = no; then
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+         _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+          case $host_cpu in
+            hppa*64*|ia64*)
+              ;;
+            *)
+             _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+              ;;
+          esac
+        fi
+        case $host_cpu in
+          hppa*64*|ia64*)
+            _LT_TAGVAR(hardcode_direct, $1)=no
+            _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+            ;;
+          *)
+            _LT_TAGVAR(hardcode_direct, $1)=yes
+            _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+                                                # but as the default
+                                                # location of the library.
+            ;;
+        esac
+
+        case $cc_basename in
+          CC*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          aCC*)
+           case $host_cpu in
+             hppa*64*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+               ;;
+             ia64*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+               ;;
+             *)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+               ;;
+           esac
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+           ;;
+          *)
+           if test "$GXX" = yes; then
+             if test $with_gnu_ld = no; then
+               case $host_cpu in
+                 hppa*64*)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+                   ;;
+                 ia64*)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+                   ;;
+                 *)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+                   ;;
+               esac
+             fi
+           else
+             # FIXME: insert proper C++ library support
+             _LT_TAGVAR(ld_shlibs, $1)=no
+           fi
+           ;;
+        esac
+        ;;
+
+      interix[[3-9]]*)
+       _LT_TAGVAR(hardcode_direct, $1)=no
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+       # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+       # Instead, shared libraries are loaded at an image base (0x10000000 by
+       # default) and relocated if they conflict, which is a slow very memory
+       # consuming and fragmenting process.  To avoid this, we pick a random,
+       # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+       # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+       ;;
+      irix5* | irix6*)
+        case $cc_basename in
+          CC*)
+           # SGI C++
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+
+           # Archives containing C++ object files must be created using
+           # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+           # necessary to make sure instantiated templates are included
+           # in the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+           ;;
+          *)
+           if test "$GXX" = yes; then
+             if test "$with_gnu_ld" = no; then
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+             else
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib'
+             fi
+           fi
+           _LT_TAGVAR(link_all_deplibs, $1)=yes
+           ;;
+        esac
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(inherit_rpath, $1)=yes
+        ;;
+
+      linux* | k*bsd*-gnu)
+        case $cc_basename in
+          KCC*)
+           # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+           # KCC will only create a shared library if the output file
+           # ends with ".so" (or ".sl" for HP-UX), so rename the library
+           # to its proper name (with version) after linking.
+           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+           # Archives containing C++ object files must be created using
+           # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+           ;;
+         icpc* | ecpc* )
+           # Intel C++
+           with_gnu_ld=yes
+           # version 8.0 and above of icpc choke on multiply defined symbols
+           # if we add $predep_objects and $postdep_objects, however 7.1 and
+           # earlier do not add the objects themselves.
+           case `$CC -V 2>&1` in
+             *"Version 7."*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+               ;;
+             *)  # Version 8.0 or newer
+               tmp_idyn=
+               case $host_cpu in
+                 ia64*) tmp_idyn=' -i_dynamic';;
+               esac
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+               ;;
+           esac
+           _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+           ;;
+          pgCC* | pgcpp*)
+            # Portland Group C++ compiler
+           case `$CC -V` in
+           *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*)
+             _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+               compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
+             _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+               $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
+               $RANLIB $oldlib'
+             _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+               $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+               $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+             ;;
+           *) # Version 6 will use weak symbols
+             _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+             ;;
+           esac
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+            ;;
+         cxx*)
+           # Compaq C++
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+           runpath_var=LD_RUN_PATH
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+           ;;
+         xl*)
+           # IBM XL 8.0 on PPC, with GNU ld
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           if test "x$supports_anon_versioning" = xyes; then
+             _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+               cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+               echo "local: *; };" >> $output_objdir/$libname.ver~
+               $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+           fi
+           ;;
+         *)
+           case `$CC -V 2>&1 | sed 5q` in
+           *Sun\ C*)
+             # Sun C++ 5.9
+             _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+             _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+             _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+             _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+             # Not sure whether something based on
+             # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+             # would be better.
+             output_verbose_link_cmd='echo'
+
+             # Archives containing C++ object files must be created using
+             # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+             # necessary to make sure instantiated templates are included
+             # in the archive.
+             _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+             ;;
+           esac
+           ;;
+       esac
+       ;;
+
+      lynxos*)
+        # FIXME: insert proper C++ library support
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      m88k*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      mvs*)
+        case $cc_basename in
+          cxx*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+         *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+       esac
+       ;;
+
+      netbsd*)
+        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+         _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+         wlarc=
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       fi
+       # Workaround some broken pre-1.5 toolchains
+       output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+       ;;
+
+      *nto* | *qnx*)
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+       ;;
+
+      openbsd2*)
+        # C++ shared libraries are fairly broken
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      openbsd*)
+       if test -f /usr/libexec/ld.so; then
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+         if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+           _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+         fi
+         output_verbose_link_cmd=echo
+       else
+         _LT_TAGVAR(ld_shlibs, $1)=no
+       fi
+       ;;
+
+      osf3* | osf4* | osf5*)
+        case $cc_basename in
+          KCC*)
+           # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+           # KCC will only create a shared library if the output file
+           # ends with ".so" (or ".sl" for HP-UX), so rename the library
+           # to its proper name (with version) after linking.
+           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Archives containing C++ object files must be created using
+           # the KAI C++ compiler.
+           case $host in
+             osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+             *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+           esac
+           ;;
+          RCC*)
+           # Rational C++ 2.4.1
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          cxx*)
+           case $host in
+             osf3*)
+               _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+               ;;
+             *)
+               _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+                 echo "-hidden">> $lib.exp~
+                 $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp  `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~
+                 $RM $lib.exp'
+               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+               ;;
+           esac
+
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+           ;;
+         *)
+           if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+             _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+             case $host in
+               osf3*)
+                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+                 ;;
+               *)
+                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+                 ;;
+             esac
+
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+             _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+             # Commands to make compiler produce verbose output that lists
+             # what "hidden" libraries, object files and flags are used when
+             # linking a shared library.
+             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+           else
+             # FIXME: insert proper C++ library support
+             _LT_TAGVAR(ld_shlibs, $1)=no
+           fi
+           ;;
+        esac
+        ;;
+
+      psos*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      sunos4*)
+        case $cc_basename in
+          CC*)
+           # Sun C++ 4.x
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          lcc*)
+           # Lucid
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      solaris*)
+        case $cc_basename in
+          CC*)
+           # Sun C++ 4.2, 5.x and Centerline C++
+            _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+           _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag}  -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+             $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+           _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+           case $host_os in
+             solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+             *)
+               # The compiler driver will combine and reorder linker options,
+               # but understands `-z linker_flag'.
+               # Supported since Solaris 2.6 (maybe 2.5.1?)
+               _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+               ;;
+           esac
+           _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+           output_verbose_link_cmd='echo'
+
+           # Archives containing C++ object files must be created using
+           # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+           # necessary to make sure instantiated templates are included
+           # in the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+           ;;
+          gcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+           # The C++ compiler must be used to create the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+           ;;
+          *)
+           # GNU C++ compiler with Solaris linker
+           if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+             _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+             if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+                 $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+               # Commands to make compiler produce verbose output that lists
+               # what "hidden" libraries, object files and flags are used when
+               # linking a shared library.
+               output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+             else
+               # g++ 2.7 appears to require `-G' NOT `-shared' on this
+               # platform.
+               _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+                 $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+               # Commands to make compiler produce verbose output that lists
+               # what "hidden" libraries, object files and flags are used when
+               # linking a shared library.
+               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+             fi
+
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+             case $host_os in
+               solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+               *)
+                 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+                 ;;
+             esac
+           fi
+           ;;
+        esac
+        ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      case $cc_basename in
+        CC*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+      esac
+      ;;
+
+      sysv5* | sco3.2v5* | sco5v6*)
+       # Note: We can NOT use -z defs as we might desire, because we do not
+       # link with -lc, and that would cause any symbols used from libc to
+       # always be unresolved, which means just about no library would
+       # ever link correctly.  If we're not using GNU ld we use -z text
+       # though, which does catch some bad symbols but isn't as heavy-handed
+       # as -z defs.
+       _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+       _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+       _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+       _LT_TAGVAR(link_all_deplibs, $1)=yes
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+       runpath_var='LD_RUN_PATH'
+
+       case $cc_basename in
+          CC*)
+           _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           ;;
+         *)
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           ;;
+       esac
+      ;;
+
+      tandem*)
+        case $cc_basename in
+          NCC*)
+           # NonStop-UX NCC 3.20
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      vxworks*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      *)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+    esac
+
+    AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+    test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+    _LT_TAGVAR(GCC, $1)="$GXX"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  CC=$lt_save_CC
+  LDCXX=$LD
+  LD=$lt_save_LD
+  GCC=$lt_save_GCC
+  with_gnu_ld=$lt_save_with_gnu_ld
+  lt_cv_path_LDCXX=$lt_cv_path_LD
+  lt_cv_path_LD=$lt_save_path_LD
+  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library.  It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer*4 a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+  private int a;
+  public void bar (void) {
+    a = 0;
+  }
+};
+_LT_EOF
+])
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  for p in `eval "$output_verbose_link_cmd"`; do
+    case $p in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test $p = "-L" ||
+          test $p = "-R"; then
+        prev=$p
+        continue
+       else
+        prev=
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+        case $p in
+        -L* | -R*)
+          # Internal compiler library paths should come after those
+          # provided the user.  The postdeps already come after the
+          # user supplied libs so there is no need to process them.
+          if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+            _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+          else
+            _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+          fi
+          ;;
+        # The "-l" case would never come before the object being
+        # linked, so don't bother handling this case.
+        esac
+       else
+        if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+          _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+        else
+          _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+        fi
+       fi
+       ;;
+
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+        pre_test_object_deps_done=yes
+        continue
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+        if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+          _LT_TAGVAR(predep_objects, $1)="$p"
+        else
+          _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+        fi
+       else
+        if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+          _LT_TAGVAR(postdep_objects, $1)="$p"
+        else
+          _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+        fi
+       fi
+       ;;
+
+    *) ;; # Ignore the rest.
+
+    esac
+  done
+
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+  # Interix 3.5 installs completely hosed .la files for C++, so rather than
+  # hack all around it, let's just trust "g++" to DTRT.
+  _LT_TAGVAR(predep_objects,$1)=
+  _LT_TAGVAR(postdep_objects,$1)=
+  _LT_TAGVAR(postdeps,$1)=
+  ;;
+
+linux*)
+  case `$CC -V 2>&1 | sed 5q` in
+  *Sun\ C*)
+    # Sun C++ 5.9
+
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+
+solaris*)
+  case $cc_basename in
+  CC*)
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    # Adding this requires a known-good setup of shared libraries for
+    # Sun compiler versions before 5.6, else PIC objects from an old
+    # archive will be linked into the output, leading to subtle bugs.
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+    [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+    [Dependencies to place before and after the objects being linked to
+    create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+    [The library search path used internally by the compiler when linking
+    a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_PROG_F77
+# ------------
+# Since AC_PROG_F77 is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_F77],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes])
+AC_PROG_F77
+if test -z "$F77" || test "X$F77" = "Xno"; then
+  _lt_disable_F77=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_F77
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_F77], [])
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_REQUIRE([_LT_PROG_F77])dnl
+AC_LANG_PUSH(Fortran 77)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_F77" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  CC=${F77-"f77"}
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+  GCC=$G77
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+       if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+         test "$enable_shared" = yes && enable_static=no
+       fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$G77"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC="$lt_save_CC"
+fi # test "$_lt_disable_F77" != yes
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_PROG_FC
+# -----------
+# Since AC_PROG_FC is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_FC],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes])
+AC_PROG_FC
+if test -z "$FC" || test "X$FC" = "Xno"; then
+  _lt_disable_FC=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_FC
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_FC], [])
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_REQUIRE([_LT_PROG_FC])dnl
+AC_LANG_PUSH(Fortran)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_FC" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  CC=${FC-"f95"}
+  compiler=$CC
+  GCC=$ac_cv_fc_compiler_gnu
+
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+       if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+         test "$enable_shared" = yes && enable_static=no
+       fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC="$lt_save_CC"
+fi # test "$_lt_disable_FC" != yes
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC="$lt_save_CC"
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+  :
+  _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC="$lt_save_CC"
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+  [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+    [AC_CHECK_TOOL(GCJ, gcj,)
+      test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+      AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible.  Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+    [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for lt_ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+  test ! -f $lt_ac_sed && continue
+  cat /dev/null > conftest.in
+  lt_ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+  # Check for GNU sed and select it if it is found.
+  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+    lt_cv_path_SED=$lt_ac_sed
+    break
+  fi
+  while true; do
+    cat conftest.in conftest.in >conftest.tmp
+    mv conftest.tmp conftest.in
+    cp conftest.in conftest.nl
+    echo >>conftest.nl
+    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+    cmp -s conftest.out conftest.nl || break
+    # 10000 chars as input seems more than enough
+    test $lt_ac_count -gt 10 && break
+    lt_ac_count=`expr $lt_ac_count + 1`
+    if test $lt_ac_count -gt $lt_ac_max; then
+      lt_ac_max=$lt_ac_count
+      lt_cv_path_SED=$lt_ac_sed
+    fi
+  done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+AC_MSG_RESULT([$xsi_shell])
+_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
+
+AC_MSG_CHECKING([whether the shell understands "+="])
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+AC_MSG_RESULT([$lt_shell_append])
+_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PROG_XSI_SHELLFNS
+# ---------------------
+# Bourne and XSI compatible variants of some useful shell functions.
+m4_defun([_LT_PROG_XSI_SHELLFNS],
+[case $xsi_shell in
+  yes)
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+  case ${1} in
+    */*) func_dirname_result="${1%/*}${2}" ;;
+    *  ) func_dirname_result="${3}" ;;
+  esac
+}
+
+# func_basename file
+func_basename ()
+{
+  func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+#   dirname:  Compute the dirname of FILE.  If nonempty,
+#             add APPEND to the result, otherwise set result
+#             to NONDIR_REPLACEMENT.
+#             value returned in "$func_dirname_result"
+#   basename: Compute filename of FILE.
+#             value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+  case ${1} in
+    */*) func_dirname_result="${1%/*}${2}" ;;
+    *  ) func_dirname_result="${3}" ;;
+  esac
+  func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+  # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+  # positional parameters, so assign one to ordinary parameter first.
+  func_stripname_result=${3}
+  func_stripname_result=${func_stripname_result#"${1}"}
+  func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+  func_opt_split_opt=${1%%=*}
+  func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+  case ${1} in
+    *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+    *)    func_lo2o_result=${1} ;;
+  esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+  func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+  func_arith_result=$(( $[*] ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+  func_len_result=${#1}
+}
+
+_LT_EOF
+    ;;
+  *) # Bourne compatible functions.
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+  # Extract subdirectory from the argument.
+  func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+  if test "X$func_dirname_result" = "X${1}"; then
+    func_dirname_result="${3}"
+  else
+    func_dirname_result="$func_dirname_result${2}"
+  fi
+}
+
+# func_basename file
+func_basename ()
+{
+  func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+dnl func_dirname_and_basename
+dnl A portable version of this function is already defined in general.m4sh
+dnl so there is no need for it here.
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+  case ${2} in
+    .*) func_stripname_result=`$ECHO "X${3}" \
+           | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+    *)  func_stripname_result=`$ECHO "X${3}" \
+           | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+  esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[[^=]]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+  func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+  func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+  func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+  func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+  func_arith_result=`expr "$[@]"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+  func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+  yes)
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+  eval "$[1]+=\$[2]"
+}
+_LT_EOF
+    ;;
+  *)
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+  eval "$[1]=\$$[1]\$[2]"
+}
+
+_LT_EOF
+    ;;
+  esac
+])
+
+# Helper functions for option handling.                    -*- Autoconf -*-
+#
+#   Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+#   Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it.  Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+        _LT_MANGLE_DEFUN([$1], [$2]),
+    [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+           [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+                     [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME.  If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+    [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+  dnl
+  dnl Simply set some default values (i.e off) if boolean options were not
+  dnl specified:
+  _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+  ])
+  _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+  ])
+  dnl
+  dnl If no reference was made to various pairs of opposing options, then
+  dnl we run the default mode handler for the pair.  For example, if neither
+  dnl `shared' nor `disable-shared' was passed, we enable building of shared
+  dnl archives by default:
+  _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+  _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+                  [_LT_ENABLE_FAST_INSTALL])
+  ])
+])# _LT_SET_OPTIONS
+
+
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+  ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS],      [0], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+    [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+       [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_shared=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+    _LT_DECL([build_libtool_libs], [enable_shared], [0],
+       [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+    [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+       [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_static=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+    _LT_DECL([build_old_libs], [enable_static], [0],
+       [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+    [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+    [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_fast_install=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+        [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'.  If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+    [AS_HELP_STRING([--with-pic],
+       [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+    [pic_mode="$withval"],
+    [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+                [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+                [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+                [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+                [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+                [m4_define([_LTDL_TYPE], [convenience])])
+
+# ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+       [$#], [2], [[$2]],
+       [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+       [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+       [$#], 1, [],
+       [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+          m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+       [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+            [m4_foreach([_Lt_suffix],
+               ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+       [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+         [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+                [lt_append([$1], [$2], [$3])$4],
+                [$5])],
+         [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+       m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+    m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+       [$5],
+    [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+  [lt_join(m4_quote(m4_default([$4], [[, ]])),
+           lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+                     [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
+
+# ltversion.m4 -- version numbers                      -*- Autoconf -*-
+#
+#   Copyright (C) 2004 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# Generated from ltversion.in.
+
+# serial 3017 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.2.6b])
+m4_define([LT_PACKAGE_REVISION], [1.3017])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.2.6b'
+macro_revision='1.3017'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*-
+#
+#   Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 4 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else.  This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. 
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION],  [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP],             [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],        [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT],         [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],    [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN],           [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR],             [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL],    [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN],           [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER],       [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK],               [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],        [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],    [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],    [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR],         [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR],            [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],  [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC],             [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU],            [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG],    [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD],   [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP],          [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED],            [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME],           [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE],  [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE],    [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL],          [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP],          [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN],                [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],     [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG],          [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL],   [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX],           [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77],           [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ],           [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_RC],             [AC_DEFUN([AC_LIBTOOL_RC])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],  [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG],      [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG],    [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG],    [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],    [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG],     [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG],         [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C],       [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+       AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+       _pkg_min_version=m4_default([$1], [0.9.0])
+       AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+       if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+               AC_MSG_RESULT([yes])
+       else
+               AC_MSG_RESULT([no])
+               PKG_CONFIG=""
+       fi
+               
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+                    [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1`
+        else 
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+       ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+               [AC_MSG_RESULT([no])
+                $4])
+elif test $pkg_failed = untried; then
+       ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
+               [$4])
+else
+       $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+       $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+       ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version.  Point them to the right macro.
+m4_if([$1], [1.11.1], [],
+      [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too.  Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.11.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL                                            -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 10
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
+       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
+       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+       [$1], UPC,  [depcc="$UPC"  am_compiler_list=],
+       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                   [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  am__universal=false
+  m4_case([$1], [CC],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac],
+    [CXX],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac])
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    msvisualcpp | msvcmsys)
+      # This compiler won't grok `-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+
+# Generate code to set up dependency tracking.              -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 5
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+  # Autoconf 2.62 quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named `Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`AS_DIRNAME("$mf")`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running `make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # When using ansi2knr, U may be empty or an underscore; expand it
+    U=`sed -n 's/^U = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+        sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`AS_DIRNAME(["$file"])`
+      AS_MKDIR_P([$dirpart/$fdir])
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 8
+
+# AM_CONFIG_HEADER is obsolete.  It has been replaced by AC_CONFIG_HEADERS.
+AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
+
+# Do all the work for Automake.                             -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 16
+
+# This macro actually does too much.  Some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+  [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+             [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+                            [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+                 [_AM_DEPENDENCIES(CC)],
+                 [define([AC_PROG_CC],
+                         defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+                 [_AM_DEPENDENCIES(CXX)],
+                 [define([AC_PROG_CXX],
+                         defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+                 [_AM_DEPENDENCIES(OBJC)],
+                 [define([AC_PROG_OBJC],
+                         defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+  [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+
+dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005, 2008  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Add --enable-maintainer-mode option to configure.         -*- Autoconf -*-
+# From Jim Meyering
+
+# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_MAINTAINER_MODE([DEFAULT-MODE])
+# ----------------------------------
+# Control maintainer-specific portions of Makefiles.
+# Default is to disable them, unless `enable' is passed literally.
+# For symmetry, `disable' may be passed as well.  Anyway, the user
+# can override the default with the --enable/--disable switch.
+AC_DEFUN([AM_MAINTAINER_MODE],
+[m4_case(m4_default([$1], [disable]),
+       [enable], [m4_define([am_maintainer_other], [disable])],
+       [disable], [m4_define([am_maintainer_other], [enable])],
+       [m4_define([am_maintainer_other], [enable])
+        m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to am_maintainer_other maintainer-specific portions of Makefiles])
+  dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+  AC_ARG_ENABLE([maintainer-mode],
+[  --][am_maintainer_other][-maintainer-mode  am_maintainer_other make rules and dependencies not useful
+                         (and sometimes confusing) to the casual installer],
+      [USE_MAINTAINER_MODE=$enableval],
+      [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST([MAINT])dnl
+]
+)
+
+AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+
+# Check to see how 'make' treats includes.                 -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2009  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+       @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_PROG_CC_C_O
+# --------------
+# Like AC_PROG_CC_C_O, but changed for automake.
+AC_DEFUN([AM_PROG_CC_C_O],
+[AC_REQUIRE([AC_PROG_CC_C_O])dnl
+AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+dnl Make sure AC_PROG_CC is never called again, or it will override our
+dnl setting of CC.
+m4_define([AC_PROG_CC],
+          [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])])
+])
+
+# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p.  We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+  [[\\/$]]* | ?:[[\\/]]*) ;;
+  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling.                     -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2008  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[[\\\"\#\$\&\'\`$am_lf]]*)
+    AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+  *[[\\\"\#\$\&\'\`$am_lf\ \   ]]*)
+    AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+   if test "$[*]" = "X"; then
+      # -L didn't work.
+      set X `ls -t "$srcdir/configure" conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$[*]" != "X $srcdir/configure conftest.file" \
+      && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2009  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 1
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# (`yes' being less verbose, `no' or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules],
+[  --enable-silent-rules          less verbose build output (undo: `make V=1')
+  --disable-silent-rules         verbose build output (undo: `make V=0')])
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no)  AM_DEFAULT_VERBOSITY=1;;
+*)   AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001, 2003, 2005  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006, 2008  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+     [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+     [m4_case([$1], [ustar],, [pax],,
+              [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+  case $_am_tool in
+  gnutar)
+    for _am_tar in tar gnutar gtar;
+    do
+      AM_RUN_LOG([$_am_tar --version]) && break
+    done
+    am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+    am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+    am__untar="$_am_tar -xf -"
+    ;;
+  plaintar)
+    # Must skip GNU tar: if it does not support --format= it doesn't create
+    # ustar tarball either.
+    (tar --version) >/dev/null 2>&1 && continue
+    am__tar='tar chf - "$$tardir"'
+    am__tar_='tar chf - "$tardir"'
+    am__untar='tar xf -'
+    ;;
+  pax)
+    am__tar='pax -L -x $1 -w "$$tardir"'
+    am__tar_='pax -L -x $1 -w "$tardir"'
+    am__untar='pax -r'
+    ;;
+  cpio)
+    am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+    am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+    am__untar='cpio -i -H $1 -d'
+    ;;
+  none)
+    am__tar=false
+    am__tar_=false
+    am__untar=false
+    ;;
+  esac
+
+  # If the value was cached, stop now.  We just wanted to have am__tar
+  # and am__untar set.
+  test -n "${am_cv_prog_tar_$1}" && break
+
+  # tar/untar a dummy directory, and stop if the command works
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  echo GrepMe > conftest.dir/file
+  AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+  rm -rf conftest.dir
+  if test -s conftest.tar; then
+    AM_RUN_LOG([$am__untar <conftest.tar])
+    grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+  fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([acinclude.m4])
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644 (file)
index 0000000..9781ec4
--- /dev/null
@@ -0,0 +1,1447 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#ifndef BT_FLUSHABLE
+#define BT_FLUSHABLE   8
+#endif
+
+#define ERROR_FAILED(gerr, str, err) \
+               g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+                               str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+       bdaddr_t src;
+       bdaddr_t dst;
+       int defer;
+       int sec_level;
+       uint8_t channel;
+       uint16_t psm;
+       uint16_t cid;
+       uint16_t mtu;
+       uint16_t imtu;
+       uint16_t omtu;
+       int master;
+       uint8_t mode;
+       int flushable;
+       uint32_t priority;
+};
+
+struct connect {
+       BtIOConnect connect;
+       gpointer user_data;
+       GDestroyNotify destroy;
+};
+
+struct accept {
+       BtIOConnect connect;
+       gpointer user_data;
+       GDestroyNotify destroy;
+};
+
+struct server {
+       BtIOConnect connect;
+       BtIOConfirm confirm;
+       gpointer user_data;
+       GDestroyNotify destroy;
+};
+
+static void server_remove(struct server *server)
+{
+       if (server->destroy)
+               server->destroy(server->user_data);
+       g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+       if (conn->destroy)
+               conn->destroy(conn->user_data);
+       g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+       if (accept->destroy)
+               accept->destroy(accept->user_data);
+       g_free(accept);
+}
+
+static gboolean check_nval(GIOChannel *io)
+{
+       struct pollfd fds;
+
+       memset(&fds, 0, sizeof(fds));
+       fds.fd = g_io_channel_unix_get_fd(io);
+       fds.events = POLLNVAL;
+
+       if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct accept *accept = user_data;
+       GError *err = NULL;
+
+       /* If the user aborted this accept attempt */
+       if ((cond & G_IO_NVAL) || check_nval(io))
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+                               "HUP or ERR on socket");
+
+       accept->connect(io, err, accept->user_data);
+
+       g_clear_error(&err);
+
+       return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct connect *conn = user_data;
+       GError *gerr = NULL;
+
+       /* If the user aborted this connect attempt */
+       if ((cond & G_IO_NVAL) || check_nval(io))
+               return FALSE;
+
+       if (cond & G_IO_OUT) {
+               int err, sk_err = 0, sock = g_io_channel_unix_get_fd(io);
+               socklen_t len = sizeof(sk_err);
+
+               if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+                       err = -errno;
+               else
+                       err = -sk_err;
+
+               if (err < 0)
+                       g_set_error(&gerr, BT_IO_ERROR,
+                                       BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+                                       strerror(-err), -err);
+       } else if (cond & (G_IO_HUP | G_IO_ERR))
+               g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+                               "HUP or ERR on socket");
+
+       conn->connect(io, gerr, conn->user_data);
+
+       if (gerr)
+               g_error_free(gerr);
+
+       return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct server *server = user_data;
+       int srv_sock, cli_sock;
+       GIOChannel *cli_io;
+
+       /* If the user closed the server */
+       if ((cond & G_IO_NVAL) || check_nval(io))
+               return FALSE;
+
+       srv_sock = g_io_channel_unix_get_fd(io);
+
+       cli_sock = accept(srv_sock, NULL, NULL);
+       if (cli_sock < 0)
+               return TRUE;
+
+       cli_io = g_io_channel_unix_new(cli_sock);
+
+       g_io_channel_set_close_on_unref(cli_io, TRUE);
+       g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+       if (server->confirm)
+               server->confirm(cli_io, server->user_data);
+       else
+               server->connect(cli_io, NULL, server->user_data);
+
+       g_io_channel_unref(cli_io);
+
+       return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+                               BtIOConfirm confirm, gpointer user_data,
+                               GDestroyNotify destroy)
+{
+       struct server *server;
+       GIOCondition cond;
+
+       server = g_new0(struct server, 1);
+       server->connect = connect;
+       server->confirm = confirm;
+       server->user_data = user_data;
+       server->destroy = destroy;
+
+       cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+                                       (GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+                               gpointer user_data, GDestroyNotify destroy)
+{
+       struct connect *conn;
+       GIOCondition cond;
+
+       conn = g_new0(struct connect, 1);
+       conn->connect = connect;
+       conn->user_data = user_data;
+       conn->destroy = destroy;
+
+       cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+                                       (GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+                                                       GDestroyNotify destroy)
+{
+       struct accept *accept;
+       GIOCondition cond;
+
+       accept = g_new0(struct accept, 1);
+       accept->connect = connect;
+       accept->user_data = user_data;
+       accept->destroy = destroy;
+
+       cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+                                       (GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm,
+                                               uint16_t cid, GError **err)
+{
+       struct sockaddr_l2 addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+
+       if (cid)
+               addr.l2_cid = htobs(cid);
+       else
+               addr.l2_psm = htobs(psm);
+
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               int error = -errno;
+               ERROR_FAILED(err, "l2cap_bind", errno);
+               return error;
+       }
+
+       return 0;
+}
+
+static int l2cap_connect(int sock, const bdaddr_t *dst,
+                                       uint16_t psm, uint16_t cid)
+{
+       int err;
+       struct sockaddr_l2 addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, dst);
+       if (cid)
+               addr.l2_cid = htobs(cid);
+       else
+               addr.l2_psm = htobs(psm);
+
+       err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+               return -errno;
+
+       return 0;
+}
+
+static int l2cap_set_master(int sock, int master)
+{
+       int flags;
+       socklen_t len;
+
+       len = sizeof(flags);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+               return -errno;
+
+       if (master) {
+               if (flags & L2CAP_LM_MASTER)
+                       return 0;
+               flags |= L2CAP_LM_MASTER;
+       } else {
+               if (!(flags & L2CAP_LM_MASTER))
+                       return 0;
+               flags &= ~L2CAP_LM_MASTER;
+       }
+
+       if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+       int flags;
+       socklen_t len;
+
+       len = sizeof(flags);
+       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+               return -errno;
+
+       if (master) {
+               if (flags & RFCOMM_LM_MASTER)
+                       return 0;
+               flags |= RFCOMM_LM_MASTER;
+       } else {
+               if (!(flags & RFCOMM_LM_MASTER))
+                       return 0;
+               flags &= ~RFCOMM_LM_MASTER;
+       }
+
+       if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+       int lm_map[] = {
+               0,
+               L2CAP_LM_AUTH,
+               L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+               L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+       }, opt = lm_map[level];
+
+       if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+       int lm_map[] = {
+               0,
+               RFCOMM_LM_AUTH,
+               RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+               RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+       }, opt = lm_map[level];
+
+       if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+       struct bt_security sec;
+       int ret;
+
+       if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+               g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                               "Valid security level range is %d-%d",
+                               BT_SECURITY_LOW, BT_SECURITY_HIGH);
+               return FALSE;
+       }
+
+       memset(&sec, 0, sizeof(sec));
+       sec.level = level;
+
+       if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+                                                       sizeof(sec)) == 0)
+               return TRUE;
+
+       if (errno != ENOPROTOOPT) {
+               ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+               return FALSE;
+       }
+
+       if (type == BT_IO_L2CAP)
+               ret = l2cap_set_lm(sock, level);
+       else
+               ret = rfcomm_set_lm(sock, level);
+
+       if (ret < 0) {
+               ERROR_FAILED(err, "setsockopt(LM)", -ret);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+       int opt;
+       socklen_t len;
+
+       len = sizeof(opt);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+               return -errno;
+
+       *sec_level = 0;
+
+       if (opt & L2CAP_LM_AUTH)
+               *sec_level = BT_SECURITY_LOW;
+       if (opt & L2CAP_LM_ENCRYPT)
+               *sec_level = BT_SECURITY_MEDIUM;
+       if (opt & L2CAP_LM_SECURE)
+               *sec_level = BT_SECURITY_HIGH;
+
+       return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+       int opt;
+       socklen_t len;
+
+       len = sizeof(opt);
+       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+               return -errno;
+
+       *sec_level = 0;
+
+       if (opt & RFCOMM_LM_AUTH)
+               *sec_level = BT_SECURITY_LOW;
+       if (opt & RFCOMM_LM_ENCRYPT)
+               *sec_level = BT_SECURITY_MEDIUM;
+       if (opt & RFCOMM_LM_SECURE)
+               *sec_level = BT_SECURITY_HIGH;
+
+       return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+                                                               GError **err)
+{
+       struct bt_security sec;
+       socklen_t len;
+       int ret;
+
+       memset(&sec, 0, sizeof(sec));
+       len = sizeof(sec);
+       if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+               *level = sec.level;
+               return TRUE;
+       }
+
+       if (errno != ENOPROTOOPT) {
+               ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+               return FALSE;
+       }
+
+       if (type == BT_IO_L2CAP)
+               ret = l2cap_get_lm(sock, level);
+       else
+               ret = rfcomm_get_lm(sock, level);
+
+       if (ret < 0) {
+               ERROR_FAILED(err, "getsockopt(LM)", -ret);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int l2cap_set_flushable(int sock, gboolean flushable)
+{
+       int f;
+
+       f = flushable;
+       if (setsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, sizeof(f)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int set_priority(int sock, uint32_t prio)
+{
+       if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static gboolean get_key_size(int sock, int *size, GError **err)
+{
+       struct bt_security sec;
+       socklen_t len;
+
+       memset(&sec, 0, sizeof(sec));
+       len = sizeof(sec);
+       if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+               *size = sec.key_size;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu,
+                               uint16_t omtu, uint8_t mode, int master,
+                               int flushable, uint32_t priority, GError **err)
+{
+       if (imtu || omtu || mode) {
+               struct l2cap_options l2o;
+               socklen_t len;
+
+               memset(&l2o, 0, sizeof(l2o));
+               len = sizeof(l2o);
+               if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+                                                               &len) < 0) {
+                       ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+                       return FALSE;
+               }
+
+               if (imtu)
+                       l2o.imtu = imtu;
+               if (omtu)
+                       l2o.omtu = omtu;
+               if (mode)
+                       l2o.mode = mode;
+
+               if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+                                                       sizeof(l2o)) < 0) {
+                       ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+                       return FALSE;
+               }
+       }
+
+       if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+               ERROR_FAILED(err, "l2cap_set_master", errno);
+               return FALSE;
+       }
+
+       if (flushable >= 0 && l2cap_set_flushable(sock, flushable) < 0) {
+               ERROR_FAILED(err, "l2cap_set_flushable", errno);
+               return FALSE;
+       }
+
+       if (priority > 0 && set_priority(sock, priority) < 0) {
+               ERROR_FAILED(err, "set_priority", errno);
+               return FALSE;
+       }
+
+       if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+               return FALSE;
+
+       return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+               const bdaddr_t *src, uint8_t channel, GError **err)
+{
+       struct sockaddr_rc addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, src);
+       addr.rc_channel = channel;
+
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               int error = -errno;
+               ERROR_FAILED(err, "rfcomm_bind", errno);
+               return error;
+       }
+
+       return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+       int err;
+       struct sockaddr_rc addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, dst);
+       addr.rc_channel = channel;
+
+       err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+               return -errno;
+
+       return 0;
+}
+
+static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+       if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+               return FALSE;
+
+       if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+               ERROR_FAILED(err, "rfcomm_set_master", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+       struct sockaddr_sco addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, src);
+
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               int error = -errno;
+               ERROR_FAILED(err, "sco_bind", errno);
+               return error;
+       }
+
+       return 0;
+}
+
+static int sco_connect(int sock, const bdaddr_t *dst)
+{
+       struct sockaddr_sco addr;
+       int err;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, dst);
+
+       err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+               return -errno;
+
+       return 0;
+}
+
+static gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+       struct sco_options sco_opt;
+       socklen_t len;
+
+       if (!mtu)
+               return TRUE;
+
+       len = sizeof(sco_opt);
+       memset(&sco_opt, 0, len);
+       if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+               ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       sco_opt.mtu = mtu;
+       if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+                                               sizeof(sco_opt)) < 0) {
+               ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+                                               BtIOOption opt1, va_list args)
+{
+       BtIOOption opt = opt1;
+       const char *str;
+
+       memset(opts, 0, sizeof(*opts));
+
+       /* Set defaults */
+       opts->defer = DEFAULT_DEFER_TIMEOUT;
+       opts->master = -1;
+       opts->mode = L2CAP_MODE_BASIC;
+       opts->flushable = -1;
+       opts->priority = 0;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       str = va_arg(args, const char *);
+                       str2ba(str, &opts->src);
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(&opts->src, va_arg(args, const bdaddr_t *));
+                       break;
+               case BT_IO_OPT_DEST:
+                       str2ba(va_arg(args, const char *), &opts->dst);
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
+                       break;
+               case BT_IO_OPT_DEFER_TIMEOUT:
+                       opts->defer = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_SEC_LEVEL:
+                       opts->sec_level = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_CHANNEL:
+                       opts->channel = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_PSM:
+                       opts->psm = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_CID:
+                       opts->cid = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_MTU:
+                       opts->mtu = va_arg(args, int);
+                       opts->imtu = opts->mtu;
+                       opts->omtu = opts->mtu;
+                       break;
+               case BT_IO_OPT_OMTU:
+                       opts->omtu = va_arg(args, int);
+                       if (!opts->mtu)
+                               opts->mtu = opts->omtu;
+                       break;
+               case BT_IO_OPT_IMTU:
+                       opts->imtu = va_arg(args, int);
+                       if (!opts->mtu)
+                               opts->mtu = opts->imtu;
+                       break;
+               case BT_IO_OPT_MASTER:
+                       opts->master = va_arg(args, gboolean);
+                       break;
+               case BT_IO_OPT_MODE:
+                       opts->mode = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_FLUSHABLE:
+                       opts->flushable = va_arg(args, gboolean);
+                       break;
+               case BT_IO_OPT_PRIORITY:
+                       opts->priority = va_arg(args, int);
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+                               socklen_t len, GError **err)
+{
+       socklen_t olen;
+
+       memset(src, 0, len);
+       olen = len;
+       if (getsockname(sock, src, &olen) < 0) {
+               ERROR_FAILED(err, "getsockname", errno);
+               return FALSE;
+       }
+
+       memset(dst, 0, len);
+       olen = len;
+       if (getpeername(sock, dst, &olen) < 0) {
+               ERROR_FAILED(err, "getpeername", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+       struct l2cap_conninfo info;
+       socklen_t len;
+
+       len = sizeof(info);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+               return -errno;
+
+       if (handle)
+               *handle = info.hci_handle;
+
+       if (dev_class)
+               memcpy(dev_class, info.dev_class, 3);
+
+       return 0;
+}
+
+static int l2cap_get_flushable(int sock, gboolean *flushable)
+{
+       int f;
+       socklen_t len;
+
+       f = 0;
+       len = sizeof(f);
+       if (getsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, &len) < 0)
+               return -errno;
+
+       if (f)
+               *flushable = TRUE;
+       else
+               *flushable = FALSE;
+
+       return 0;
+}
+
+static int get_priority(int sock, uint32_t *prio)
+{
+       socklen_t len;
+
+       len = sizeof(*prio);
+       if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, prio, &len) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+                                                               va_list args)
+{
+       BtIOOption opt = opt1;
+       struct sockaddr_l2 src, dst;
+       struct l2cap_options l2o;
+       int flags;
+       uint8_t dev_class[3];
+       uint16_t handle;
+       socklen_t len;
+       gboolean flushable = FALSE;
+       uint32_t priority;
+
+       len = sizeof(l2o);
+       memset(&l2o, 0, len);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+               ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       if (!get_peers(sock, (struct sockaddr *) &src,
+                               (struct sockaddr *) &dst, sizeof(src), err))
+               return FALSE;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       ba2str(&src.l2_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+                       break;
+               case BT_IO_OPT_DEST:
+                       ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+                       break;
+               case BT_IO_OPT_DEFER_TIMEOUT:
+                       len = sizeof(int);
+                       if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+                                       va_arg(args, int *), &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       break;
+               case BT_IO_OPT_SEC_LEVEL:
+                       if (!get_sec_level(sock, BT_IO_L2CAP,
+                                               va_arg(args, int *), err))
+                               return FALSE;
+                       break;
+               case BT_IO_OPT_KEY_SIZE:
+                       if (!get_key_size(sock, va_arg(args, int *), err))
+                               return FALSE;
+                       break;
+               case BT_IO_OPT_PSM:
+                       *(va_arg(args, uint16_t *)) = src.l2_psm ?
+                                       btohs(src.l2_psm) : btohs(dst.l2_psm);
+                       break;
+               case BT_IO_OPT_CID:
+                       *(va_arg(args, uint16_t *)) = src.l2_cid ?
+                                       btohs(src.l2_cid) : btohs(dst.l2_cid);
+                       break;
+               case BT_IO_OPT_OMTU:
+                       *(va_arg(args, uint16_t *)) = l2o.omtu;
+                       break;
+               case BT_IO_OPT_IMTU:
+                       *(va_arg(args, uint16_t *)) = l2o.imtu;
+                       break;
+               case BT_IO_OPT_MASTER:
+                       len = sizeof(flags);
+                       if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+                                                               &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, gboolean *)) =
+                               (flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+                       break;
+               case BT_IO_OPT_HANDLE:
+                       if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint16_t *)) = handle;
+                       break;
+               case BT_IO_OPT_CLASS:
+                       if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       memcpy(va_arg(args, uint8_t *), dev_class, 3);
+                       break;
+               case BT_IO_OPT_MODE:
+                       *(va_arg(args, uint8_t *)) = l2o.mode;
+                       break;
+               case BT_IO_OPT_FLUSHABLE:
+                       if (l2cap_get_flushable(sock, &flushable) < 0) {
+                               ERROR_FAILED(err, "get_flushable", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, gboolean *)) = flushable;
+                       break;
+               case BT_IO_OPT_PRIORITY:
+                       if (get_priority(sock, &priority) < 0) {
+                               ERROR_FAILED(err, "get_priority", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint32_t *)) = priority;
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+       struct rfcomm_conninfo info;
+       socklen_t len;
+
+       len = sizeof(info);
+       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+               return -errno;
+
+       if (handle)
+               *handle = info.hci_handle;
+
+       if (dev_class)
+               memcpy(dev_class, info.dev_class, 3);
+
+       return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+                                                               va_list args)
+{
+       BtIOOption opt = opt1;
+       struct sockaddr_rc src, dst;
+       int flags;
+       socklen_t len;
+       uint8_t dev_class[3];
+       uint16_t handle;
+
+       if (!get_peers(sock, (struct sockaddr *) &src,
+                               (struct sockaddr *) &dst, sizeof(src), err))
+               return FALSE;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       ba2str(&src.rc_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+                       break;
+               case BT_IO_OPT_DEST:
+                       ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+                       break;
+               case BT_IO_OPT_DEFER_TIMEOUT:
+                       len = sizeof(int);
+                       if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+                                       va_arg(args, int *), &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       break;
+               case BT_IO_OPT_SEC_LEVEL:
+                       if (!get_sec_level(sock, BT_IO_RFCOMM,
+                                               va_arg(args, int *), err))
+                               return FALSE;
+                       break;
+               case BT_IO_OPT_CHANNEL:
+                       *(va_arg(args, uint8_t *)) = src.rc_channel ?
+                                       src.rc_channel : dst.rc_channel;
+                       break;
+               case BT_IO_OPT_SOURCE_CHANNEL:
+                       *(va_arg(args, uint8_t *)) = src.rc_channel;
+                       break;
+               case BT_IO_OPT_DEST_CHANNEL:
+                       *(va_arg(args, uint8_t *)) = dst.rc_channel;
+                       break;
+               case BT_IO_OPT_MASTER:
+                       len = sizeof(flags);
+                       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+                                                               &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, gboolean *)) =
+                               (flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+                       break;
+               case BT_IO_OPT_HANDLE:
+                       if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint16_t *)) = handle;
+                       break;
+               case BT_IO_OPT_CLASS:
+                       if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       memcpy(va_arg(args, uint8_t *), dev_class, 3);
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+       struct sco_conninfo info;
+       socklen_t len;
+
+       len = sizeof(info);
+       if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+               return -errno;
+
+       if (handle)
+               *handle = info.hci_handle;
+
+       if (dev_class)
+               memcpy(dev_class, info.dev_class, 3);
+
+       return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+       BtIOOption opt = opt1;
+       struct sockaddr_sco src, dst;
+       struct sco_options sco_opt;
+       socklen_t len;
+       uint8_t dev_class[3];
+       uint16_t handle;
+
+       len = sizeof(sco_opt);
+       memset(&sco_opt, 0, len);
+       if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+               ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       if (!get_peers(sock, (struct sockaddr *) &src,
+                               (struct sockaddr *) &dst, sizeof(src), err))
+               return FALSE;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       ba2str(&src.sco_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+                       break;
+               case BT_IO_OPT_DEST:
+                       ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+                       break;
+               case BT_IO_OPT_MTU:
+               case BT_IO_OPT_IMTU:
+               case BT_IO_OPT_OMTU:
+                       *(va_arg(args, uint16_t *)) = sco_opt.mtu;
+                       break;
+               case BT_IO_OPT_HANDLE:
+                       if (sco_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "SCO_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint16_t *)) = handle;
+                       break;
+               case BT_IO_OPT_CLASS:
+                       if (sco_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "SCO_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       memcpy(va_arg(args, uint8_t *), dev_class, 3);
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+                                               BtIOOption opt1, va_list args)
+{
+       int sock;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       switch (type) {
+       case BT_IO_L2RAW:
+       case BT_IO_L2CAP:
+       case BT_IO_L2ERTM:
+               return l2cap_get(sock, err, opt1, args);
+       case BT_IO_RFCOMM:
+               return rfcomm_get(sock, err, opt1, args);
+       case BT_IO_SCO:
+               return sco_get(sock, err, opt1, args);
+       }
+
+       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                       "Unknown BtIO type %d", type);
+       return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+                                       GDestroyNotify destroy, GError **err)
+{
+       int sock;
+       char c;
+       struct pollfd pfd;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = sock;
+       pfd.events = POLLOUT;
+
+       if (poll(&pfd, 1, 0) < 0) {
+               ERROR_FAILED(err, "poll", errno);
+               return FALSE;
+       }
+
+       if (!(pfd.revents & POLLOUT)) {
+               if (read(sock, &c, 1) < 0) {
+                       ERROR_FAILED(err, "read", errno);
+                       return FALSE;
+               }
+       }
+
+       accept_add(io, connect, user_data, destroy);
+
+       return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+                                                       BtIOOption opt1, ...)
+{
+       va_list args;
+       gboolean ret;
+       struct set_opts opts;
+       int sock;
+
+       va_start(args, opt1);
+       ret = parse_set_opts(&opts, err, opt1, args);
+       va_end(args);
+
+       if (!ret)
+               return ret;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       switch (type) {
+       case BT_IO_L2RAW:
+       case BT_IO_L2CAP:
+       case BT_IO_L2ERTM:
+               return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+                               opts.mode, opts.master, opts.flushable,
+                               opts.priority, err);
+       case BT_IO_RFCOMM:
+               return rfcomm_set(sock, opts.sec_level, opts.master, err);
+       case BT_IO_SCO:
+               return sco_set(sock, opts.mtu, err);
+       }
+
+       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                       "Unknown BtIO type %d", type);
+       return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+                                                       BtIOOption opt1, ...)
+{
+       va_list args;
+       gboolean ret;
+
+       va_start(args, opt1);
+       ret = get_valist(io, type, err, opt1, args);
+       va_end(args);
+
+       return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+                                       struct set_opts *opts, GError **err)
+{
+       int sock;
+       GIOChannel *io;
+
+       switch (type) {
+       case BT_IO_L2RAW:
+               sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+                       return NULL;
+               }
+               if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+                                                       opts->cid, err) < 0)
+                       goto failed;
+               if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, -1, 0, err))
+                       goto failed;
+               break;
+       case BT_IO_L2CAP:
+               sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+                       return NULL;
+               }
+               if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+                                                       opts->cid, err) < 0)
+                       goto failed;
+               if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+                               opts->mode, opts->master, opts->flushable,
+                               opts->priority, err))
+                       goto failed;
+               break;
+       case BT_IO_L2ERTM:
+               sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(STREAM, L2CAP)", errno);
+                       return NULL;
+               }
+               if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+                                                       opts->cid, err) < 0)
+                       goto failed;
+               if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+                               opts->mode, opts->master, opts->flushable,
+                               opts->priority, err))
+                       goto failed;
+               break;
+       case BT_IO_RFCOMM:
+               sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+                       return NULL;
+               }
+               if (rfcomm_bind(sock, &opts->src,
+                                       server ? opts->channel : 0, err) < 0)
+                       goto failed;
+               if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+                       goto failed;
+               break;
+       case BT_IO_SCO:
+               sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+                       return NULL;
+               }
+               if (sco_bind(sock, &opts->src, err) < 0)
+                       goto failed;
+               if (!sco_set(sock, opts->mtu, err))
+                       goto failed;
+               break;
+       default:
+               g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                               "Unknown BtIO type %d", type);
+               return NULL;
+       }
+
+       io = g_io_channel_unix_new(sock);
+
+       g_io_channel_set_close_on_unref(io, TRUE);
+       g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+       return io;
+
+failed:
+       close(sock);
+
+       return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+                               gpointer user_data, GDestroyNotify destroy,
+                               GError **gerr, BtIOOption opt1, ...)
+{
+       GIOChannel *io;
+       va_list args;
+       struct set_opts opts;
+       int err, sock;
+       gboolean ret;
+
+       va_start(args, opt1);
+       ret = parse_set_opts(&opts, gerr, opt1, args);
+       va_end(args);
+
+       if (ret == FALSE)
+               return NULL;
+
+       io = create_io(type, FALSE, &opts, gerr);
+       if (io == NULL)
+               return NULL;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       switch (type) {
+       case BT_IO_L2RAW:
+               err = l2cap_connect(sock, &opts.dst, 0, opts.cid);
+               break;
+       case BT_IO_L2CAP:
+       case BT_IO_L2ERTM:
+               err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);
+               break;
+       case BT_IO_RFCOMM:
+               err = rfcomm_connect(sock, &opts.dst, opts.channel);
+               break;
+       case BT_IO_SCO:
+               err = sco_connect(sock, &opts.dst);
+               break;
+       default:
+               g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                               "Unknown BtIO type %d", type);
+               return NULL;
+       }
+
+       if (err < 0) {
+               g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+                               "connect: %s (%d)", strerror(-err), -err);
+               g_io_channel_unref(io);
+               return NULL;
+       }
+
+       connect_add(io, connect, user_data, destroy);
+
+       return io;
+}
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+                               BtIOConfirm confirm, gpointer user_data,
+                               GDestroyNotify destroy, GError **err,
+                               BtIOOption opt1, ...)
+{
+       GIOChannel *io;
+       va_list args;
+       struct set_opts opts;
+       int sock;
+       gboolean ret;
+
+       if (type == BT_IO_L2RAW) {
+               g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                               "Server L2CAP RAW sockets not supported");
+               return NULL;
+       }
+
+       va_start(args, opt1);
+       ret = parse_set_opts(&opts, err, opt1, args);
+       va_end(args);
+
+       if (ret == FALSE)
+               return NULL;
+
+       io = create_io(type, TRUE, &opts, err);
+       if (io == NULL)
+               return NULL;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       if (confirm)
+               setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+                                                       sizeof(opts.defer));
+
+       if (listen(sock, 5) < 0) {
+               ERROR_FAILED(err, "listen", errno);
+               g_io_channel_unref(io);
+               return NULL;
+       }
+
+       server_add(io, connect, confirm, user_data, destroy);
+
+       return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+       return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644 (file)
index 0000000..429e8c0
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef BT_IO_H
+#define BT_IO_H
+
+#include <glib.h>
+
+typedef enum {
+       BT_IO_ERROR_DISCONNECTED,
+       BT_IO_ERROR_CONNECT_FAILED,
+       BT_IO_ERROR_FAILED,
+       BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+       BT_IO_L2RAW,
+       BT_IO_L2CAP,
+       BT_IO_L2ERTM,
+       BT_IO_RFCOMM,
+       BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+       BT_IO_OPT_INVALID = 0,
+       BT_IO_OPT_SOURCE,
+       BT_IO_OPT_SOURCE_BDADDR,
+       BT_IO_OPT_DEST,
+       BT_IO_OPT_DEST_BDADDR,
+       BT_IO_OPT_DEFER_TIMEOUT,
+       BT_IO_OPT_SEC_LEVEL,
+       BT_IO_OPT_KEY_SIZE,
+       BT_IO_OPT_CHANNEL,
+       BT_IO_OPT_SOURCE_CHANNEL,
+       BT_IO_OPT_DEST_CHANNEL,
+       BT_IO_OPT_PSM,
+       BT_IO_OPT_CID,
+       BT_IO_OPT_MTU,
+       BT_IO_OPT_OMTU,
+       BT_IO_OPT_IMTU,
+       BT_IO_OPT_MASTER,
+       BT_IO_OPT_HANDLE,
+       BT_IO_OPT_CLASS,
+       BT_IO_OPT_MODE,
+       BT_IO_OPT_FLUSHABLE,
+       BT_IO_OPT_PRIORITY,
+} BtIOOption;
+
+typedef enum {
+       BT_IO_SEC_SDP = 0,
+       BT_IO_SEC_LOW,
+       BT_IO_SEC_MEDIUM,
+       BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef enum {
+       BT_IO_MODE_BASIC = 0,
+       BT_IO_MODE_RETRANS,
+       BT_IO_MODE_FLOWCTL,
+       BT_IO_MODE_ERTM,
+       BT_IO_MODE_STREAMING
+} BtIOMode;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+                                       GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+                                               BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+                                               BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+                               gpointer user_data, GDestroyNotify destroy,
+                               GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+                               BtIOConfirm confirm, gpointer user_data,
+                               GDestroyNotify destroy, GError **err,
+                               BtIOOption opt1, ...);
+
+#endif
diff --git a/compile b/compile
new file mode 100755 (executable)
index 0000000..c0096a7
--- /dev/null
+++ b/compile
@@ -0,0 +1,143 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand `-c -o'.
+
+scriptversion=2009-10-06.20; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009  Free Software
+# Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand `-c -o'.
+Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file `INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit $?
+    ;;
+esac
+
+ofile=
+cfile=
+eat=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+       # configure might choose to run compile as `compile cc -o foo foo.c'.
+       # So we strip `-o arg' only if arg is an object.
+       eat=1
+       case $2 in
+         *.o | *.obj)
+           ofile=$2
+           ;;
+         *)
+           set x "$@" -o "$2"
+           shift
+           ;;
+       esac
+       ;;
+      *.c)
+       cfile=$1
+       set x "$@" "$1"
+       shift
+       ;;
+      *)
+       set x "$@" "$1"
+       shift
+       ;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no `-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # `.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use `[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config.guess b/config.guess
new file mode 100755 (executable)
index 0000000..dc84c68
--- /dev/null
@@ -0,0 +1,1501 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+#   Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner.  Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+       for c in cc gcc c89 c99 ; do
+         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       sysctl="sysctl -n hw.machine_arch"
+       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       case "${UNAME_MACHINE_ARCH}" in
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           sh5el) machine=sh5le-unknown ;;
+           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE_ARCH}" in
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep -q __ELF__
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case "${UNAME_VERSION}" in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit ;;
+    *:OpenBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+       exit ;;
+    *:ekkoBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+       exit ;;
+    *:SolidBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+       exit ;;
+    macppc:MirBSD:*:*)
+       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    *:MirBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    alpha:OSF1:*:*)
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case "$ALPHA_CPU_TYPE" in
+           "EV4 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE="alpha" ;;
+           "EV5 (21164)")
+               UNAME_MACHINE="alphaev5" ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE="alphaev56" ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE="alphapca56" ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE="alphapca57" ;;
+           "EV6 (21264)")
+               UNAME_MACHINE="alphaev6" ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE="alphaev67" ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE="alphaev69" ;;
+           "EV7 (21364)")
+               UNAME_MACHINE="alphaev7" ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE="alphaev79" ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-morphos
+       exit ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit ;;
+    *:z/VM:*:*)
+       echo s390-ibm-zvmoe
+       exit ;;
+    *:OS400:*:*)
+        echo powerpc-ibm-os400
+       exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit ;;
+    arm:riscos:*:*|arm:RISCOS:*:*)
+       echo arm-unknown-riscos
+       exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit ;;
+    DRS?6000:unix:4.0:6*)
+       echo sparc-icl-nx6
+       exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) echo sparc-icl-nx7; exit ;;
+       esac ;;
+    s390x:SunOS:*:*)
+       echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+       echo i386-pc-auroraux${UNAME_RELEASE}
+       exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+       eval $set_cc_for_build
+       SUN_ARCH="i386"
+       # If there is a compiler, see if it is configured for 64-bit objects.
+       # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+       # This test works for both compilers.
+       if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+           if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+               (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+               grep IS_64BIT_ARCH >/dev/null
+           then
+               SUN_ARCH="x86_64"
+           fi
+       fi
+       echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit ;;
+    m68k:machten:*:*)
+       echo m68k-apple-machten${UNAME_RELEASE}
+       exit ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c &&
+         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`$dummy $dummyarg` &&
+           { echo "$SYSTEM_NAME"; exit; }
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit ;;
+    Motorola:*:4.3:PL8-*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+           else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+       fi
+       exit ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+       exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               eval $set_cc_for_build
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+               then
+                       echo "$SYSTEM_NAME"
+               else
+                       echo rs6000-ibm-aix3.2.5
+               fi
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit ;;
+    *:AIX:*:[456])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if [ -x /usr/bin/getconf ]; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+               fi
+               if [ "${HP_ARCH}" = "" ]; then
+                   eval $set_cc_for_build
+                   sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if [ ${HP_ARCH} = "hppa2.0w" ]
+       then
+           eval $set_cc_for_build
+
+           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+           # generating 64-bit code.  GNU and HP use different nomenclature:
+           #
+           # $ CC_FOR_BUILD=cc ./config.guess
+           # => hppa2.0w-hp-hpux11.23
+           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+           # => hppa64-hp-hpux11.23
+
+           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+               grep -q __LP64__
+           then
+               HP_ARCH="hppa2.0w"
+           else
+               HP_ARCH="hppa64"
+           fi
+       fi
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit ;;
+    3050*:HI-UX:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+               { echo "$SYSTEM_NAME"; exit; }
+       echo unknown-hitachi-hiuxwe2
+       exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    *:UNICOS/mp:*:*)
+       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit ;;
+    5000:UNIX_System_V:4.*:*)
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:FreeBSD:*:*)
+       case ${UNAME_MACHINE} in
+           pc98)
+               echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+           amd64)
+               echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+           *)
+               echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+       esac
+       exit ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit ;;
+    *:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit ;;
+    i*:windows32*:*)
+       # uname -m includes "-pc" on this system.
+       echo ${UNAME_MACHINE}-mingw32
+       exit ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit ;;
+    *:Interix*:*)
+       case ${UNAME_MACHINE} in
+           x86)
+               echo i586-pc-interix${UNAME_RELEASE}
+               exit ;;
+           authenticamd | genuineintel | EM64T)
+               echo x86_64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+           IA64)
+               echo ia64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+       esac ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+       echo i${UNAME_MACHINE}-pc-mks
+       exit ;;
+    8664:Windows_NT:*)
+       echo x86_64-pc-mks
+       exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i586-pc-interix
+       exit ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+       echo x86_64-unknown-cygwin
+       exit ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    *:GNU:*:*)
+       # the GNU system
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+       exit ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+       objdump --private-headers /bin/sh | grep -q ld.so.1
+       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+       exit ;;
+    arm*:Linux:*:*)
+       eval $set_cc_for_build
+       if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_EABI__
+       then
+           echo ${UNAME_MACHINE}-unknown-linux-gnu
+       else
+           echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+       fi
+       exit ;;
+    avr32*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    cris:Linux:*:*)
+       echo cris-axis-linux-gnu
+       exit ;;
+    crisv32:Linux:*:*)
+       echo crisv32-axis-linux-gnu
+       exit ;;
+    frv:Linux:*:*)
+       echo frv-unknown-linux-gnu
+       exit ;;
+    i*86:Linux:*:*)
+       LIBC=gnu
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #ifdef __dietlibc__
+       LIBC=dietlibc
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+       echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+       exit ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m32r*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef ${UNAME_MACHINE}
+       #undef ${UNAME_MACHINE}el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=${UNAME_MACHINE}el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=${UNAME_MACHINE}
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+       ;;
+    or32:Linux:*:*)
+       echo or32-unknown-linux-gnu
+       exit ;;
+    padre:Linux:*:*)
+       echo sparc-unknown-linux-gnu
+       exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-gnu
+       exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-gnu ;;
+         PA8*) echo hppa2.0-unknown-linux-gnu ;;
+         *)    echo hppa-unknown-linux-gnu ;;
+       esac
+       exit ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-gnu
+       exit ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-gnu
+       exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit ;;
+    sh64*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    vax:Linux:*:*)
+       echo ${UNAME_MACHINE}-dec-linux-gnu
+       exit ;;
+    x86_64:Linux:*:*)
+       echo x86_64-unknown-linux-gnu
+       exit ;;
+    xtensa*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit ;;
+    i*86:atheos:*:*)
+       echo ${UNAME_MACHINE}-unknown-atheos
+       exit ;;
+    i*86:syllable:*:*)
+       echo ${UNAME_MACHINE}-pc-syllable
+       exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit ;;
+    i*86:*:5:[678]*)
+       # UnixWare 7.x, OpenUNIX and OpenServer 6.
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i586.
+       # Note: whatever this is, it MUST be the same as what config.sub
+       # prints for the "djgpp" host, or else GDB configury will decide that
+       # this is a cross-build.
+       echo i586-pc-msdosdjgpp
+        exit ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       echo m68k-convergent-sysv
+       exit ;;
+    M680?0:D-NIX:5.3:*)
+       echo m68k-diab-dnix
+       exit ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+       OS_REL='.3'
+       test -r /etc/.relid \
+           && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+           && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+           && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+           && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit ;;
+    i*86:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo ${UNAME_MACHINE}-stratus-vos
+       exit ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit ;;
+    BePC:Haiku:*:*)    # Haiku running on Intel PC compatible.
+       echo i586-pc-haiku
+       exit ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-6:SUPER-UX:*:*)
+       echo sx6-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-7:SUPER-UX:*:*)
+       echo sx7-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8:SUPER-UX:*:*)
+       echo sx8-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8R:SUPER-UX:*:*)
+       echo sx8r-nec-superux${UNAME_RELEASE}
+       exit ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+       case $UNAME_PROCESSOR in
+           i386)
+               eval $set_cc_for_build
+               if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+                 if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+                     (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+                     grep IS_64BIT_ARCH >/dev/null
+                 then
+                     UNAME_PROCESSOR="x86_64"
+                 fi
+               fi ;;
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = "x86"; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       exit ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit ;;
+    NSE-?:NONSTOP_KERNEL:*:*)
+       echo nse-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+       exit ;;
+    SEI:*:*:SEIUX)
+        echo mips-sei-seiux${UNAME_RELEASE}
+       exit ;;
+    *:DragonFly:*:*)
+       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case "${UNAME_MACHINE}" in
+           A*) echo alpha-dec-vms ; exit ;;
+           I*) echo ia64-dec-vms ; exit ;;
+           V*) echo vax-dec-vms ; exit ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       echo i386-pc-xenix
+       exit ;;
+    i*86:skyos:*:*)
+       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+       exit ;;
+    i*86:rdos:*:*)
+       echo ${UNAME_MACHINE}-pc-rdos
+       exit ;;
+    i*86:AROS:*:*)
+       echo ${UNAME_MACHINE}-pc-aros
+       exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+       { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    c34*)
+       echo c34-convex-bsd
+       exit ;;
+    c38*)
+       echo c38-convex-bsd
+       exit ;;
+    c4*)
+       echo c4-convex-bsd
+       exit ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..7a3f58c
--- /dev/null
@@ -0,0 +1,96 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Directory for the configuration files */
+#undef CONFIGDIR
+
+/* Define to 1 if you have capabilities library. */
+#undef HAVE_CAPNG
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Define to 1 if you need the dbus_connection_can_send_type() function. */
+#undef NEED_DBUS_CONNECTION_CAN_SEND_TYPE
+
+/* Define to 1 if you need the dbus_watch_get_unix_fd() function. */
+#undef NEED_DBUS_WATCH_GET_UNIX_FD
+
+/* Define if threading support is required */
+#undef NEED_THREADS
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Mobile provider database */
+#undef PROVIDER_DATABASE
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Directory for the storage files */
+#undef STORAGEDIR
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to the equivalent of the C99 'restrict' keyword, or to
+   nothing if this is not supported.  Do not define if restrict is
+   supported directly.  */
+#undef restrict
+/* Work around a bug in Sun C++: it does not support _Restrict, even
+   though the corresponding Sun C compiler does, which causes
+   "#define restrict _Restrict" in the previous line.  Perhaps some future
+   version of Sun C++ will work with _Restrict; if so, it'll probably
+   define __RESTRICT, just as Sun C does.  */
+#if defined __SUNPRO_CC && !defined __RESTRICT
+# define _Restrict
+#endif
diff --git a/config.sub b/config.sub
new file mode 100755 (executable)
index 0000000..2a55a50
--- /dev/null
@@ -0,0 +1,1705 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+#   Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted GNU ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+  uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+  kopensolaris*-gnu* | \
+  storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis | -knuth | -cray | -microblaze)
+               os=
+               basic_machine=$1
+               ;;
+        -bluegene*)
+               os=-cnk
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco6)
+               os=-sco5v6
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5v6*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | am33_2.0 \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+       | bfin \
+       | c4x | clipper \
+       | d10v | d30v | dlx | dsp16xx \
+       | fido | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k | iq2000 \
+       | lm32 \
+       | m32c | m32r | m32rle | m68000 | m68k | m88k \
+       | maxq | mb | microblaze | mcore | mep | metag \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64octeon | mips64octeonel \
+       | mips64orion | mips64orionel \
+       | mips64r5900 | mips64r5900el \
+       | mips64vr | mips64vrel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mips64vr5900 | mips64vr5900el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64r2 | mipsisa64r2el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | moxie \
+       | mt \
+       | msp430 \
+       | nios | nios2 \
+       | ns16k | ns32k \
+       | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | rx \
+       | score \
+       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+       | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+       | spu | strongarm \
+       | tahoe | thumb | tic4x | tic80 | tron \
+       | ubicom32 \
+       | v850 | v850e \
+       | we32k \
+       | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+       | z8k | z80)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12 | picochip)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+       ms1)
+               basic_machine=mt-unknown
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* | avr32-* \
+       | bfin-* | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+       | clipper-* | craynv-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* | iq2000-* \
+       | lm32-* \
+       | m32c-* | m32r-* | m32rle-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64octeon-* | mips64octeonel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64r5900-* | mips64r5900el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mips64vr5900-* | mips64vr5900el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64r2-* | mipsisa64r2el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | mmix-* \
+       | mt-* \
+       | msp430-* \
+       | nios-* | nios2-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* | rx-* \
+       | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+       | sparclite-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+       | tahoe-* | thumb-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
+       | tron-* \
+       | ubicom32-* \
+       | v850-* | v850e-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+       | xstormy16-* | xtensa*-* \
+       | ymp-* \
+       | z8k-* | z80-*)
+               ;;
+       # Recognize the basic CPU types without company name, with glob match.
+       xtensa*)
+               basic_machine=$basic_machine-unknown
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       abacus)
+               basic_machine=abacus-unknown
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amd64)
+               basic_machine=x86_64-pc
+               ;;
+       amd64-*)
+               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aros)
+               basic_machine=i386-pc
+               os=-aros
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       blackfin)
+               basic_machine=bfin-unknown
+               os=-linux
+               ;;
+       blackfin-*)
+               basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       bluegene*)
+               basic_machine=powerpc-ibm
+               os=-cnk
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+        cegcc)
+               basic_machine=arm-unknown
+               os=-cegcc
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       craynv)
+               basic_machine=craynv-cray
+               os=-unicosmp
+               ;;
+       cr16)
+               basic_machine=cr16-unknown
+               os=-elf
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       crisv32 | crisv32-* | etraxfs*)
+               basic_machine=crisv32-axis
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       crx)
+               basic_machine=crx-unknown
+               os=-elf
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dicos)
+               basic_machine=i686-pc
+               os=-dicos
+               ;;
+       djgpp)
+               basic_machine=i586-pc
+               os=-msdosdjgpp
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m68knommu)
+               basic_machine=m68k-unknown
+               os=-linux
+               ;;
+       m68knommu-*)
+               basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+        microblaze)
+               basic_machine=microblaze-xilinx
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       mingw32ce)
+               basic_machine=arm-unknown
+               os=-mingw32ce
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       ms1-*)
+               basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       openrisc | openrisc-*)
+               basic_machine=or32-unknown
+               ;;
+       os400)
+               basic_machine=powerpc-ibm
+               os=-os400
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       parisc)
+               basic_machine=hppa-unknown
+               os=-linux
+               ;;
+       parisc-*)
+               basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+       pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pc98)
+               basic_machine=i386-pc
+               ;;
+       pc98-*)
+               basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2 | pentiumiii | pentium3)
+               basic_machine=i686-pc
+               ;;
+       pentium4)
+               basic_machine=i786-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium4-*)
+               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rdos)
+               basic_machine=i386-pc
+               os=-rdos
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sde)
+               basic_machine=mipsisa32-sde
+               os=-elf
+               ;;
+       sei)
+               basic_machine=mips-sei
+               os=-seiux
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sh5el)
+               basic_machine=sh5le-unknown
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tic55x | c55x*)
+               basic_machine=tic55x-unknown
+               os=-coff
+               ;;
+       tic6x | c6x*)
+               basic_machine=tic6x-unknown
+               os=-coff
+               ;;
+       tile*)
+               basic_machine=tile-unknown
+               os=-linux-gnu
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       tpf)
+               basic_machine=s390x-ibm
+               os=-tpf
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xbox)
+               basic_machine=i686-pc
+               os=-mingw32
+               ;;
+       xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       z80-*-coff)
+               basic_machine=z80-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       mmix)
+               basic_machine=mmix-knuth
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+               basic_machine=sh-unknown
+               ;;
+       sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+               basic_machine=sparc-sun
+               ;;
+       cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+        -auroraux)
+               os=-auroraux
+               ;;
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+             | -sym* | -kopensolaris* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* | -aros* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+             | -openbsd* | -solidbsd* \
+             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* | -cegcc* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+             | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+             | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux-dietlibc)
+               os=-linux-dietlibc
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+        -os400*)
+               os=-os400
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -syllable*)
+               os=-syllable
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+        -tpf*)
+               os=-tpf
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -aros*)
+               os=-aros
+               ;;
+       -kaos*)
+               os=-kaos
+               ;;
+       -zvmoe)
+               os=-zvmoe
+               ;;
+       -dicos*)
+               os=-dicos
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+        score-*)
+               os=-elf
+               ;;
+        spu-*)
+               os=-elf
+               ;;
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+        c4x-* | tic4x-*)
+               os=-coff
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+       pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+        mep-*)
+               os=-elf
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-haiku)
+               os=-haiku
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-knuth)
+               os=-mmixware
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+       *-gould)
+               os=-sysv
+               ;;
+       *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+       *-sgi)
+               os=-irix
+               ;;
+       *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -cnk*|-aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -os400*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -tpf*)
+                               vendor=ibm
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..7180a47
--- /dev/null
+++ b/configure
@@ -0,0 +1,16135 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.63 for ofono 1.4.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+  if (eval ":") 2>/dev/null; then
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+
+  if test $as_have_required = yes &&    (eval ":
+(as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=\$LINENO
+  as_lineno_2=\$LINENO
+  test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+  test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+  :
+else
+  as_candidate_shells=
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  case $as_dir in
+        /*)
+          for as_base in sh bash ksh sh5; do
+            as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+          done;;
+       esac
+done
+IFS=$as_save_IFS
+
+
+      for as_shell in $as_candidate_shells $SHELL; do
+        # Try only shells that exist, to save several forks.
+        if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+               { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+  CONFIG_SHELL=$as_shell
+              as_have_required=yes
+              if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+  (exit $1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+  break
+fi
+
+fi
+
+      done
+
+      if test "x$CONFIG_SHELL" != x; then
+  for as_var in BASH_ENV ENV
+       do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+       done
+       export CONFIG_SHELL
+       exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+    if test $as_have_required = no; then
+  echo This script requires a shell more modern than all the
+      echo shells that I found on your system.  Please install a
+      echo modern shell, or manually run the script under such a
+      echo shell if you do have one.
+      { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+  echo No shell found that supports shell functions.
+  echo Please tell bug-autoconf@gnu.org about your system,
+  echo including any error possibly output before this message.
+  echo This can help us improve future autoconf versions.
+  echo Configuration will now proceed without shell functions.
+}
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -p'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -p'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -p'
+  fi
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+       test -d "$1/.";
+      else
+       case $1 in
+       -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+  # Remove one level of quotation (which was required for Make).
+  ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','`
+  ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+  # Yippee, $ECHO works!
+  :
+else
+  # Restart under the correct shell.
+  exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<_LT_EOF
+$*
+_LT_EOF
+  exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+  if test "X${echo_test_string+set}" != Xset; then
+    # find a string as large as possible, as long as the shell can cope with it
+    for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+      # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+      if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+        { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+      then
+        break
+      fi
+    done
+  fi
+
+  if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+     echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+     test "X$echo_testing_string" = "X$echo_test_string"; then
+    :
+  else
+    # The Solaris, AIX, and Digital Unix default echo programs unquote
+    # backslashes.  This makes it impossible to quote backslashes using
+    #   echo "$something" | sed 's/\\/\\\\/g'
+    #
+    # So, first we look for a working echo in the user's PATH.
+
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for dir in $PATH /usr/ucb; do
+      IFS="$lt_save_ifs"
+      if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+         test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+         echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+         test "X$echo_testing_string" = "X$echo_test_string"; then
+        ECHO="$dir/echo"
+        break
+      fi
+    done
+    IFS="$lt_save_ifs"
+
+    if test "X$ECHO" = Xecho; then
+      # We didn't find a better echo, so look for alternatives.
+      if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+         echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+         test "X$echo_testing_string" = "X$echo_test_string"; then
+        # This shell has a builtin print -r that does the trick.
+        ECHO='print -r'
+      elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+          test "X$CONFIG_SHELL" != X/bin/ksh; then
+        # If we have ksh, try running configure again with it.
+        ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+        export ORIGINAL_CONFIG_SHELL
+        CONFIG_SHELL=/bin/ksh
+        export CONFIG_SHELL
+        exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+      else
+        # Try using printf.
+        ECHO='printf %s\n'
+        if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+          echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+          test "X$echo_testing_string" = "X$echo_test_string"; then
+         # Cool, printf works
+         :
+        elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+            test "X$echo_testing_string" = 'X\t' &&
+            echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+            test "X$echo_testing_string" = "X$echo_test_string"; then
+         CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+         export CONFIG_SHELL
+         SHELL="$CONFIG_SHELL"
+         export SHELL
+         ECHO="$CONFIG_SHELL $0 --fallback-echo"
+        elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+            test "X$echo_testing_string" = 'X\t' &&
+            echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+            test "X$echo_testing_string" = "X$echo_test_string"; then
+         ECHO="$CONFIG_SHELL $0 --fallback-echo"
+        else
+         # maybe with a smaller string...
+         prev=:
+
+         for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+           if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+           then
+             break
+           fi
+           prev="$cmd"
+         done
+
+         if test "$prev" != 'sed 50q "$0"'; then
+           echo_test_string=`eval $prev`
+           export echo_test_string
+           exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+         else
+           # Oops.  We lost completely, so just stick with echo.
+           ECHO=echo
+         fi
+        fi
+      fi
+    fi
+  fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+   lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME='ofono'
+PACKAGE_TARNAME='ofono'
+PACKAGE_VERSION='1.4'
+PACKAGE_STRING='ofono 1.4'
+PACKAGE_BUGREPORT=''
+
+ac_default_prefix=/usr/local
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+DATAFILES_FALSE
+DATAFILES_TRUE
+PROVISION_FALSE
+PROVISION_TRUE
+BLUETOOTH_FALSE
+BLUETOOTH_TRUE
+BLUEZ_LIBS
+BLUEZ_CFLAGS
+ISIMODEM_FALSE
+ISIMODEM_TRUE
+PHONESIM_FALSE
+PHONESIM_TRUE
+CDMAMODEM_FALSE
+CDMAMODEM_TRUE
+ATMODEM_FALSE
+ATMODEM_TRUE
+TOOLS_FALSE
+TOOLS_TRUE
+USB_LIBS
+USB_CFLAGS
+UDEV_FALSE
+UDEV_TRUE
+UDEV_DATADIR
+UDEV_LIBS
+UDEV_CFLAGS
+CAPNG_LIBS
+CAPNG_CFLAGS
+SYSTEMD_FALSE
+SYSTEMD_TRUE
+SYSTEMD_UNITDIR
+DBUS_DATADIR
+DBUS_CONFDIR
+DBUS_LIBS
+DBUS_CFLAGS
+GTHREAD_LIBS
+GTHREAD_CFLAGS
+GLIB_LIBS
+GLIB_CFLAGS
+TEST_FALSE
+TEST_TRUE
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+lt_ECHO
+RANLIB
+AR
+OBJDUMP
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+LIBTOOL
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+PKG_CONFIG
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_maintainer_mode
+enable_dependency_tracking
+enable_static
+enable_shared
+with_pic
+enable_fast_install
+with_gnu_ld
+enable_libtool_lock
+enable_optimization
+enable_debug
+enable_test
+enable_pie
+enable_threads
+with_dbusconfdir
+with_dbusdatadir
+with_systemdunitdir
+enable_capng
+enable_udev
+enable_tools
+enable_atmodem
+enable_cdmamodem
+enable_phonesim
+enable_isimodem
+enable_bluetooth
+with_provisiondb
+enable_provision
+enable_datafiles
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+PKG_CONFIG
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+GLIB_CFLAGS
+GLIB_LIBS
+GTHREAD_CFLAGS
+GTHREAD_LIBS
+DBUS_CFLAGS
+DBUS_LIBS
+CAPNG_CFLAGS
+CAPNG_LIBS
+UDEV_CFLAGS
+UDEV_LIBS
+USB_CFLAGS
+USB_LIBS
+BLUEZ_CFLAGS
+BLUEZ_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *)   ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { $as_echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { $as_echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2
+   { (exit 1); exit 1; }; } ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  { $as_echo "$as_me: error: working directory cannot be determined" >&2
+   { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  { $as_echo "$as_me: error: pwd does not report name of working directory" >&2
+   { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2
+   { (exit 1); exit 1; }; }
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures ofono 1.4 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/ofono]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of ofono 1.4:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-silent-rules          less verbose build output (undo: `make V=1')
+  --disable-silent-rules         verbose build output (undo: `make V=0')
+  --enable-maintainer-mode  enable make rules and dependencies not useful
+                         (and sometimes confusing) to the casual installer
+  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors
+  --enable-static[=PKGS]  build static libraries [default=no]
+  --enable-shared[=PKGS]  build shared libraries [default=yes]
+  --enable-fast-install[=PKGS]
+                          optimize for fast installation [default=yes]
+  --disable-libtool-lock  avoid locking (might break parallel builds)
+  --disable-optimization  disable code optimization through compiler
+  --enable-debug          enable compiling with debugging information
+  --enable-test           enable test/example scripts
+  --enable-pie            enable position independent executables flag
+  --enable-threads        enable threading support
+  --enable-capng          enable capabilities support
+  --disable-udev          don't use udev support even if available
+  --enable-tools          enable testing tools
+  --disable-atmodem       disable ETSI AT modem support
+  --disable-cdmamodem     disable CDMA AT modem support
+  --disable-phonesim      disable Phone simulator support
+  --disable-isimodem      disable PhoNet/ISI modem support
+  --disable-bluetooth     disable Bluetooth modem support
+  --disable-provision     disable provisioning suport
+  --disable-datafiles     don't install configuration and data files
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-pic              try to use only PIC/non-PIC objects [default=use
+                          both]
+  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-dbusconfdir=PATH path to D-Bus config directory
+  --with-dbusdatadir=PATH path to D-Bus data directory
+  --with-systemdunitdir=DIR
+                          path to systemd service directory
+  --with-provisiondb=FILE location of provision database
+
+Some influential environment variables:
+  PKG_CONFIG  path to pkg-config utility
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+  GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config
+  GLIB_LIBS   linker flags for GLIB, overriding pkg-config
+  GTHREAD_CFLAGS
+              C compiler flags for GTHREAD, overriding pkg-config
+  GTHREAD_LIBS
+              linker flags for GTHREAD, overriding pkg-config
+  DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config
+  DBUS_LIBS   linker flags for DBUS, overriding pkg-config
+  CAPNG_CFLAGS
+              C compiler flags for CAPNG, overriding pkg-config
+  CAPNG_LIBS  linker flags for CAPNG, overriding pkg-config
+  UDEV_CFLAGS C compiler flags for UDEV, overriding pkg-config
+  UDEV_LIBS   linker flags for UDEV, overriding pkg-config
+  USB_CFLAGS  C compiler flags for USB, overriding pkg-config
+  USB_LIBS    linker flags for USB, overriding pkg-config
+  BLUEZ_CFLAGS
+              C compiler flags for BLUEZ, overriding pkg-config
+  BLUEZ_LIBS  linker flags for BLUEZ, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+ofono configure 1.4
+generated by GNU Autoconf 2.63
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by ofono $as_me 1.4, which was
+generated by GNU Autoconf 2.63.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  $as_echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      ac_configure_args="$ac_configure_args '$ac_arg'"
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  ac_site_file1=$CONFIG_SITE
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test -r "$ac_site_file"; then
+    { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:$LINENO:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:$LINENO:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+am__api_version='1.11'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5
+$as_echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           rm -rf conftest.one conftest.two conftest.dir
+           echo one > conftest.one
+           echo two > conftest.two
+           mkdir conftest.dir
+           if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+             test -s conftest.one && test -s conftest.two &&
+             test -s conftest.dir/conftest.one &&
+             test -s conftest.dir/conftest.two
+           then
+             ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+             break 3
+           fi
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+
+done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:$LINENO: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[\\\"\#\$\&\'\`$am_lf]*)
+    { { $as_echo "$as_me:$LINENO: error: unsafe absolute working directory name" >&5
+$as_echo "$as_me: error: unsafe absolute working directory name" >&2;}
+   { (exit 1); exit 1; }; };;
+esac
+case $srcdir in
+  *[\\\"\#\$\&\'\`$am_lf\ \    ]*)
+    { { $as_echo "$as_me:$LINENO: error: unsafe srcdir value: \`$srcdir'" >&5
+$as_echo "$as_me: error: unsafe srcdir value: \`$srcdir'" >&2;}
+   { (exit 1); exit 1; }; };;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+   if test "$*" = "X"; then
+      # -L didn't work.
+      set X `ls -t "$srcdir/configure" conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$*" != "X $srcdir/configure conftest.file" \
+      && test "$*" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      { { $as_echo "$as_me:$LINENO: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" >&5
+$as_echo "$as_me: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" >&2;}
+   { (exit 1); exit 1; }; }
+   fi
+
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   { { $as_echo "$as_me:$LINENO: error: newly created file is older than distributed files!
+Check your system clock" >&5
+$as_echo "$as_me: error: newly created file is older than distributed files!
+Check your system clock" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+{ $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  { $as_echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_STRIP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:$LINENO: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if test "${ac_cv_path_mkdir+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in mkdir gmkdir; do
+        for ac_exec_ext in '' $ac_executable_extensions; do
+          { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+          case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+            'mkdir (GNU coreutils) '* | \
+            'mkdir (coreutils) '* | \
+            'mkdir (fileutils) '4.1*)
+              ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+              break 3;;
+          esac
+        done
+       done
+done
+IFS=$as_save_IFS
+
+fi
+
+  if test "${ac_cv_path_mkdir+set}" = set; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    test -d ./--version && rmdir ./--version
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+  [\\/$]* | ?:[\\/]*) ;;
+  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_AWK="$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { $as_echo "$as_me:$LINENO: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+       @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  am__isrc=' -I$(srcdir)'
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    { { $as_echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
+$as_echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='ofono'
+ VERSION='1.4'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no)  AM_DEFAULT_VERBOSITY=1;;
+*)   AM_DEFAULT_VERBOSITY=0;;
+esac
+AM_BACKSLASH='\'
+
+
+
+{ $as_echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+    # Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then
+  enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi
+
+  { $as_echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+   if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+
+  MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+       if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_PKG_CONFIG+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+  ac_pt_PKG_CONFIG=$PKG_CONFIG
+  # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_PKG_CONFIG" = x; then
+    PKG_CONFIG=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    PKG_CONFIG=$ac_pt_PKG_CONFIG
+  fi
+else
+  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+       _pkg_min_version=0.9.0
+       { $as_echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+       if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+               { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       else
+               { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+               PKG_CONFIG=""
+       fi
+
+fi
+
+
+       if (test "${CFLAGS}" = ""); then
+               CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2"
+       fi
+       if (test "$USE_MAINTAINER_MODE" = "yes"); then
+               CFLAGS="$CFLAGS -Werror -Wextra"
+               CFLAGS="$CFLAGS -Wno-unused-parameter"
+               CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+               CFLAGS="$CFLAGS -Wdeclaration-after-statement"
+               CFLAGS="$CFLAGS -Wmissing-declarations"
+               CFLAGS="$CFLAGS -Wredundant-decls"
+               CFLAGS="$CFLAGS -Wcast-align"
+               CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
+       fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+       @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+
+
+{ $as_echo "$as_me:$LINENO: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then
+  enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:$LINENO: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler --version >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler -v >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler -V >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+        if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+if test -z "$ac_file"; then
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if test "${ac_cv_objext+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_compiler_gnu=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       CFLAGS=""
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_c89=$ac_arg
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:$LINENO: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:$LINENO: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+depcc="$CC"   am_compiler_list=
+
+{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    msvisualcpp | msvcmsys)
+      # This compiler won't grok `-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for C/C++ restrict keyword" >&5
+$as_echo_n "checking for C/C++ restrict keyword... " >&6; }
+if test "${ac_cv_c_restrict+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_restrict=no
+   # The order here caters to the fact that C++ does not require restrict.
+   for ac_kw in __restrict __restrict__ _Restrict restrict; do
+     cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+typedef int * int_ptr;
+       int foo (int_ptr $ac_kw ip) {
+       return ip[0];
+       }
+int
+main ()
+{
+int s[1];
+       int * $ac_kw t = s;
+       t[0] = 0;
+       return foo(t)
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_c_restrict=$ac_kw
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+     test "$ac_cv_c_restrict" != no && break
+   done
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_restrict" >&5
+$as_echo "$ac_cv_c_restrict" >&6; }
+
+
+ case $ac_cv_c_restrict in
+   restrict) ;;
+   no) cat >>confdefs.h <<\_ACEOF
+#define restrict /**/
+_ACEOF
+ ;;
+   *)  cat >>confdefs.h <<_ACEOF
+#define restrict $ac_cv_c_restrict
+_ACEOF
+ ;;
+ esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:$LINENO: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler --version >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler -v >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler -V >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_compiler_gnu=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       CFLAGS=""
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_c89=$ac_arg
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:$LINENO: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:$LINENO: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+depcc="$CC"   am_compiler_list=
+
+{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    msvisualcpp | msvcmsys)
+      # This compiler won't grok `-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+if test "x$CC" != xcc; then
+  { $as_echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5
+$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; }
+else
+  { $as_echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5
+$as_echo_n "checking whether cc understands -c and -o together... " >&6; }
+fi
+set dummy $CC; ac_cc=`$as_echo "$2" |
+                     sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+if { as_var=ac_cv_prog_cc_${ac_cc}_c_o; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+# Make sure it works both with $CC and with simple cc.
+# We do the test twice because some compilers refuse to overwrite an
+# existing .o file with -o, though they will create one.
+ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+rm -f conftest2.*
+if { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+   test -f conftest2.$ac_objext && { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); };
+then
+  eval ac_cv_prog_cc_${ac_cc}_c_o=yes
+  if test "x$CC" != xcc; then
+    # Test first that cc exists at all.
+    if { ac_try='cc -c conftest.$ac_ext >&5'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+      ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+      rm -f conftest2.*
+      if { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        test -f conftest2.$ac_objext && { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); };
+      then
+       # cc works too.
+       :
+      else
+       # cc exists but doesn't like -o.
+       eval ac_cv_prog_cc_${ac_cc}_c_o=no
+      fi
+    fi
+  fi
+else
+  eval ac_cv_prog_cc_${ac_cc}_c_o=no
+fi
+rm -f core conftest*
+
+fi
+if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then
+  { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define NO_MINUS_C_MINUS_O 1
+_ACEOF
+
+fi
+
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+
+
+
+       { $as_echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -fPIE" >&5
+$as_echo_n "checking whether ${CC-cc} accepts -fPIE... " >&6; }
+if test "${ac_cv_prog_cc_pie+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+
+               echo 'void f(){}' > conftest.c
+               if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+                       ac_cv_prog_cc_pie=yes
+               else
+                       ac_cv_prog_cc_pie=no
+               fi
+               rm -rf conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_pie" >&5
+$as_echo "$ac_cv_prog_cc_pie" >&6; }
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           rm -rf conftest.one conftest.two conftest.dir
+           echo one > conftest.one
+           echo two > conftest.two
+           mkdir conftest.dir
+           if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+             test -s conftest.one && test -s conftest.two &&
+             test -s conftest.dir/conftest.one &&
+             test -s conftest.dir/conftest.two
+           then
+             ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+             break 3
+           fi
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+
+done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+  [\\/$]* | ?:[\\/]*) ;;
+  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+
+
+
+
+# Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then
+  enableval=$enable_static; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_static=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_static=no
+fi
+
+
+
+
+
+
+
+
+
+case `pwd` in
+  *\ * | *\    *)
+    { $as_echo "$as_me:$LINENO: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.2.6b'
+macro_revision='1.3017'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
+$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+{ $as_echo "$as_me:$LINENO: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if test "${ac_cv_build+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
+$as_echo "$as_me: error: invalid value of canonical build" >&2;}
+   { (exit 1); exit 1; }; };;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if test "${ac_cv_host+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
+$as_echo "$as_me: error: invalid value of canonical host" >&2;}
+   { (exit 1); exit 1; }; };;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if test "${ac_cv_path_SED+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+     for ac_i in 1 2 3 4 5 6 7; do
+       ac_script="$ac_script$as_nl$ac_script"
+     done
+     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+     $as_unset ac_script || ac_script=
+     if test -z "$SED"; then
+  ac_path_SED_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+  # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo '' >> "conftest.nl"
+    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_SED_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_SED="$ac_path_SED"
+      ac_path_SED_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_SED_found && break 3
+    done
+  done
+done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_SED"; then
+    { { $as_echo "$as_me:$LINENO: error: no acceptable sed could be found in \$PATH" >&5
+$as_echo "$as_me: error: no acceptable sed could be found in \$PATH" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+else
+  ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+  rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if test "${ac_cv_path_FGREP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+   then ac_cv_path_FGREP="$GREP -F"
+   else
+     if test -z "$FGREP"; then
+  ac_path_FGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in fgrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+  # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'FGREP' >> "conftest.nl"
+    "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_FGREP="$ac_path_FGREP"
+      ac_path_FGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_FGREP_found && break 3
+    done
+  done
+done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_FGREP"; then
+    { { $as_echo "$as_me:$LINENO: error: no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+else
+  ac_cv_path_FGREP=$FGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+  withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  { $as_echo "$as_me:$LINENO: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+       ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  { $as_echo "$as_me:$LINENO: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+  { $as_echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if test "${lt_cv_path_LD+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+       test "$with_gnu_ld" != no && break
+       ;;
+      *)
+       test "$with_gnu_ld" != yes && break
+       ;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  { $as_echo "$as_me:$LINENO: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && { { $as_echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+$as_echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+   { (exit 1); exit 1; }; }
+{ $as_echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if test "${lt_cv_prog_gnu_ld+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if test "${lt_cv_path_NM+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+       # Check to see if the nm accepts a BSD-compat flag.
+       # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+       #   nm: unknown option "B" ignored
+       # Tru64's nm complains that /dev/null is an invalid object file
+       case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+       */dev/null* | *'Invalid file or object type'*)
+         lt_cv_path_NM="$tmp_nm -B"
+         break
+         ;;
+       *)
+         case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+         */dev/null*)
+           lt_cv_path_NM="$tmp_nm -p"
+           break
+           ;;
+         *)
+           lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+           continue # so that we can try to find one that supports BSD flags
+           ;;
+         esac
+         ;;
+       esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_DUMPBIN+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DUMPBIN"; then
+  ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+  { $as_echo "$as_me:$LINENO: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$DUMPBIN" && break
+  done
+fi
+if test -z "$DUMPBIN"; then
+  ac_ct_DUMPBIN=$DUMPBIN
+  for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_DUMPBIN+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DUMPBIN"; then
+  ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_DUMPBIN" && break
+done
+
+  if test "x$ac_ct_DUMPBIN" = x; then
+    DUMPBIN=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DUMPBIN=$ac_ct_DUMPBIN
+  fi
+fi
+
+
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if test "${lt_cv_nm_interface+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:5843: $ac_compile\"" >&5)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:5846: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:5849: output\"" >&5)
+  cat conftest.out >&5
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:$LINENO: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if test "${lt_cv_sys_max_cmd_len+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+    i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536      # usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[         ]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+                = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+             test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+  { $as_echo "$as_me:$LINENO: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:$LINENO: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+{ $as_echo "$as_me:$LINENO: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:$LINENO: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+{ $as_echo "$as_me:$LINENO: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if test "${lt_cv_ld_reload_flag+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_OBJDUMP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OBJDUMP"; then
+  ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+  { $as_echo "$as_me:$LINENO: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+  ac_ct_OBJDUMP=$OBJDUMP
+  # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OBJDUMP"; then
+  ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_OBJDUMP="objdump"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OBJDUMP" = x; then
+    OBJDUMP="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OBJDUMP=$ac_ct_OBJDUMP
+  fi
+else
+  OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if test "${lt_cv_deplibs_check_method+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[45]*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  if ( file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[3-9]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AR+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_AR="${ac_tool_prefix}ar"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { $as_echo "$as_me:$LINENO: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+  ac_ct_AR=$AR
+  # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_AR+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_AR="ar"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_AR" = x; then
+    AR="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+else
+  AR="$ac_cv_prog_AR"
+fi
+
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_STRIP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:$LINENO: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:$LINENO: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:$LINENO: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[ABCDEGRST]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[BCDEGRST]'
+  ;;
+osf*)
+  symcode='[BCDEGQRST]'
+  ;;
+solaris*)
+  symcode='[BDRT]'
+  ;;
+sco3.2v5*)
+  symcode='[DT]'
+  ;;
+sysv4.2uw2*)
+  symcode='[DT]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[ABDT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK '"\
+"     {last_section=section; section=\$ 3};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[     ]\($symcode$symcode*\)[         ][      ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5
+  (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+       mv -f "$nlist"T "$nlist"
+      else
+       rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+       if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+         cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+         # Now generate the symbol file.
+         eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+         cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+const struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+         $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+         cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+         # Now try linking the two files.
+         mv conftest.$ac_objext conftstm.$ac_objext
+         lt_save_LIBS="$LIBS"
+         lt_save_CFLAGS="$CFLAGS"
+         LIBS="conftstm.$ac_objext"
+         CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+         if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext}; then
+           pipe_works=yes
+         fi
+         LIBS="$lt_save_LIBS"
+         CFLAGS="$lt_save_CFLAGS"
+       else
+         echo "cannot find nm_test_func in $nlist" >&5
+       fi
+      else
+       echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  { $as_echo "$as_me:$LINENO: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then
+  enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+       HPUX_IA64_MODE="32"
+       ;;
+      *ELF-64*)
+       HPUX_IA64_MODE="64"
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '#line 7054 "configure"' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -melf32bsmip"
+         ;;
+       *N32*)
+         LD="${LD-ld} -melf32bmipn32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -melf64bmip"
+       ;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -32"
+         ;;
+       *N32*)
+         LD="${LD-ld} -n32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -64"
+         ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_i386_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_i386"
+           ;;
+         ppc64-*linux*|powerpc64-*linux*)
+           LD="${LD-ld} -m elf32ppclinux"
+           ;;
+         s390x-*linux*)
+           LD="${LD-ld} -m elf_s390"
+           ;;
+         sparc64-*linux*)
+           LD="${LD-ld} -m elf32_sparc"
+           ;;
+       esac
+       ;;
+      *64-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_x86_64_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_x86_64"
+           ;;
+         ppc*-*linux*|powerpc*-*linux*)
+           LD="${LD-ld} -m elf64ppc"
+           ;;
+         s390*-*linux*|s390*-*tpf*)
+           LD="${LD-ld} -m elf64_s390"
+           ;;
+         sparc*-*linux*)
+           LD="${LD-ld} -m elf64_sparc"
+           ;;
+       esac
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  { $as_echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if test "${lt_cv_cc_needs_belf+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+     cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  lt_cv_cc_needs_belf=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       lt_cv_cc_needs_belf=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+sparc*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*) LD="${LD-ld} -m elf64_sparc" ;;
+      *)
+       if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+         LD="${LD-ld} -64"
+       fi
+       ;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+  case $host_os in
+    rhapsody* | darwin*)
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_DSYMUTIL+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DSYMUTIL"; then
+  ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+  { $as_echo "$as_me:$LINENO: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+  ac_ct_DSYMUTIL=$DSYMUTIL
+  # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_DSYMUTIL+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DSYMUTIL"; then
+  ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_DSYMUTIL" = x; then
+    DSYMUTIL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DSYMUTIL=$ac_ct_DSYMUTIL
+  fi
+else
+  DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_NMEDIT+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NMEDIT"; then
+  ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+  { $as_echo "$as_me:$LINENO: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+  ac_ct_NMEDIT=$NMEDIT
+  # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_NMEDIT+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_NMEDIT"; then
+  ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_NMEDIT="nmedit"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_NMEDIT" = x; then
+    NMEDIT=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    NMEDIT=$ac_ct_NMEDIT
+  fi
+else
+  NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_LIPO+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$LIPO"; then
+  ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+  { $as_echo "$as_me:$LINENO: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+  ac_ct_LIPO=$LIPO
+  # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_LIPO"; then
+  ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_LIPO="lipo"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_LIPO" = x; then
+    LIPO=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    LIPO=$ac_ct_LIPO
+  fi
+else
+  LIPO="$ac_cv_prog_LIPO"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_OTOOL+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL"; then
+  ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+  { $as_echo "$as_me:$LINENO: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+  ac_ct_OTOOL=$OTOOL
+  # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_OTOOL+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL"; then
+  ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_OTOOL="otool"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL" = x; then
+    OTOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL=$ac_ct_OTOOL
+  fi
+else
+  OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_OTOOL64+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL64"; then
+  ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+  { $as_echo "$as_me:$LINENO: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+  ac_ct_OTOOL64=$OTOOL64
+  # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_OTOOL64+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL64"; then
+  ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_OTOOL64="otool64"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL64" = x; then
+    OTOOL64=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL64=$ac_ct_OTOOL64
+  fi
+else
+  OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    { $as_echo "$as_me:$LINENO: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if test "${lt_cv_apple_cc_single_mod+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+       # By default we will add the -single_module flag. You can override
+       # by either setting the environment variable LT_MULTI_MODULE
+       # non-empty at configure time, or by adding -multi_module to the
+       # link flags.
+       rm -rf libconftest.dylib*
+       echo "int foo(void){return 1;}" > conftest.c
+       echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+       $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+         -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+       if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+         lt_cv_apple_cc_single_mod=yes
+       else
+         cat conftest.err >&5
+       fi
+       rm -rf libconftest.dylib*
+       rm -f conftest.*
+      fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+    { $as_echo "$as_me:$LINENO: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if test "${lt_cv_ld_exported_symbols_list+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  lt_cv_ld_exported_symbols_list=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       lt_cv_ld_exported_symbols_list=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+       LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+    case $host_os in
+    rhapsody* | darwin1.[012])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+       10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+       10.[012]*)
+         _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+       10.*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_header_stdc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_header in dlfcn.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+# Set options
+
+
+
+        enable_dlopen=no
+
+
+  enable_win32_dll=no
+
+
+            # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then
+  enableval=$enable_shared; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_shared=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then
+  withval=$with_pic; pic_mode="$withval"
+else
+  pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+  # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then
+  enableval=$enable_fast_install; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_fast_install=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if test "${lt_cv_objdir+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+  case $cc_temp in
+    compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+    distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    { $as_echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/${ac_tool_prefix}file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+      if test -n "$file_magic_test_file"; then
+       case $deplibs_check_method in
+       "file_magic "*)
+         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+           $EGREP "$file_magic_regex" > /dev/null; then
+           :
+         else
+           cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+         fi ;;
+       esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    { $as_echo "$as_me:$LINENO: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/file"
+      if test -n "$file_magic_test_file"; then
+       case $deplibs_check_method in
+       "file_magic "*)
+         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+           $EGREP "$file_magic_regex" > /dev/null; then
+           :
+         else
+           cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+         fi ;;
+       esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  else
+    MAGIC_CMD=:
+  fi
+fi
+
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+  lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+  { $as_echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_rtti_exceptions=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="-fno-rtti -fno-exceptions"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:8877: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:8881: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_rtti_exceptions=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+    lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+    :
+fi
+
+fi
+
+
+
+
+
+
+  lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+{ $as_echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+
+  if test "$GCC" = yes; then
+    lt_prog_compiler_wl='-Wl,'
+    lt_prog_compiler_static='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            lt_prog_compiler_pic='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic='-fno-common'
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       # +Z the default
+       ;;
+      *)
+       lt_prog_compiler_pic='-fPIC'
+       ;;
+      esac
+      ;;
+
+    interix[3-9]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       lt_prog_compiler_pic=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      lt_prog_compiler_pic='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl='-Wl,'
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       lt_prog_compiler_static='-Bstatic'
+      else
+       lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+       # +Z the default
+       ;;
+      *)
+       lt_prog_compiler_pic='+Z'
+       ;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-KPIC'
+       lt_prog_compiler_static='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-fPIC'
+       lt_prog_compiler_static='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='--shared'
+       lt_prog_compiler_static='--static'
+       ;;
+      pgcc* | pgf77* | pgf90* | pgf95*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+       # which looks to be a dead project)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-fpic'
+       lt_prog_compiler_static='-Bstatic'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static='-non_shared'
+        ;;
+      xl*)
+       # IBM XL C 8.0/Fortran 10.1 on PPC
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-qpic'
+       lt_prog_compiler_static='-qstaticlink'
+       ;;
+      *)
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ C*)
+         # Sun C 5.9
+         lt_prog_compiler_pic='-KPIC'
+         lt_prog_compiler_static='-Bstatic'
+         lt_prog_compiler_wl='-Wl,'
+         ;;
+       *Sun\ F*)
+         # Sun Fortran 8.3 passes all unrecognized flags to the linker
+         lt_prog_compiler_pic='-KPIC'
+         lt_prog_compiler_static='-Bstatic'
+         lt_prog_compiler_wl=''
+         ;;
+       esac
+       ;;
+      esac
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    rdos*)
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95*)
+       lt_prog_compiler_wl='-Qoption ld ';;
+      *)
+       lt_prog_compiler_wl='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl='-Qoption ld '
+      lt_prog_compiler_pic='-PIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+       lt_prog_compiler_pic='-Kconform_pic'
+       lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    unicos*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_can_build_shared=no
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic='-pic'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared=no
+      ;;
+    esac
+  fi
+
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic=
+    ;;
+  *)
+    lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: result: $lt_prog_compiler_pic" >&5
+$as_echo "$lt_prog_compiler_pic" >&6; }
+
+
+
+
+
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+  { $as_echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if test "${lt_cv_prog_compiler_pic_works+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic_works=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:9216: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:9220: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_pic_works=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+    case $lt_prog_compiler_pic in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+     esac
+else
+    lt_prog_compiler_pic=
+     lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if test "${lt_cv_prog_compiler_static_works+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_static_works=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler_static_works=yes
+       fi
+     else
+       lt_cv_prog_compiler_static_works=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+    :
+else
+    lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+  { $as_echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if test "${lt_cv_prog_compiler_c_o+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:9321: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:9325: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+  { $as_echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if test "${lt_cv_prog_compiler_c_o+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:9376: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:9380: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  { $as_echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  { $as_echo "$as_me:$LINENO: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+  if test "$hard_links" = no; then
+    { $as_echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+
+
+
+
+
+  { $as_echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+  runpath_var=
+  allow_undefined_flag=
+  always_export_symbols=no
+  archive_cmds=
+  archive_expsym_cmds=
+  compiler_needs_object=no
+  enable_shared_with_static_runtimes=no
+  export_dynamic_flag_spec=
+  export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  hardcode_automatic=no
+  hardcode_direct=no
+  hardcode_direct_absolute=no
+  hardcode_libdir_flag_spec=
+  hardcode_libdir_flag_spec_ld=
+  hardcode_libdir_separator=
+  hardcode_minus_L=no
+  hardcode_shlibpath_var=unsupported
+  inherit_rpath=no
+  link_all_deplibs=unknown
+  module_cmds=
+  module_expsym_cmds=
+  old_archive_from_new_cmds=
+  old_archive_from_expsyms_cmds=
+  thread_safe_flag_spec=
+  whole_archive_flag_spec=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  esac
+
+  ld_shlibs=yes
+  if test "$with_gnu_ld" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    export_dynamic_flag_spec='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      whole_archive_flag_spec=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[3-9]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+       ld_shlibs=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       allow_undefined_flag=unsupported
+       # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+       # support --undefined.  This deserves some investigation.  FIXME
+       archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      allow_undefined_flag=unsupported
+      always_export_symbols=no
+      enable_shared_with_static_runtimes=yes
+      export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+       # If the export-symbols file already is a .def file (1st line
+       # is EXPORTS), use it as is; otherwise, prepend...
+       archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+         cp $export_symbols $output_objdir/$soname.def;
+       else
+         echo EXPORTS > $output_objdir/$soname.def;
+         cat $export_symbols >> $output_objdir/$soname.def;
+       fi~
+       $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    interix[3-9]*)
+      hardcode_direct=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      export_dynamic_flag_spec='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+       case $cc_basename in
+         diet\ *) tmp_diet=yes;;       # linux-dietlibc with static linking (!diet-dyn)
+       esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+        && test "$tmp_diet" = no
+      then
+       tmp_addflag=
+       tmp_sharedflag='-shared'
+       case $cc_basename,$host_cpu in
+        pgcc*)                         # Portland Group C compiler
+         whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag'
+         ;;
+       pgf77* | pgf90* | pgf95*)       # Portland Group f77 and f90 compilers
+         whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag -Mnomain' ;;
+       ecc*,ia64* | icc*,ia64*)        # Intel C compiler on ia64
+         tmp_addflag=' -i_dynamic' ;;
+       efc*,ia64* | ifort*,ia64*)      # Intel Fortran compiler on ia64
+         tmp_addflag=' -i_dynamic -nofor_main' ;;
+       ifc* | ifort*)                  # Intel Fortran compiler
+         tmp_addflag=' -nofor_main' ;;
+       lf95*)                          # Lahey Fortran 8.1
+         whole_archive_flag_spec=
+         tmp_sharedflag='--shared' ;;
+       xl[cC]*)                        # IBM XL C 8.0 on PPC (deal with xlf below)
+         tmp_sharedflag='-qmkshrobj'
+         tmp_addflag= ;;
+       esac
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ C*)                       # Sun C 5.9
+         whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+         compiler_needs_object=yes
+         tmp_sharedflag='-G' ;;
+       *Sun\ F*)                       # Sun Fortran 8.3
+         tmp_sharedflag='-G' ;;
+       esac
+       archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+           cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+           echo "local: *; };" >> $output_objdir/$libname.ver~
+           $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+       case $cc_basename in
+       xlf*)
+         # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+         whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+         hardcode_libdir_flag_spec=
+         hardcode_libdir_flag_spec_ld='-rpath $libdir'
+         archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+         if test "x$supports_anon_versioning" = xyes; then
+           archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+             cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+             echo "local: *; };" >> $output_objdir/$libname.ver~
+             $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+         fi
+         ;;
+       esac
+      else
+        ld_shlibs=no
+      fi
+      ;;
+
+    netbsd*)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+       wlarc=
+      else
+       archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+       ld_shlibs=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+       ld_shlibs=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+       ;;
+       *)
+         # For security reasons, it is highly recommended that you always
+         # use absolute paths for naming shared libraries, and exclude the
+         # DT_RUNPATH tag from executables and libraries.  But doing so
+         # requires that you compile everything twice, which is a pain.
+         if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+           hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+           archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+         else
+           ld_shlibs=no
+         fi
+       ;;
+      esac
+      ;;
+
+    sunos4*)
+      archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+    esac
+
+    if test "$ld_shlibs" = no; then
+      runpath_var=
+      hardcode_libdir_flag_spec=
+      export_dynamic_flag_spec=
+      whole_archive_flag_spec=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag=unsupported
+      always_export_symbols=yes
+      archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+       # Neither direct hardcoding nor static linking is supported with a
+       # broken collect2.
+       hardcode_direct=unsupported
+      fi
+      ;;
+
+    aix[4-9]*)
+      if test "$host_cpu" = ia64; then
+       # On IA64, the linker does run time linking by default, so we don't
+       # have to do anything special.
+       aix_use_runtimelinking=no
+       exp_sym_flag='-Bexport'
+       no_entry_flag=""
+      else
+       # If we're using GNU nm, then we don't want the "-C" option.
+       # -C means demangle to AIX nm, but means don't demangle with GNU nm
+       if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+         export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       else
+         export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       fi
+       aix_use_runtimelinking=no
+
+       # Test if we are trying to use run time linking or normal
+       # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+       # need to do runtime linking.
+       case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+         for ld_flag in $LDFLAGS; do
+         if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+           aix_use_runtimelinking=yes
+           break
+         fi
+         done
+         ;;
+       esac
+
+       exp_sym_flag='-bexport'
+       no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds=''
+      hardcode_direct=yes
+      hardcode_direct_absolute=yes
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      file_list_spec='${wl}-f,'
+
+      if test "$GCC" = yes; then
+       case $host_os in aix4.[012]|aix4.[012].*)
+       # We only want to do this on AIX 4.2 and lower, the check
+       # below for broken collect2 doesn't work under 4.3+
+         collect2name=`${CC} -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+          strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+         # We have reworked collect2
+         :
+         else
+         # We have old collect2
+         hardcode_direct=unsupported
+         # It fails to find uninstalled libraries when the uninstalled
+         # path is not listed in the libpath.  Setting hardcode_minus_L
+         # to unsupported forces relinking
+         hardcode_minus_L=yes
+         hardcode_libdir_flag_spec='-L$libdir'
+         hardcode_libdir_separator=
+         fi
+         ;;
+       esac
+       shared_flag='-shared'
+       if test "$aix_use_runtimelinking" = yes; then
+         shared_flag="$shared_flag "'${wl}-G'
+       fi
+      else
+       # not using gcc
+       if test "$host_cpu" = ia64; then
+       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+       # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+       else
+         if test "$aix_use_runtimelinking" = yes; then
+           shared_flag='${wl}-G'
+         else
+           shared_flag='${wl}-bM:SRE'
+         fi
+       fi
+      fi
+
+      export_dynamic_flag_spec='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols=yes
+      if test "$aix_use_runtimelinking" = yes; then
+       # Warning - without using the other runtime loading flags (-brtl),
+       # -berok will link without error, but may produce a broken library.
+       allow_undefined_flag='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+
+lt_aix_libpath_sed='
+    /Import File Strings/,/^$/ {
+       /^0/ {
+           s/^0  *\(.*\)$/\1/
+           p
+       }
+    }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+  aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+        archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+       if test "$host_cpu" = ia64; then
+         hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+         allow_undefined_flag="-z nodefs"
+         archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+       else
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+
+lt_aix_libpath_sed='
+    /Import File Strings/,/^$/ {
+       /^0/ {
+           s/^0  *\(.*\)$/\1/
+           p
+       }
+    }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+  aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+         # Warning - without using the other run time loading flags,
+         # -berok will link without error, but may produce a broken library.
+         no_undefined_flag=' ${wl}-bernotok'
+         allow_undefined_flag=' ${wl}-berok'
+         # Exported symbols can be pulled into shared objects from archives
+         whole_archive_flag_spec='$convenience'
+         archive_cmds_need_lc=yes
+         # This is similar to how AIX traditionally builds its shared libraries.
+         archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+       fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[45]*)
+      export_dynamic_flag_spec=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec=' '
+      allow_undefined_flag=unsupported
+      # Tell ltmain to make .lib files, not .a files.
+      libext=lib
+      # Tell ltmain to make .dll files, not .so files.
+      shrext_cmds=".dll"
+      # FIXME: Setting linknames here is a bad hack.
+      archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+      # The linker will automatically build a .lib file if we build a DLL.
+      old_archive_from_new_cmds='true'
+      # FIXME: Should let the user specify the lib program.
+      old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+      fix_srcfile_path='`cygpath -w "$srcfile"`'
+      enable_shared_with_static_runtimes=yes
+      ;;
+
+    darwin* | rhapsody*)
+
+
+  archive_cmds_need_lc=no
+  hardcode_direct=no
+  hardcode_automatic=yes
+  hardcode_shlibpath_var=unsupported
+  whole_archive_flag_spec=''
+  link_all_deplibs=yes
+  allow_undefined_flag="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=echo
+    archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+  else
+  ld_shlibs=no
+  fi
+
+      ;;
+
+    dgux*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    freebsd1*)
+      ld_shlibs=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+       archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+       archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      export_dynamic_flag_spec='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+       archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+       hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+       hardcode_libdir_flag_spec_ld='+b $libdir'
+       hardcode_libdir_separator=:
+       hardcode_direct=yes
+       hardcode_direct_absolute=yes
+       export_dynamic_flag_spec='${wl}-E'
+       # hardcode_minus_L: Not really in the search PATH,
+       # but as the default location of the library.
+       hardcode_minus_L=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+       case $host_cpu in
+       hppa*64*)
+         archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       esac
+      else
+       case $host_cpu in
+       hppa*64*)
+         archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       esac
+      fi
+      if test "$with_gnu_ld" = no; then
+       hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+       hardcode_libdir_separator=:
+
+       case $host_cpu in
+       hppa*64*|ia64*)
+         hardcode_direct=no
+         hardcode_shlibpath_var=no
+         ;;
+       *)
+         hardcode_direct=yes
+         hardcode_direct_absolute=yes
+         export_dynamic_flag_spec='${wl}-E'
+
+         # hardcode_minus_L: Not really in the search PATH,
+         # but as the default location of the library.
+         hardcode_minus_L=yes
+         ;;
+       esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+       archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       # Try to use the -exported_symbol ld option, if it does not
+       # work, assume that -exports_file does not work either and
+       # implicitly export all symbols.
+        save_LDFLAGS="$LDFLAGS"
+        LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+        cat >conftest.$ac_ext <<_ACEOF
+int foo(void) {}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+        LDFLAGS="$save_LDFLAGS"
+      else
+       archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+       archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      inherit_rpath=yes
+      link_all_deplibs=yes
+      ;;
+
+    netbsd*)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+       archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    newsos6)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_shlibpath_var=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+       hardcode_direct=yes
+       hardcode_shlibpath_var=no
+       hardcode_direct_absolute=yes
+       if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+         archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+         archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+         hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+         export_dynamic_flag_spec='${wl}-E'
+       else
+         case $host_os in
+          openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+            archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+            hardcode_libdir_flag_spec='-R$libdir'
+            ;;
+          *)
+            archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+            hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+            ;;
+         esac
+       fi
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      allow_undefined_flag=unsupported
+      archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+       allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+       allow_undefined_flag=' -expect_unresolved \*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+
+    osf4* | osf5*)     # as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+       allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      else
+       allow_undefined_flag=' -expect_unresolved \*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+       archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+       $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+       # Both c and cxx compiler support -rpath directly
+       hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_separator=:
+      ;;
+
+    solaris*)
+      no_undefined_flag=' -z defs'
+      if test "$GCC" = yes; then
+       wlarc='${wl}'
+       archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+       case `$CC -V 2>&1` in
+       *"Compilers 5.0"*)
+         wlarc=''
+         archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+         ;;
+       *)
+         wlarc='${wl}'
+         archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+         archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+         ;;
+       esac
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_shlibpath_var=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *)
+       # The compiler driver will combine and reorder linker options,
+       # but understands `-z linker_flag'.  GCC discards it without `$wl',
+       # but is careful enough not to reorder.
+       # Supported since Solaris 2.6 (maybe 2.5.1?)
+       if test "$GCC" = yes; then
+         whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+       else
+         whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+       fi
+       ;;
+      esac
+      link_all_deplibs=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+       # Use $CC to link under sequent, because it throws in some extra .o
+       # files that make .init and .fini sections work.
+       archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+       sni)
+         archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         hardcode_direct=yes # is this really true???
+       ;;
+       siemens)
+         ## LD is ld it makes a PLAMLIB
+         ## CC just makes a GrossModule.
+         archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+         reload_cmds='$CC -r -o $output$reload_objs'
+         hardcode_direct=no
+        ;;
+       motorola)
+         archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+       ;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      export_dynamic_flag_spec='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+       hardcode_shlibpath_var=no
+       runpath_var=LD_RUN_PATH
+       hardcode_runpath_var=yes
+       ld_shlibs=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+      no_undefined_flag='${wl}-z,text'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      no_undefined_flag='${wl}-z,text'
+      allow_undefined_flag='${wl}-z,nodefs'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-R,$libdir'
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      export_dynamic_flag_spec='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      ld_shlibs=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+       export_dynamic_flag_spec='${wl}-Blargedynsym'
+       ;;
+      esac
+    fi
+  fi
+
+{ $as_echo "$as_me:$LINENO: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      { $as_echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+      $RM conftest*
+      echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+      if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } 2>conftest.err; then
+        soname=conftest
+        lib=conftest
+        libobjs=conftest.$ac_objext
+        deplibs=
+        wl=$lt_prog_compiler_wl
+       pic_flag=$lt_prog_compiler_pic
+        compiler_flags=-v
+        linker_flags=-v
+        verstring=
+        output_objdir=.
+        libname=conftest
+        lt_save_allow_undefined_flag=$allow_undefined_flag
+        allow_undefined_flag=
+        if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\"") >&5
+  (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+        then
+         archive_cmds_need_lc=no
+        else
+         archive_cmds_need_lc=yes
+        fi
+        allow_undefined_flag=$lt_save_allow_undefined_flag
+      else
+        cat conftest.err 1>&5
+      fi
+      $RM conftest*
+      { $as_echo "$as_me:$LINENO: result: $archive_cmds_need_lc" >&5
+$as_echo "$archive_cmds_need_lc" >&6; }
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+  if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+  else
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+  fi
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+       lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[lt_foo]++; }
+  if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+  sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[4-9]*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+          echo ' yes '
+          echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+       :
+      else
+       can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[45]*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$host_os in
+  yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+      if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH printed by
+        # mingw gcc, but we are running on Cygwin. Gcc prints its search
+        # path with ; separators, and with drive letters. We can handle the
+        # drive letters (cygwin fileutils understands them), so leave them,
+        # especially as we might pass files found there to a mingw objdump,
+        # which wouldn't understand a cygwinified path. Ahh.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    ;;
+
+  *)
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[123]*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[01]* | freebsdelf3.[01]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+interix[3-9]*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+       if test "$lt_cv_prog_gnu_ld" = yes; then
+               version_type=linux
+       else
+               version_type=irix
+       fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # Some binutils ld are patched to set DT_RUNPATH
+  save_LDFLAGS=$LDFLAGS
+  save_libdir=$libdir
+  eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+       LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then
+  shlibpath_overrides_runpath=yes
+fi
+
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+  LDFLAGS=$save_LDFLAGS
+  libdir=$save_libdir
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Add ABI-specific directories to the system library path.
+  sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[      ]*hwcap[        ]/d;s/[:,      ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+    *)                         need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+       shlibpath_overrides_runpath=no
+       ;;
+      *)
+       shlibpath_overrides_runpath=yes
+       ;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+       ;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+{ $as_echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+   test -n "$runpath_var" ||
+   test "X$hardcode_automatic" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:$LINENO: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+   test "$inherit_rpath" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+
+
+
+
+
+  if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    { $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+    ;;
+
+  *)
+    { $as_echo "$as_me:$LINENO: checking for shl_load" >&5
+$as_echo_n "checking for shl_load... " >&6; }
+if test "${ac_cv_func_shl_load+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define shl_load to an innocuous variant, in case <limits.h> declares shl_load.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define shl_load innocuous_shl_load
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shl_load (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shl_load
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_shl_load || defined __stub___shl_load
+choke me
+#endif
+
+int
+main ()
+{
+return shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_func_shl_load=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_shl_load=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5
+$as_echo "$ac_cv_func_shl_load" >&6; }
+if test "x$ac_cv_func_shl_load" = x""yes; then
+  lt_cv_dlopen="shl_load"
+else
+  { $as_echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dld_shl_load=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dld_shl_load=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = x""yes; then
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+  { $as_echo "$as_me:$LINENO: checking for dlopen" >&5
+$as_echo_n "checking for dlopen... " >&6; }
+if test "${ac_cv_func_dlopen+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define dlopen to an innocuous variant, in case <limits.h> declares dlopen.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define dlopen innocuous_dlopen
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char dlopen (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef dlopen
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_dlopen || defined __stub___dlopen
+choke me
+#endif
+
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_func_dlopen=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5
+$as_echo "$ac_cv_func_dlopen" >&6; }
+if test "x$ac_cv_func_dlopen" = x""yes; then
+  lt_cv_dlopen="dlopen"
+else
+  { $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  { $as_echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if test "${ac_cv_lib_svld_dlopen+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_svld_dlopen=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_svld_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = x""yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  { $as_echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if test "${ac_cv_lib_dld_dld_link+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dld_dld_link=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dld_dld_link=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = x""yes; then
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    { $as_echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if test "${lt_cv_dlopen_self+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+         if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line 12179 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL          RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL                DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL                0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW         DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW       RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW     DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW     0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      { $as_echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+         if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line 12275 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL          RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL                DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL                0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW         DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW       RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW     DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW     0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+    else
+      { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+    fi
+    ;;
+  *)
+    { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+    ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+  # Report which library types will actually be built
+  { $as_echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+  { $as_echo "$as_me:$LINENO: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+  { $as_echo "$as_me:$LINENO: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[4-9]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  { $as_echo "$as_me:$LINENO: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+  { $as_echo "$as_me:$LINENO: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  { $as_echo "$as_me:$LINENO: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+        ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then
+  enableval=$enable_optimization;
+       if (test "${enableval}" = "no"); then
+               CFLAGS="$CFLAGS -O0"
+       fi
+
+fi
+
+
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then
+  enableval=$enable_debug;
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_g}" = "yes"); then
+               CFLAGS="$CFLAGS -g"
+       fi
+
+fi
+
+
+# Check whether --enable-test was given.
+if test "${enable_test+set}" = set; then
+  enableval=$enable_test; enable_test=${enableval}
+fi
+
+ if test "${enable_test}" = "yes"; then
+  TEST_TRUE=
+  TEST_FALSE='#'
+else
+  TEST_TRUE='#'
+  TEST_FALSE=
+fi
+
+
+# Check whether --enable-pie was given.
+if test "${enable_pie+set}" = set; then
+  enableval=$enable_pie;
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_pie}" = "yes"); then
+               CFLAGS="$CFLAGS -fPIE"
+               LDFLAGS="$LDFLAGS -pie"
+       fi
+
+fi
+
+
+# Check whether --enable-threads was given.
+if test "${enable_threads+set}" = set; then
+  enableval=$enable_threads; enable_threads=${enableval}
+fi
+
+
+{ $as_echo "$as_me:$LINENO: checking for signalfd" >&5
+$as_echo_n "checking for signalfd... " >&6; }
+if test "${ac_cv_func_signalfd+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define signalfd to an innocuous variant, in case <limits.h> declares signalfd.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define signalfd innocuous_signalfd
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char signalfd (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef signalfd
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char signalfd ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_signalfd || defined __stub___signalfd
+choke me
+#endif
+
+int
+main ()
+{
+return signalfd ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_func_signalfd=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_signalfd=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_signalfd" >&5
+$as_echo "$ac_cv_func_signalfd" >&6; }
+if test "x$ac_cv_func_signalfd" = x""yes; then
+  dummy=yes
+else
+  { { $as_echo "$as_me:$LINENO: error: signalfd support is required" >&5
+$as_echo "$as_me: error: signalfd support is required" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+{ $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+  dummy=yes
+else
+  { { $as_echo "$as_me:$LINENO: error: dynamic linking loader is required" >&5
+$as_echo "$as_me: error: dynamic linking loader is required" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for GLIB" >&5
+$as_echo_n "checking for GLIB... " >&6; }
+
+if test -n "$GLIB_CFLAGS"; then
+    pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.22\"") >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.22") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.22" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GLIB_LIBS"; then
+    pkg_cv_GLIB_LIBS="$GLIB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.22\"") >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.22") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.22" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "glib-2.0 >= 2.22" 2>&1`
+        else
+               GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors "glib-2.0 >= 2.22" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GLIB_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: GLib >= 2.22 is required" >&5
+$as_echo "$as_me: error: GLib >= 2.22 is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: GLib >= 2.22 is required" >&5
+$as_echo "$as_me: error: GLib >= 2.22 is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+       GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
+       GLIB_LIBS=$pkg_cv_GLIB_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+
+
+
+if (test "${enable_threads}" = "yes"); then
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_THREADS 1
+_ACEOF
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for GTHREAD" >&5
+$as_echo_n "checking for GTHREAD... " >&6; }
+
+if test -n "$GTHREAD_CFLAGS"; then
+    pkg_cv_GTHREAD_CFLAGS="$GTHREAD_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gthread-2.0 >= 2.16\"") >&5
+  ($PKG_CONFIG --exists --print-errors "gthread-2.0 >= 2.16") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GTHREAD_CFLAGS=`$PKG_CONFIG --cflags "gthread-2.0 >= 2.16" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GTHREAD_LIBS"; then
+    pkg_cv_GTHREAD_LIBS="$GTHREAD_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gthread-2.0 >= 2.16\"") >&5
+  ($PKG_CONFIG --exists --print-errors "gthread-2.0 >= 2.16") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GTHREAD_LIBS=`$PKG_CONFIG --libs "gthread-2.0 >= 2.16" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GTHREAD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "gthread-2.0 >= 2.16" 2>&1`
+        else
+               GTHREAD_PKG_ERRORS=`$PKG_CONFIG --print-errors "gthread-2.0 >= 2.16" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GTHREAD_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: GThread >= 2.16 is required" >&5
+$as_echo "$as_me: error: GThread >= 2.16 is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: GThread >= 2.16 is required" >&5
+$as_echo "$as_me: error: GThread >= 2.16 is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+       GTHREAD_CFLAGS=$pkg_cv_GTHREAD_CFLAGS
+       GTHREAD_LIBS=$pkg_cv_GTHREAD_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+       GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
+       GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
+fi
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for DBUS" >&5
+$as_echo_n "checking for DBUS... " >&6; }
+
+if test -n "$DBUS_CFLAGS"; then
+    pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.0\"") >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.0" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$DBUS_LIBS"; then
+    pkg_cv_DBUS_LIBS="$DBUS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.0\"") >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.0" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "dbus-1 >= 1.0" 2>&1`
+        else
+               DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors "dbus-1 >= 1.0" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$DBUS_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: D-Bus >= 1.0 is required" >&5
+$as_echo "$as_me: error: D-Bus >= 1.0 is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: D-Bus >= 1.0 is required" >&5
+$as_echo "$as_me: error: D-Bus >= 1.0 is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+       DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS
+       DBUS_LIBS=$pkg_cv_DBUS_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+saved_CFLAGS="$CFLAGS"
+saved_LIBS="$LIBS"
+CFLAGS="$CFLAGS $DBUS_CFLAGS"
+LIBS="$LIBS $DBUS_LIBS"
+{ $as_echo "$as_me:$LINENO: checking for dbus_watch_get_unix_fd in -ldbus-1" >&5
+$as_echo_n "checking for dbus_watch_get_unix_fd in -ldbus-1... " >&6; }
+if test "${ac_cv_lib_dbus_1_dbus_watch_get_unix_fd+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldbus-1  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dbus_watch_get_unix_fd ();
+int
+main ()
+{
+return dbus_watch_get_unix_fd ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dbus_1_dbus_watch_get_unix_fd=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dbus_1_dbus_watch_get_unix_fd=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dbus_1_dbus_watch_get_unix_fd" >&5
+$as_echo "$ac_cv_lib_dbus_1_dbus_watch_get_unix_fd" >&6; }
+if test "x$ac_cv_lib_dbus_1_dbus_watch_get_unix_fd" = x""yes; then
+  dummy=yes
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_DBUS_WATCH_GET_UNIX_FD 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for dbus_connection_can_send_type in -ldbus-1" >&5
+$as_echo_n "checking for dbus_connection_can_send_type in -ldbus-1... " >&6; }
+if test "${ac_cv_lib_dbus_1_dbus_connection_can_send_type+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldbus-1  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dbus_connection_can_send_type ();
+int
+main ()
+{
+return dbus_connection_can_send_type ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dbus_1_dbus_connection_can_send_type=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dbus_1_dbus_connection_can_send_type=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dbus_1_dbus_connection_can_send_type" >&5
+$as_echo "$ac_cv_lib_dbus_1_dbus_connection_can_send_type" >&6; }
+if test "x$ac_cv_lib_dbus_1_dbus_connection_can_send_type" = x""yes; then
+  dummy=yes
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_DBUS_CONNECTION_CAN_SEND_TYPE 1
+_ACEOF
+
+fi
+
+CFLAGS="$saved_CFLAGS"
+LIBS="$saved_LIBS"
+
+
+
+
+# Check whether --with-dbusconfdir was given.
+if test "${with_dbusconfdir+set}" = set; then
+  withval=$with_dbusconfdir; path_dbusconf=${withval}
+else
+  path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"
+fi
+
+if (test -z "${path_dbusconf}"); then
+       DBUS_CONFDIR="${sysconfdir}/dbus-1/system.d"
+else
+       DBUS_CONFDIR="${path_dbusconf}/dbus-1/system.d"
+fi
+
+
+
+# Check whether --with-dbusdatadir was given.
+if test "${with_dbusdatadir+set}" = set; then
+  withval=$with_dbusdatadir; path_dbusdata=${withval}
+else
+  path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"
+fi
+
+if (test -z "${path_dbusdata}"); then
+       DBUS_DATADIR="${datadir}/dbus-1/system-services"
+else
+       DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services"
+fi
+
+
+
+# Check whether --with-systemdunitdir was given.
+if test "${with_systemdunitdir+set}" = set; then
+  withval=$with_systemdunitdir; path_systemdunit=${withval}
+else
+  path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"
+fi
+
+if (test -n "${path_systemdunit}"); then
+       SYSTEMD_UNITDIR="${path_systemdunit}"
+
+fi
+ if test -n "${path_systemdunit}"; then
+  SYSTEMD_TRUE=
+  SYSTEMD_FALSE='#'
+else
+  SYSTEMD_TRUE='#'
+  SYSTEMD_FALSE=
+fi
+
+
+# Check whether --enable-capng was given.
+if test "${enable_capng+set}" = set; then
+  enableval=$enable_capng; enable_capng=${enableval}
+fi
+
+if (test "${enable_capng}" = "yes"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CAPNG" >&5
+$as_echo_n "checking for CAPNG... " >&6; }
+
+if test -n "$CAPNG_CFLAGS"; then
+    pkg_cv_CAPNG_CFLAGS="$CAPNG_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcap-ng\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcap-ng") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CAPNG_CFLAGS=`$PKG_CONFIG --cflags "libcap-ng" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$CAPNG_LIBS"; then
+    pkg_cv_CAPNG_LIBS="$CAPNG_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcap-ng\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcap-ng") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CAPNG_LIBS=`$PKG_CONFIG --libs "libcap-ng" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               CAPNG_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcap-ng" 2>&1`
+        else
+               CAPNG_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcap-ng" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$CAPNG_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: Capabilities library is required" >&5
+$as_echo "$as_me: error: Capabilities library is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: Capabilities library is required" >&5
+$as_echo "$as_me: error: Capabilities library is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+       CAPNG_CFLAGS=$pkg_cv_CAPNG_CFLAGS
+       CAPNG_LIBS=$pkg_cv_CAPNG_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CAPNG 1
+_ACEOF
+
+fi
+
+# Check whether --enable-udev was given.
+if test "${enable_udev+set}" = set; then
+  enableval=$enable_udev; enable_udev=${enableval}
+fi
+
+if (test "${enable_udev}" != "no"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for UDEV" >&5
+$as_echo_n "checking for UDEV... " >&6; }
+
+if test -n "$UDEV_CFLAGS"; then
+    pkg_cv_UDEV_CFLAGS="$UDEV_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libudev >= 143\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libudev >= 143") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_UDEV_CFLAGS=`$PKG_CONFIG --cflags "libudev >= 143" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$UDEV_LIBS"; then
+    pkg_cv_UDEV_LIBS="$UDEV_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libudev >= 143\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libudev >= 143") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_UDEV_LIBS=`$PKG_CONFIG --libs "libudev >= 143" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               UDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libudev >= 143" 2>&1`
+        else
+               UDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors "libudev >= 143" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$UDEV_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: libudev >= 143 is required" >&5
+$as_echo "$as_me: error: libudev >= 143 is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: libudev >= 143 is required" >&5
+$as_echo "$as_me: error: libudev >= 143 is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+       UDEV_CFLAGS=$pkg_cv_UDEV_CFLAGS
+       UDEV_LIBS=$pkg_cv_UDEV_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       enable_udev="yes"
+fi
+       UDEV_DATADIR="`$PKG_CONFIG --variable=udevdir udev`"
+       if (test -z "${UDEV_DATADIR}"); then
+               UDEV_DATADIR="${sysconfdir}/udev/rules.d"
+       else
+               UDEV_DATADIR="${UDEV_DATADIR}/rules.d"
+       fi
+
+fi
+
+
+ if test "${enable_udev}" = "yes"; then
+  UDEV_TRUE=
+  UDEV_FALSE='#'
+else
+  UDEV_TRUE='#'
+  UDEV_FALSE=
+fi
+
+
+# Check whether --enable-tools was given.
+if test "${enable_tools+set}" = set; then
+  enableval=$enable_tools; enable_tools=${enableval}
+fi
+
+if (test "${enable_tools}" = "yes"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for USB" >&5
+$as_echo_n "checking for USB... " >&6; }
+
+if test -n "$USB_CFLAGS"; then
+    pkg_cv_USB_CFLAGS="$USB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libusb-1.0\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libusb-1.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_USB_CFLAGS=`$PKG_CONFIG --cflags "libusb-1.0" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$USB_LIBS"; then
+    pkg_cv_USB_LIBS="$USB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libusb-1.0\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libusb-1.0") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_USB_LIBS=`$PKG_CONFIG --libs "libusb-1.0" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               USB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libusb-1.0" 2>&1`
+        else
+               USB_PKG_ERRORS=`$PKG_CONFIG --print-errors "libusb-1.0" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$USB_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: USB library is required" >&5
+$as_echo "$as_me: error: USB library is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: USB library is required" >&5
+$as_echo "$as_me: error: USB library is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+       USB_CFLAGS=$pkg_cv_USB_CFLAGS
+       USB_LIBS=$pkg_cv_USB_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+
+
+fi
+ if test "${enable_tools}" = "yes"; then
+  TOOLS_TRUE=
+  TOOLS_FALSE='#'
+else
+  TOOLS_TRUE='#'
+  TOOLS_FALSE=
+fi
+
+
+# Check whether --enable-atmodem was given.
+if test "${enable_atmodem+set}" = set; then
+  enableval=$enable_atmodem; enable_atmodem=${enableval}
+fi
+
+ if test "${enable_atmodem}" != "no"; then
+  ATMODEM_TRUE=
+  ATMODEM_FALSE='#'
+else
+  ATMODEM_TRUE='#'
+  ATMODEM_FALSE=
+fi
+
+
+# Check whether --enable-cdmamodem was given.
+if test "${enable_cdmamodem+set}" = set; then
+  enableval=$enable_cdmamodem; enable_cdmamodem=${enableval}
+fi
+
+ if test "${enable_cdmamodem}" != "no"; then
+  CDMAMODEM_TRUE=
+  CDMAMODEM_FALSE='#'
+else
+  CDMAMODEM_TRUE='#'
+  CDMAMODEM_FALSE=
+fi
+
+
+# Check whether --enable-phonesim was given.
+if test "${enable_phonesim+set}" = set; then
+  enableval=$enable_phonesim; enable_phonesim=${enableval}
+fi
+
+ if test "${enable_phonesim}" != "no" &&
+                                       test "${enable_atmodem}" != "no"; then
+  PHONESIM_TRUE=
+  PHONESIM_FALSE='#'
+else
+  PHONESIM_TRUE='#'
+  PHONESIM_FALSE=
+fi
+
+
+# Check whether --enable-isimodem was given.
+if test "${enable_isimodem+set}" = set; then
+  enableval=$enable_isimodem; enable_isimodem=${enableval}
+fi
+
+ if test "${enable_isimodem}" != "no"; then
+  ISIMODEM_TRUE=
+  ISIMODEM_FALSE='#'
+else
+  ISIMODEM_TRUE='#'
+  ISIMODEM_FALSE=
+fi
+
+
+# Check whether --enable-bluetooth was given.
+if test "${enable_bluetooth+set}" = set; then
+  enableval=$enable_bluetooth; enable_bluetooth=${enableval}
+fi
+
+if (test "${enable_bluetooth}" != "no"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for BLUEZ" >&5
+$as_echo_n "checking for BLUEZ... " >&6; }
+
+if test -n "$BLUEZ_CFLAGS"; then
+    pkg_cv_BLUEZ_CFLAGS="$BLUEZ_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"bluez >= 4.30\"") >&5
+  ($PKG_CONFIG --exists --print-errors "bluez >= 4.30") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_BLUEZ_CFLAGS=`$PKG_CONFIG --cflags "bluez >= 4.30" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$BLUEZ_LIBS"; then
+    pkg_cv_BLUEZ_LIBS="$BLUEZ_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"bluez >= 4.30\"") >&5
+  ($PKG_CONFIG --exists --print-errors "bluez >= 4.30") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_BLUEZ_LIBS=`$PKG_CONFIG --libs "bluez >= 4.30" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               BLUEZ_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "bluez >= 4.30" 2>&1`
+        else
+               BLUEZ_PKG_ERRORS=`$PKG_CONFIG --print-errors "bluez >= 4.30" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$BLUEZ_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: Bluetooth library >= 4.30 is required" >&5
+$as_echo "$as_me: error: Bluetooth library >= 4.30 is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: Bluetooth library >= 4.30 is required" >&5
+$as_echo "$as_me: error: Bluetooth library >= 4.30 is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+       BLUEZ_CFLAGS=$pkg_cv_BLUEZ_CFLAGS
+       BLUEZ_LIBS=$pkg_cv_BLUEZ_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+fi
+
+
+ if test "${enable_bluetooth}" != "no"; then
+  BLUETOOTH_TRUE=
+  BLUETOOTH_FALSE='#'
+else
+  BLUETOOTH_TRUE='#'
+  BLUETOOTH_FALSE=
+fi
+
+
+
+# Check whether --with-provisiondb was given.
+if test "${with_provisiondb+set}" = set; then
+  withval=$with_provisiondb; path_provisiondb=${withval}
+fi
+
+
+# Check whether --enable-provision was given.
+if test "${enable_provision+set}" = set; then
+  enableval=$enable_provision; enable_provision=${enableval}
+fi
+
+if (test "${enable_provision}" != "no"); then
+       if (test -n "${path_provisiondb}"); then
+
+cat >>confdefs.h <<_ACEOF
+#define PROVIDER_DATABASE "${path_provisiondb}"
+_ACEOF
+
+       else
+               { $as_echo "$as_me:$LINENO: checking for mobile-broadband-provider-info" >&5
+$as_echo_n "checking for mobile-broadband-provider-info... " >&6; }
+               if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"mobile-broadband-provider-info\"") >&5
+  ($PKG_CONFIG --exists --print-errors "mobile-broadband-provider-info") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  if test -n "$PROVIDER_DATABASE"; then
+    pkg_cv_PROVIDER_DATABASE="$PROVIDER_DATABASE"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"mobile-broadband-provider-info\"") >&5
+  ($PKG_CONFIG --exists --print-errors "mobile-broadband-provider-info") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_PROVIDER_DATABASE=`$PKG_CONFIG --variable=database "mobile-broadband-provider-info" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define PROVIDER_DATABASE "$pkg_cv_PROVIDER_DATABASE"
+_ACEOF
+
+                       { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { { $as_echo "$as_me:$LINENO: error: Mobile broadband provider database is required" >&5
+$as_echo "$as_me: error: Mobile broadband provider database is required" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+       fi
+fi
+ if test "${enable_provision}" != "no"; then
+  PROVISION_TRUE=
+  PROVISION_FALSE='#'
+else
+  PROVISION_TRUE='#'
+  PROVISION_FALSE=
+fi
+
+
+# Check whether --enable-datafiles was given.
+if test "${enable_datafiles+set}" = set; then
+  enableval=$enable_datafiles; enable_datafiles=${enableval}
+fi
+
+ if test "${enable_datafiles}" != "no"; then
+  DATAFILES_TRUE=
+  DATAFILES_FALSE='#'
+else
+  DATAFILES_TRUE='#'
+  DATAFILES_FALSE=
+fi
+
+
+if (test "${prefix}" = "NONE"); then
+               if (test "$localstatedir" = '${prefix}/var'); then
+               localstatedir='/var'
+
+       fi
+
+       prefix="${ac_default_prefix}"
+fi
+
+if (test "$localstatedir" = '${prefix}/var'); then
+       storagedir="${prefix}/var/lib/ofono"
+else
+       storagedir="${localstatedir}/lib/ofono"
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define STORAGEDIR "${storagedir}"
+_ACEOF
+
+
+if (test "$sysconfdir" = '${prefix}/etc'); then
+       configdir="${prefix}/etc/ofono"
+else
+       configdir="${sysconfdir}/ofono"
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIGDIR "${configdir}"
+_ACEOF
+
+
+ac_config_files="$ac_config_files Makefile include/version.h src/ofono.service ofono.pc"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    test "x$cache_file" != "x/dev/null" &&
+      { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+    cat confcache >$cache_file
+  else
+    { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+  am__EXEEXT_TRUE=
+  am__EXEEXT_FALSE='#'
+else
+  am__EXEEXT_TRUE='#'
+  am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${TEST_TRUE}" && test -z "${TEST_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"TEST\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"TEST\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${SYSTEMD_TRUE}" && test -z "${SYSTEMD_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"SYSTEMD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"SYSTEMD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${UDEV_TRUE}" && test -z "${UDEV_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"UDEV\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"UDEV\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${TOOLS_TRUE}" && test -z "${TOOLS_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"TOOLS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"TOOLS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${ATMODEM_TRUE}" && test -z "${ATMODEM_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"ATMODEM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"ATMODEM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${CDMAMODEM_TRUE}" && test -z "${CDMAMODEM_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"CDMAMODEM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"CDMAMODEM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${PHONESIM_TRUE}" && test -z "${PHONESIM_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"PHONESIM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"PHONESIM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${ISIMODEM_TRUE}" && test -z "${ISIMODEM_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"ISIMODEM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"ISIMODEM\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${BLUETOOTH_TRUE}" && test -z "${BLUETOOTH_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"BLUETOOTH\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"BLUETOOTH\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${PROVISION_TRUE}" && test -z "${PROVISION_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"PROVISION\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"PROVISION\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${DATAFILES_TRUE}" && test -z "${DATAFILES_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"DATAFILES\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"DATAFILES\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+: ${CONFIG_STATUS=./config.status}
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -p'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -p'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -p'
+  fi
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+       test -d "$1/.";
+      else
+       case $1 in
+       -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by ofono $as_me 1.4, which was
+generated by GNU Autoconf 2.63.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTION]... [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_version="\\
+ofono config.status 1.4
+configured by $0, generated by GNU Autoconf 2.63,
+  with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    CONFIG_FILES="$CONFIG_FILES '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    { $as_echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { $as_echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`'
+macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`'
+host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`'
+host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`'
+host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`'
+build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`'
+build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`'
+build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`'
+SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`'
+Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`'
+GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`'
+EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`'
+FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`'
+LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`'
+NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`'
+LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`'
+exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`'
+AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`'
+GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`'
+SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`'
+ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`'
+need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`'
+LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`'
+libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`'
+fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`'
+version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`'
+striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+AR \
+AR_FLAGS \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+SHELL \
+ECHO \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_wl \
+lt_prog_compiler_pic \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_flag_spec_ld \
+hardcode_libdir_separator \
+fix_srcfile_path \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+finish_eval \
+old_striplib \
+striplib; do
+    case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+    case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\$0 --fallback-echo"')  lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\`
+  ;;
+esac
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+    "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "include/version.h") CONFIG_FILES="$CONFIG_FILES include/version.h" ;;
+    "src/ofono.service") CONFIG_FILES="$CONFIG_FILES src/ofono.service" ;;
+    "ofono.pc") CONFIG_FILES="$CONFIG_FILES ofono.pc" ;;
+
+  *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp=
+  trap 'exit_status=$?
+  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} ||
+{
+   $as_echo "$as_me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr='\r'
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\).*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\).*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+  || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5
+$as_echo "$as_me: error: could not setup config files machinery" >&2;}
+   { (exit 1); exit 1; }; }
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[    ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_t=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_t"; then
+    break
+  elif $ac_last_try; then
+    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;}
+   { (exit 1); exit 1; }; }
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5
+$as_echo "$as_me: error: could not setup config headers machinery" >&2;}
+   { (exit 1); exit 1; }; }
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5
+$as_echo "$as_me: error: invalid tag $ac_tag" >&2;}
+   { (exit 1); exit 1; }; };;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+   { (exit 1); exit 1; }; };;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      ac_file_inputs="$ac_file_inputs '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:$LINENO: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$tmp/stdin" \
+      || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; } ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  { as_dir="$ac_dir"
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
+   { (exit 1); exit 1; }; }; }
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+    s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+  || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&2;}
+
+  rm -f "$tmp/stdin"
+  case $ac_file in
+  -) cat "$tmp/out" && rm -f "$tmp/out";;
+  *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+  esac \
+  || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
+    } >"$tmp/config.h" \
+      || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+    if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$tmp/config.h" "$ac_file" \
+       || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
+      || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5
+$as_echo "$as_me: error: could not create -" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$_am_arg" : 'X\(//\)[^/]' \| \
+        X"$_am_arg" : 'X\(//\)$' \| \
+        X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+  :C)  { $as_echo "$as_me:$LINENO: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+  # Autoconf 2.62 quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named `Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$mf" : 'X\(//\)[^/]' \| \
+        X"$mf" : 'X\(//\)$' \| \
+        X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running `make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # When using ansi2knr, U may be empty or an underscore; expand it
+    U=`sed -n 's/^U = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+        sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$file" : 'X\(//\)[^/]' \| \
+        X"$file" : 'X\(//\)$' \| \
+        X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      { as_dir=$dirpart/$fdir
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
+   { (exit 1); exit 1; }; }; }
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+ ;;
+    "libtool":C)
+
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008 Free Software Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that does not interpret backslashes.
+ECHO=$lt_ECHO
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking.  This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+    || (rm -f "$cfgfile"; exit 1)
+
+  case $xsi_shell in
+  yes)
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+  case ${1} in
+    */*) func_dirname_result="${1%/*}${2}" ;;
+    *  ) func_dirname_result="${3}" ;;
+  esac
+}
+
+# func_basename file
+func_basename ()
+{
+  func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+#   dirname:  Compute the dirname of FILE.  If nonempty,
+#             add APPEND to the result, otherwise set result
+#             to NONDIR_REPLACEMENT.
+#             value returned in "$func_dirname_result"
+#   basename: Compute filename of FILE.
+#             value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+  case ${1} in
+    */*) func_dirname_result="${1%/*}${2}" ;;
+    *  ) func_dirname_result="${3}" ;;
+  esac
+  func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+  # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+  # positional parameters, so assign one to ordinary parameter first.
+  func_stripname_result=${3}
+  func_stripname_result=${func_stripname_result#"${1}"}
+  func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+  func_opt_split_opt=${1%%=*}
+  func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+  case ${1} in
+    *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+    *)    func_lo2o_result=${1} ;;
+  esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+  func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+  func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+  func_len_result=${#1}
+}
+
+_LT_EOF
+    ;;
+  *) # Bourne compatible functions.
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+  # Extract subdirectory from the argument.
+  func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+  if test "X$func_dirname_result" = "X${1}"; then
+    func_dirname_result="${3}"
+  else
+    func_dirname_result="$func_dirname_result${2}"
+  fi
+}
+
+# func_basename file
+func_basename ()
+{
+  func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+  case ${2} in
+    .*) func_stripname_result=`$ECHO "X${3}" \
+           | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+    *)  func_stripname_result=`$ECHO "X${3}" \
+           | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+  esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+  func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+  func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+  func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+  func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+  func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+  func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+  yes)
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+  eval "$1+=\$2"
+}
+_LT_EOF
+    ;;
+  *)
+    cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+  eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+    ;;
+  esac
+
+
+  sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+    || (rm -f "$cfgfile"; exit 1)
+
+  mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+
+ ;;
+
+  esac
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..50ca75a
--- /dev/null
@@ -0,0 +1,254 @@
+AC_PREREQ(2.60)
+AC_INIT(ofono, 1.4)
+
+AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
+AM_CONFIG_HEADER(config.h)
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_MAINTAINER_MODE
+
+AC_PREFIX_DEFAULT(/usr/local)
+
+PKG_PROG_PKG_CONFIG
+
+COMPILER_FLAGS
+
+AC_LANG_C
+
+AC_C_RESTRICT
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+AM_PROG_MKDIR_P
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
+                       [disable code optimization through compiler]), [
+       if (test "${enableval}" = "no"); then
+               CFLAGS="$CFLAGS -O0"
+       fi
+])
+
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
+                       [enable compiling with debugging information]), [
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_g}" = "yes"); then
+               CFLAGS="$CFLAGS -g"
+       fi
+])
+
+AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
+               [enable test/example scripts]), [enable_test=${enableval}])
+AM_CONDITIONAL(TEST, test "${enable_test}" = "yes")
+
+AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
+                       [enable position independent executables flag]), [
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_pie}" = "yes"); then
+               CFLAGS="$CFLAGS -fPIE"
+               LDFLAGS="$LDFLAGS -pie"
+       fi
+])
+
+AC_ARG_ENABLE(threads, AC_HELP_STRING([--enable-threads],
+               [enable threading support]), [enable_threads=${enableval}])
+
+AC_CHECK_FUNC(signalfd, dummy=yes,
+                       AC_MSG_ERROR(signalfd support is required))
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+                       AC_MSG_ERROR(dynamic linking loader is required))
+
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.22, dummy=yes,
+                               AC_MSG_ERROR(GLib >= 2.22 is required))
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+if (test "${enable_threads}" = "yes"); then
+       AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
+       PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
+                               AC_MSG_ERROR(GThread >= 2.16 is required))
+       GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
+       GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
+fi
+
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0, dummy=yes,
+                               AC_MSG_ERROR(D-Bus >= 1.0 is required))
+saved_CFLAGS="$CFLAGS"
+saved_LIBS="$LIBS"
+CFLAGS="$CFLAGS $DBUS_CFLAGS"
+LIBS="$LIBS $DBUS_LIBS"
+AC_CHECK_LIB(dbus-1, dbus_watch_get_unix_fd, dummy=yes,
+       AC_DEFINE(NEED_DBUS_WATCH_GET_UNIX_FD, 1,
+               [Define to 1 if you need the dbus_watch_get_unix_fd() function.]))
+AC_CHECK_LIB(dbus-1, dbus_connection_can_send_type, dummy=yes,
+       AC_DEFINE(NEED_DBUS_CONNECTION_CAN_SEND_TYPE, 1,
+               [Define to 1 if you need the dbus_connection_can_send_type() function.]
+))
+CFLAGS="$saved_CFLAGS"
+LIBS="$saved_LIBS"
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+AC_ARG_WITH(dbusconfdir, AC_HELP_STRING([--with-dbusconfdir=PATH],
+       [path to D-Bus config directory]), [path_dbusconf=${withval}],
+               [path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"])
+if (test -z "${path_dbusconf}"); then
+       DBUS_CONFDIR="${sysconfdir}/dbus-1/system.d"
+else
+       DBUS_CONFDIR="${path_dbusconf}/dbus-1/system.d"
+fi
+AC_SUBST(DBUS_CONFDIR)
+
+AC_ARG_WITH(dbusdatadir, AC_HELP_STRING([--with-dbusdatadir=PATH],
+       [path to D-Bus data directory]), [path_dbusdata=${withval}],
+               [path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"])
+if (test -z "${path_dbusdata}"); then
+       DBUS_DATADIR="${datadir}/dbus-1/system-services"
+else
+       DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services"
+fi
+AC_SUBST(DBUS_DATADIR)
+
+AC_ARG_WITH([systemdunitdir], AC_HELP_STRING([--with-systemdunitdir=DIR],
+       [path to systemd service directory]), [path_systemdunit=${withval}],
+               [path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"])
+if (test -n "${path_systemdunit}"); then
+       SYSTEMD_UNITDIR="${path_systemdunit}"
+       AC_SUBST(SYSTEMD_UNITDIR)
+fi
+AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}")
+
+AC_ARG_ENABLE(capng, AC_HELP_STRING([--enable-capng],
+               [enable capabilities support]), [enable_capng=${enableval}])
+if (test "${enable_capng}" = "yes"); then
+       PKG_CHECK_MODULES(CAPNG, libcap-ng, dummy=yes,
+                               AC_MSG_ERROR(Capabilities library is required))
+       AC_SUBST(CAPNG_CFLAGS)
+       AC_SUBST(CAPNG_LIBS)
+       AC_DEFINE(HAVE_CAPNG, 1, [Define to 1 if you have capabilities library.])
+fi
+
+AC_ARG_ENABLE(udev, AC_HELP_STRING([--disable-udev],
+                       [don't use udev support even if available]),
+                                               [enable_udev=${enableval}])
+if (test "${enable_udev}" != "no"); then
+       PKG_CHECK_MODULES(UDEV, libudev >= 143, [enable_udev="yes"],
+                               AC_MSG_ERROR(libudev >= 143 is required))
+       UDEV_DATADIR="`$PKG_CONFIG --variable=udevdir udev`"
+       if (test -z "${UDEV_DATADIR}"); then
+               UDEV_DATADIR="${sysconfdir}/udev/rules.d"
+       else
+               UDEV_DATADIR="${UDEV_DATADIR}/rules.d"
+       fi
+       AC_SUBST(UDEV_DATADIR)
+fi
+AC_SUBST(UDEV_CFLAGS)
+AC_SUBST(UDEV_LIBS)
+AM_CONDITIONAL(UDEV, test "${enable_udev}" = "yes")
+
+AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools],
+               [enable testing tools]), [enable_tools=${enableval}])
+if (test "${enable_tools}" = "yes"); then
+       PKG_CHECK_MODULES(USB, libusb-1.0, dummy=yes,
+                               AC_MSG_ERROR(USB library is required))
+       AC_SUBST(USB_CFLAGS)
+       AC_SUBST(USB_LIBS)
+fi
+AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes")
+
+AC_ARG_ENABLE(atmodem, AC_HELP_STRING([--disable-atmodem],
+                               [disable ETSI AT modem support]),
+                                       [enable_atmodem=${enableval}])
+AM_CONDITIONAL(ATMODEM, test "${enable_atmodem}" != "no")
+
+AC_ARG_ENABLE(cdmamodem, AC_HELP_STRING([--disable-cdmamodem],
+                               [disable CDMA AT modem support]),
+                                       [enable_cdmamodem=${enableval}])
+AM_CONDITIONAL(CDMAMODEM, test "${enable_cdmamodem}" != "no")
+
+AC_ARG_ENABLE(phonesim, AC_HELP_STRING([--disable-phonesim],
+                               [disable Phone simulator support]),
+                                       [enable_phonesim=${enableval}])
+AM_CONDITIONAL(PHONESIM, test "${enable_phonesim}" != "no" &&
+                                       test "${enable_atmodem}" != "no")
+
+AC_ARG_ENABLE(isimodem, AC_HELP_STRING([--disable-isimodem],
+                               [disable PhoNet/ISI modem support]),
+                                       [enable_isimodem=${enableval}])
+AM_CONDITIONAL(ISIMODEM, test "${enable_isimodem}" != "no")
+
+AC_ARG_ENABLE(bluetooth, AC_HELP_STRING([--disable-bluetooth],
+                               [disable Bluetooth modem support]),
+                                       [enable_bluetooth=${enableval}])
+if (test "${enable_bluetooth}" != "no"); then
+       PKG_CHECK_MODULES(BLUEZ, bluez >= 4.30, dummy=yes,
+                       AC_MSG_ERROR(Bluetooth library >= 4.30 is required))
+fi
+AC_SUBST(BLUEZ_CFLAGS)
+AC_SUBST(BLUEZ_LIBS)
+AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
+
+AC_ARG_WITH([provisiondb], AC_HELP_STRING([--with-provisiondb=FILE],
+       [location of provision database]), [path_provisiondb=${withval}])
+
+AC_ARG_ENABLE(provision, AC_HELP_STRING([--disable-provision],
+                               [disable provisioning suport]),
+                                       [enable_provision=${enableval}])
+if (test "${enable_provision}" != "no"); then
+       if (test -n "${path_provisiondb}"); then
+               AC_DEFINE_UNQUOTED(PROVIDER_DATABASE, "${path_provisiondb}",
+                                               [Mobile provider database])
+       else
+               AC_MSG_CHECKING([for mobile-broadband-provider-info])
+               PKG_CHECK_EXISTS(mobile-broadband-provider-info,
+                       _PKG_CONFIG(PROVIDER_DATABASE, [variable=database],
+                                       [mobile-broadband-provider-info])
+                       AC_DEFINE_UNQUOTED(PROVIDER_DATABASE,
+                                               "$pkg_cv_PROVIDER_DATABASE",
+                                               [Mobile provider database])
+                       AC_MSG_RESULT([yes]),
+                       AC_MSG_ERROR(Mobile broadband provider database is required))
+       fi
+fi
+AM_CONDITIONAL(PROVISION, test "${enable_provision}" != "no")
+
+AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
+                       [don't install configuration and data files]),
+                                       [enable_datafiles=${enableval}])
+AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
+
+if (test "${prefix}" = "NONE"); then
+       dnl no prefix and no localstatedir, so default to /var
+       if (test "$localstatedir" = '${prefix}/var'); then
+               AC_SUBST([localstatedir], ['/var'])
+       fi
+
+       prefix="${ac_default_prefix}"
+fi
+
+if (test "$localstatedir" = '${prefix}/var'); then
+       storagedir="${prefix}/var/lib/ofono"
+else
+       storagedir="${localstatedir}/lib/ofono"
+fi
+AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
+                       [Directory for the storage files])
+
+if (test "$sysconfdir" = '${prefix}/etc'); then
+       configdir="${prefix}/etc/ofono"
+else
+       configdir="${sysconfdir}/ofono"
+fi
+AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}",
+                       [Directory for the configuration files])
+
+AC_OUTPUT(Makefile include/version.h src/ofono.service ofono.pc)
diff --git a/depcomp b/depcomp
new file mode 100755 (executable)
index 0000000..df8eea7
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,630 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free
+# Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by `PROGRAMS ARGS'.
+  object      Object file output by `PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputing dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit $?
+    ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+   # This is just like dashmstdout with a different argument.
+   dashmflag=-xM
+   depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+   # This is just like msvisualcpp but w/o cygpath translation.
+   # Just convert the backslash-escaped backslashes to single forward
+   # slashes to satisfy depend.m4
+   cygpath_u="sed s,\\\\\\\\,/,g"
+   depmode=msvisualcpp
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am.  Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+  for arg
+  do
+    case $arg in
+    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+    *)  set fnord "$@" "$arg" ;;
+    esac
+    shift # fnord
+    shift # $arg
+  done
+  "$@"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+  tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'.  On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like `#:fec' to the end of the
+    # dependency line.
+    tr ' ' '
+' < "$tmpdepfile" \
+    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+    tr '
+' ' ' >> "$depfile"
+    echo >> "$depfile"
+
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' '
+' < "$tmpdepfile" \
+   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+   >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts `$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+  test "x$dir" = "x$object" && dir=
+  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$base.u
+    tmpdepfile3=$dir.libs/$base.u
+    "$@" -Wc,-M
+  else
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$dir$base.u
+    tmpdepfile3=$dir$base.u
+    "$@" -M
+  fi
+  stat=$?
+
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form `foo.o: dependent.h'.
+    # Do two passes, one to just change these to
+    # `$object: dependent.h' and one to simply `dependent.h:'.
+    sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+    # That's a tab and a space in the [].
+    sed -e 's,^.*\.[a-z]*:[     ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+icc)
+  # Intel's C compiler understands `-MD -MF file'.  However on
+  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+  # ICC 7.0 will fill foo.d with something like
+  #    foo.o: sub/foo.c
+  #    foo.o: sub/foo.h
+  # which is wrong.  We want:
+  #    sub/foo.o: sub/foo.c
+  #    sub/foo.o: sub/foo.h
+  #    sub/foo.c:
+  #    sub/foo.h:
+  # ICC 7.1 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using \ :
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+    sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp2)
+  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+  # compilers, which have integrated preprocessors.  The correct option
+  # to use with these is +Maked; it writes dependencies to a file named
+  # 'foo.d', which lands next to the object file, wherever that
+  # happens to be.
+  # Much of this is similar to the tru64 case; see comments there.
+  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+  test "x$dir" = "x$object" && dir=
+  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir.libs/$base.d
+    "$@" -Wc,+Maked
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    "$@" +Maked
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+     rm -f "$tmpdepfile1" "$tmpdepfile2"
+     exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+    # Add `dependent.h:' lines.
+    sed -ne '2,${
+              s/^ *//
+              s/ \\*$//
+              s/$/:/
+              p
+            }' "$tmpdepfile" >> "$depfile"
+  else
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile" "$tmpdepfile2"
+  ;;
+
+tru64)
+   # The Tru64 compiler uses -MD to generate dependencies as a side
+   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+   # dependencies in `foo.d' instead, so we check for that too.
+   # Subdirectories are respected.
+   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+   test "x$dir" = "x$object" && dir=
+   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+   if test "$libtool" = yes; then
+      # With Tru64 cc, shared objects can also be used to make a
+      # static library.  This mechanism is used in libtool 1.4 series to
+      # handle both shared and static libraries in a single compilation.
+      # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+      #
+      # With libtool 1.5 this exception was removed, and libtool now
+      # generates 2 separate objects for the 2 libraries.  These two
+      # compilations output dependencies in $dir.libs/$base.o.d and
+      # in $dir$base.o.d.  We have to check for both files, because
+      # one of the two compilations can be disabled.  We should prefer
+      # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+      # automatically cleaned when .libs/ is deleted, while ignoring
+      # the former would cause a distcleancheck panic.
+      tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4
+      tmpdepfile2=$dir$base.o.d          # libtool 1.5
+      tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5
+      tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504
+      "$@" -Wc,-MD
+   else
+      tmpdepfile1=$dir$base.o.d
+      tmpdepfile2=$dir$base.d
+      tmpdepfile3=$dir$base.d
+      tmpdepfile4=$dir$base.d
+      "$@" -MD
+   fi
+
+   stat=$?
+   if test $stat -eq 0; then :
+   else
+      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+      exit $stat
+   fi
+
+   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+   do
+     test -f "$tmpdepfile" && break
+   done
+   if test -f "$tmpdepfile"; then
+      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+      # That's a tab and a space in the [].
+      sed -e 's,^.*\.[a-z]*:[   ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+   else
+      echo "#dummy" > "$depfile"
+   fi
+   rm -f "$tmpdepfile"
+   ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for `:'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+  "$@" $dashmflag |
+    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no eat=no
+  for arg
+  do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    if test $eat = yes; then
+      eat=no
+      continue
+    fi
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -arch)
+      eat=yes ;;
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix=`echo "$object" | sed 's/^.*\././'`
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E |
+    sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+       -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+    sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+       set fnord "$@"
+       shift
+       shift
+       ;;
+    *)
+       set fnord "$@" "$arg"
+       shift
+       shift
+       ;;
+    esac
+  done
+  "$@" -E 2>/dev/null |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::     \1 \\:p' >> "$depfile"
+  echo "       " >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvcmsys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/doc/audio-settings-api.txt b/doc/audio-settings-api.txt
new file mode 100644 (file)
index 0000000..49d7909
--- /dev/null
@@ -0,0 +1,31 @@
+Audio settings hierarchy
+========================
+
+Service                org.ofono
+Interface      org.ofono.AudioSettings
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns all audio settings properties. See the
+                       properties section for available properties.
+
+Signals                PropertyChanged(string property, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Active [readonly] [EXPERIMENTAL]
+
+                       Indicates if an audio PCM stream is active or not.
+
+                       This is not supported by all modems. Only modems
+                       where the audio data can be routed to the host
+                       application processor will support this.
+
+               string Mode [readonly, optional] [EXPERIMENTAL]
+
+                       Indicates the audio mode setting.
+
+                       This is highly modem specific audio string. Every
+                       modem might use different ones.
diff --git a/doc/call-barring-api.txt b/doc/call-barring-api.txt
new file mode 100644 (file)
index 0000000..a80bf61
--- /dev/null
@@ -0,0 +1,87 @@
+Call Barring hierarchy
+======================
+
+Service                org.ofono
+Interface      org.ofono.CallBarring
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Contains the properties for this object.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+
+               void ChangePassword(string old_password, string new_password)
+
+                       Register new network password for the barring
+                       services.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void DisableAll(string password)
+
+                       Disables all call barrings.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void DisableAllIncoming(string password)
+
+                       Disables barrings for incoming calls.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void DisableAllOutgoing(string password)
+
+                       Disables barrings for outgoing calls.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void SetProperty(string property, variant value, string pin2)
+
+                       Sets the given property value to that specified in
+                       call parameter.  For all properties, the password
+                       (typically PIN2) must be provided.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+Properties     string VoiceIncoming [readwrite]
+
+                       Contains the value of the barrings for the incoming
+                       voice calls. The possible values are:
+                       - "always" bar all incoming voice calls
+                       - "whenroaming" bar incoming voice calls when roaming,
+                       - "disabled" if no barring is active
+
+               string VoiceOutgoing [readwrite]
+
+                       Contains the value of the barrings for the outgoing
+                       voice calls. The possible values are:
+                       - "all" bar all outgoing calls
+                       - "international" bar all outgoing international calls
+                       - "internationalnothome" bar all outgoing
+                         international calls except to home country
+                       - "disabled" if no barring is active
diff --git a/doc/call-forwarding-api.txt b/doc/call-forwarding-api.txt
new file mode 100644 (file)
index 0000000..335637f
--- /dev/null
@@ -0,0 +1,80 @@
+Call Forwarding hierarchy
+===============
+Service                org.ofono
+Interface      org.ofono.CallForwarding
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Contains the properties for this object.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+
+               void DisableAll(string type)
+
+                       Disables all call forwarding rules for type.
+                       Type can be one of:
+                               "all" or "" - Disables all rules
+                               "conditional" - Disables all conditional rules,
+                                       e.g. busy, no reply and not reachable.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void SetProperty(string property, variant value)
+
+                       Sets the given property value to that specified in
+                       call parameter.
+
+                       Possible Errors: [service].Error.NotAvailable
+                                        [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+Properties     string VoiceUnconditional [readwrite]
+
+                       Contains the value of the voice unconditional call
+                       forwarding property.  If the value is an empty string,
+                       then this call forwarding rule is not active.
+                       Otherwise the rule is active with the string value
+                       as the phone number.
+
+               string VoiceBusy [readwrite]
+
+                       Contains the value of the voice "Busy" call forwarding
+                       rule.
+
+               string VoiceNoReply [readwrite]
+
+                       Contains the value of the voice "No Reply" call
+                       forwarding rule.
+
+               uint16 VoiceNoReplyTimeout [readwrite]
+
+                       Contains the value of the voice "No Reply" timeout in
+                       seconds. The timeout can be between 1 and 30 seconds.
+                       Please note that it is not possible to set this
+                       property's value if GetProperties() has not been
+                       previously called or the VoiceNoReply property
+                       has not been set.
+
+               string VoiceNotReachable [readwrite]
+
+                       Contains the value of the voice "Not Reachable" call
+                       forwarding rule.
+
+               boolean ForwardingFlagOnSim [readonly]
+
+                       Boolean representing the voice unconditional call
+                       forwarding rule status.
diff --git a/doc/call-meter-api.txt b/doc/call-meter-api.txt
new file mode 100644 (file)
index 0000000..b668f79
--- /dev/null
@@ -0,0 +1,91 @@
+Call Meter hierarchy
+===============
+Service                org.ofono
+Interface      org.ofono.CallMeter
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Contains the properties for this object.
+
+                       Possible Errors: [service].Error.InProgress
+
+               void SetProperty(string property, variant value,
+                                string password)
+
+                       Sets the property to value specified in the
+                       call parameter.  The last parameter is used
+                       to pass the SIM PIN2 code which may be
+                       required by the SIM.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void Reset(string password)
+
+                       Attempts to reset the Accumulated Call Meter.
+                       Reseting this value requires SIM PIN2, provided
+                       by the password parameter.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+               NearMaximumWarning()
+
+                       Emitted shortly before the ACM (Accumulated Call
+                       Meter) maximum values is reached.  The warning is
+                       issued approximately when 30 seconds call time
+                       remains or when starting a call with less than
+                       30 seconds remaining.
+
+Properties     uint32 CallMeter [readonly]
+
+                       Contains the current call meter value from the ME.
+                       The values are in 24-bit range, counted in home
+                       units.
+
+               uint32 AccumulatedCallMeter [readonly]
+
+                       Contains the Accumulated Call Meter (ACM) value
+                       from the SIM.  When the AccumulatedCallMeter
+                       value reaches AccumulatedCallMeterMaximum value,
+                       no further calls can be made until the ACM
+                       value is reset.  Reset is accomplished using
+                       the Reset() function.
+
+                       The values are in 24-bit range.
+
+               uint32 AccumulatedCallMeterMaximum [readwrite]
+
+                       Contains the Accumulated Call Meter maximum value
+                       on reaching which, calls are prohibited.  This is
+                       effectively the maximum number of home units
+                       allowed to be consumed by subscriber.
+
+                       According to the GSM specification, setting the value
+                       to 0, turns off this feature.
+
+                       The values are in 24-bit range.
+
+               double PricePerUnit [readwrite]
+
+                       Contains price-per-unit conversion value.  This
+                       information can be used to convert the home units
+                       into currency units.
+
+               string Currency [readwrite]
+
+                       Contains three-character currency code.  This
+                       information can be used to convert the home
+                       units into currency units.
diff --git a/doc/call-settings-api.txt b/doc/call-settings-api.txt
new file mode 100644 (file)
index 0000000..de9f314
--- /dev/null
@@ -0,0 +1,135 @@
+Call Settings hierarchy
+===============
+Service                org.ofono
+Interface      org.ofono.CallSettings
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Contains the properties for this object.
+
+                       Possible Errors: [service].Error.InProgress
+
+               void SetProperty(string property, variant value)
+
+                       Sets the given property value to that specified in
+                       call parameter.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+Properties     string CallingLinePresentation [readonly]
+
+                       Contains the value of the calling line identification
+                       presentation property.  The value indicates
+                       the state of the CLIP supplementary service in the
+                       network.  If enabled, the network will provide
+                       the number of the calling party for incoming calls.
+                       Possible values are:
+                               "disabled",
+                               "enabled",
+                               "unknown"
+
+               string CalledLinePresentation [readonly]
+
+                       Contains the value of the called line identification
+                       presentation property.  The value indicates the state
+                       of the CDIP supplementary service in the network.  If
+                       enabled, when receiving a call the network will provide
+                       the subscriber's line dialed.  This is useful for
+                       subscribers which have a multiple line service with
+                       their network provider and would like to know what
+                       line the call is coming in on.
+
+                       Possible values are:
+                               "disabled",
+                               "enabled",
+                               "unknown"
+
+               string CallingNamePresentation [readonly]
+
+                       Contains the value of the calling name identification
+                       presentation property.  The value indicates
+                       the state of the CNAP supplementary service in the
+                       network.  If enabled, the network will provide
+                       the name of the calling party for incoming calls.
+                       Possible values are:
+                               "disabled",
+                               "enabled",
+                               "unknown"
+
+               string ConnectedLinePresentation [readonly]
+
+                       Contains the value of the connected line identification
+                       presentation property.  The value indicates the state
+                       of the COLP supplementary service in the network.  If
+                       enabled, the network will attempt to provide the number
+                       of the connected party for outgoing calls.  Possible
+                       values are:
+                               "disabled",
+                               "enabled",
+                               "unknown"
+
+               string ConnectedLineRestriction [readonly]
+
+                       Contains the value of the connected line identification
+                       restriction property. The value indicates the state of
+                       the COLR supplementary service in the network.  If
+                       enabled, the network will withhold subscriber number
+                       information from the calling party on incoming calls.
+                       The possible values are:
+                               "disabled",
+                               "enabled",
+                               "unknown"
+
+                       Not all modems can report this information.
+
+               string CallingLineRestriction [readonly]
+
+                       Contains the value of the calling line identification
+                       restriction property.  The value indicates the state of
+                       the CLIR supplementary service in the network.  If
+                       enabled permanently or temporarily the restriction is
+                       in effect, the subscriber number information will be
+                       withheld from the called party on outgoing calls
+                       unless the value is overriden using the HideCallerId
+                       property or on a per call basis.
+
+                       The possible values are:
+                               "disabled"
+                               "permanent"
+                               "unknown",
+                               "on",
+                               "off"
+
+               string HideCallerId [readwrite]
+
+                       Sets whether the ID of the caller will should be
+                       provided or withheld for outgoing calls.  This setting
+                       is also modified by the status of the CLIR supplementary
+                       service in the network (see the CallingLineRestriction
+                       property).  The three possible values are:
+                               "default" - Uses the network setting
+                               "enabled" - CLIR invoked, caller id is withheld
+                               "disabled" - CLIR suppressed, caller id is
+                                               provided
+
+                       This setting can also be changed on a per-call basis,
+                       see the VoiceCallManager Dial method documentation.
+
+               string VoiceCallWaiting [readwrite]
+
+                       Contains the call waiting status for Voice calls.
+                       If enabled, the call waiting status will be
+                       presented to the subscriber for voice calls.
+                       Possible values are:
+                               "disabled",
+                               "enabled",
diff --git a/doc/call-volume-api.txt b/doc/call-volume-api.txt
new file mode 100644 (file)
index 0000000..320b25f
--- /dev/null
@@ -0,0 +1,43 @@
+CallVolume hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.CallVolume
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the CallVolume object. See
+                       the properties section for available properties.
+
+               void SetProperty(string property, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as readwrite are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Muted [readwrite]
+
+                       Boolean representing whether the microphone is muted.
+
+               byte SpeakerVolume [readwrite]
+
+                       Represents the current volume of the speaker in
+                       percentage points.  Valid values are 0-100.
+
+               byte MicrophoneVolume [readwrite]
+
+                       Represents the current volume of the microphone in
+                       percentage points.  Valid values are 0-100.
diff --git a/doc/calypso-modem.txt b/doc/calypso-modem.txt
new file mode 100644 (file)
index 0000000..8e1eb22
--- /dev/null
@@ -0,0 +1,22 @@
+Calypso modem usage
+===================
+
+On the Openmoko Freerunner phone, the Calypso modem is presented by
+the kernel as serial device /dev/ttySAC0.  To allow oFono to
+autodetect and use this, a simple udev rule is needed:
+
+KERNEL=="ttySAC0", ENV{OFONO_DRIVER}="calypso"
+
+You can put this in any file in /lib/udev/rules.d.  A logical choice
+is the "55-openmoko-gta01-gta02.rules" file, if it exists in your
+Freerunner distribution.
+
+With this rule in place:
+
+- oFono will detect the Calypso modem when it starts up, and the
+  `list-modems' test script will show this, but will not power it up
+
+- the `enable-modem' test script can be used to power it up
+
+- a subsequent `list-modems' will show lots of information, including
+  network registration.
diff --git a/doc/cell-broadcast-api.txt b/doc/cell-broadcast-api.txt
new file mode 100644 (file)
index 0000000..52618eb
--- /dev/null
@@ -0,0 +1,70 @@
+Cell broadcast hierarchy
+========================
+
+Service                org.ofono
+Interface      org.ofono.CellBroadcast
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the cell broadcast object. See
+                       the properties section for available properties.
+
+               void SetProperty(string property, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as readwrite are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               IncomingBroadcast(string text, uint16 topic)
+
+                       This signal is emitted whenever a new cell broadcast
+                       is received.  The string text contains contents of the
+                       broadcast and topic contains the channel this
+                       broadcast was received on.
+
+                       Please note that base station name broadcasts are
+                       handled by the NetworkRegistration interface.
+
+               EmergencyBroadcast(string text, dict properties)
+
+                       This signal is emitted whenever an ETWS cell broadcast
+                       is received.  The string text contains contents of the
+                       broadcast.  The dict is made up of the following
+                       entries:
+                               EmergencyType - string value, possible values
+                                               include: "Earthquake",
+                                                       "Tsunami",
+                                                       "Earthquake+Tsunami",
+                                                       "Other"
+                               EmergencyAlert - boolean value hinting whether
+                                               an extra emergency indicator
+                                               should be activated (e.g.
+                                               vibrate mode, emergency alert
+                                               mode.)
+
+                               Popup - boolean value hinting whether the UI
+                                       should popup a message box with the
+                                       emergency information.
+
+Properties     boolean Powered [readwrite]
+
+                       Boolean representing the power state of the cell
+                       broadcast service.  If powered is False, then no
+                       Cell Broadcast information is received.
+
+               string Topics [readwrite]
+
+                       Returns a list of topics currently subscribed to by
+                       this modem.  If the list is empty, then only emergency
+                       broadcasts will ever be received.
diff --git a/doc/connman-api.txt b/doc/connman-api.txt
new file mode 100644 (file)
index 0000000..2227ab8
--- /dev/null
@@ -0,0 +1,290 @@
+Connection Manager hierarchy
+=================
+
+Service                org.ofono
+Interface      org.ofono.ConnectionManager
+Object path    [variable]
+
+Methods                dict GetProperties()
+
+                       Returns all global system properties. See the
+                       properties section for available properties.
+
+               void SetProperty(string property, variant value)
+
+                       Sets the property to a desired value
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.Failed
+
+               void DeactivateAll()
+
+                       Deactivates all active contexts.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.Failed
+
+               array{object,dict} GetContexts()
+
+                       Get array of context objects and properties.
+
+                       The method should only be call once per application.
+                       Further changes shall be monitored via ContextAdded
+                       ContextRemoved signals.
+
+               object AddContext(string type)
+
+                       Creates a new Primary context.  The type contains
+                       the intended purpose of the context.
+
+                       For possible values of the type parameter see the
+                       Type documentation of ConnectionContext interface.
+                       Returns the object path of the created context.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void RemoveContext(object context)
+
+                       Removes a primary context.  All secondary contexts, if
+                       any, associated with the primary context are also
+                       removed.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.NotFound
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               ContextAdded(object path, dict properties)
+
+                       Signal that gets emitted when a new context has
+                       been created.  It contains the context object path
+                       and its properties.
+
+               ContextRemoved(object path)
+
+                       Signal that gets emitted when a context has been
+                       removed.  The object path of the context is only
+                       included for reference.  Its properties are no
+                       longer accessible at this point.
+
+Properties     boolean Attached [readonly]
+
+                       Contains whether the Packet Radio Service is attached.
+                       The attach state might change dynamically based on
+                       availability of network resources.  If this value
+                       changes to false, the user can assume that all
+                       contexts have been deactivated.
+
+                       If the modem is detached, certain features will not
+                       be available, e.g. receiving SMS over packet radio
+                       or network initiated PDP activation.
+
+               string Bearer [readonly, optional]
+
+                       Contains the data bearer technology as reported by the
+                       GPRS service registration (if known).
+
+                       Possible values are:
+                       "none", "gsm", "edge", "umts", "hsdpa", "hsupa",
+                       "hspa" (HSDPA and HSUPA at the same time) and
+                       "lte"
+
+               boolean Suspended [readonly, optional]
+
+                       Contains whether the GPRS service is suspended.
+                       During suspended state the modem is attached to the
+                       GPRS service and all contexts remain established,
+                       however, data transfer is not possible.
+
+                       The suspended state may be entered if the modem is
+                       temporarily out of network coverage. GPRS class B
+                       modems will suspend GPRS whenever a voice call is
+                       active at the same time. GPRS may also be suspended
+                       if the network does not support simultaneous packet
+                       data and voice. Various signalling procedures may
+                       also cause GPRS to be briefly suspended.
+
+                       As the suspension may be brief, clients should wait
+                       for an appropriate time for GPRS service to resume
+                       before taking corrective action.
+
+               boolean RoamingAllowed [readwrite]
+
+                       Contains whether data roaming is allowed.  In the off
+                       setting, if the packet radio registration state
+                       indicates that the modem is roaming, oFono will
+                       automatically detach and no further connection
+                       establishment will be possible.
+
+               boolean Powered [readwrite]
+
+                       Controls whether packet radio use is allowed. Setting
+                       this value to off detaches the modem from the
+                       Packet Domain network.
+
+Connection Context hierarchy
+=================
+
+Service                org.ofono
+Interface      org.ofono.ConnectionContext
+Object path    [variable]
+
+Methods                dict GetProperties()
+                       Returns all properties for the context object.
+
+               void SetProperty(string property, variant value)
+
+                       Sets the property to a desired value
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+                                        [service].Error.InProgress
+                                        [service].Error.NotAttached
+                                        [service].Error.AttachInProgress
+                                        [service].Error.NotImplemented
+
+Signals                PropertyChanged(string property, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Active [readwrite]
+
+                       Holds whether the context is activated.  This value
+                       can be set to activate / deactivate the context.
+
+               string AccessPointName [readwrite]
+
+                       Holds the name of the access point.  This is
+                       abbreviated as APN.  This value cannot be changed when
+                       the context is active.
+
+               string Type [readwrite]
+
+                       Contains the intended usage type for this context.
+                       The currently supported values are:
+                               "internet" - General internet connectivity
+                               "mms" - Used by MMS related services
+                               "wap" - Used by WAP related services
+                               "ims" - Used by IMS related services
+
+               string Username [readwrite]
+
+                       Holds the username to be used for authentication
+                       purposes.  This value cannot be changed when the
+                       context is active.
+
+               string Password [readwrite]
+
+                       Holds the password to be used for authentication
+                       purposes.  This value cannot be changed when the
+                       context is active.
+
+               string Protocol [readwrite]
+
+                       Holds the protocol for this context.  Valid values
+                       are: "ip", "ipv6" and "dual".
+
+               string Name [readwrite]
+
+                       The name is a free form string that describes this
+                       context.  The name should not be empty and limited
+                       to a short string for display purposes.
+
+               dict Settings [readonly, optional]
+
+                       Holds all the IP network settings
+
+                       string Interface [readonly, optional]
+
+                               Holds the interface of the network interface
+                               used by this context (e.g. "ppp0" "usb0")
+
+                       string Method [readonly, optional]
+
+                               Holds the IP network config method
+                                       "static"- Set IP network statically
+                                       "dhcp"  - Set IP network through DHCP
+
+                       string Address [readonly, optional]
+
+                               Holds the IP address for this context.
+
+                       string Netmask [readonly, optional]
+
+                               Holds the Netmask for this context.
+
+                       array{string} DomainNameServers [readonly, optional]
+
+                               Holds the list of domain name servers for this
+                               context.
+
+                       string Gateway [readonly, optional]
+
+                               Holds the gateway IP for this connection.
+
+                       string Proxy [readonly, MMS only]
+
+                               Holds the current proxy information for
+                               using this context.
+
+                               In combination with the Interface value
+                               this allows access to the services offered
+                               by this context.
+
+                               It is possible that this reflects just the
+                               MessageProxy property if such a routing can
+                               be set up.  However this can also be pointing
+                               to a local proxy on 127.0.0.1 and then using
+                               the loopback interace lo for it.
+
+                               Users of this context should bind to the
+                               provided interface and only attempt access
+                               via this proxy.  All other values are left
+                               out in this case.
+
+               dict IPv6.Settings [readonly, optional]
+
+                       Holds all the IPv6 network settings
+
+                       string Interface [readonly, optional]
+
+                               Holds the interface of the network interface
+                               used by this context (e.g. "ppp0" "usb0")
+
+                       string Address [readonly, optional]
+
+                               Holds the IP address for this context.
+
+                       byte PrefixLength [readonly, optional]
+
+                               Holds the prefix length.
+
+                       array{string} DomainNameServers [readonly, optional]
+
+                               Holds the list of domain name servers for this
+                               context.
+
+                       string Gateway [readonly, optional]
+
+                               Holds the gateway IP for this connection.
+
+               string MessageProxy [readwrite, MMS only]
+
+                       Holds the MMS Proxy setting.
+
+               string MessageCenter [readwrite, MMS only]
+
+                       Holds the MMSC setting.
diff --git a/doc/features.txt b/doc/features.txt
new file mode 100644 (file)
index 0000000..563a0f5
--- /dev/null
@@ -0,0 +1,741 @@
+oFono - Open Source Telephony
+*****************************
+
+Purpose
+=======
+
+The purpose of this document is to enumerate all major functionality areas
+of oFono.  In effect, this document will serve as the primary, up to date
+source of oFono feature information.  It is intended for developers, managers
+and users alike to quickly gauge the progress of the project and feature
+availability.
+
+Sim Toolkit
+===========
+
+Supported Proactive Commands:
+
+- Display Text proactive command support.  Upon receiving the proactive
+  command notification oFono decodes it, including performing character
+  conversion from packed/unpacked GSM 7bit and UCS2 to UTF-8 encoded text.
+  The registered agent is then called using the DisplayText method on the
+  SimToolkitAgent interface to handle the user interaction aspects.
+
+  SIM-specified duration are handled.  If immediate response to the SIM
+  is required, oFono sends a terminal response immediately.  DisplayText
+  method is still executed normally, until a timeout occurs or a new
+  proactive command arrives from the SIM.
+
+- Get Inkey proactive command support.  When this command is received,
+  oFono decodes it and checks what "flavor" it is.  Depending on this, the
+  SimToolkitAgent is called with a different method call:
+       * If the Get Inkey flavor indicates that this is a simple Yes/No
+         confirmation, then the RequestConfirmation method is called.
+       * If the Get Inkey flavor indicates that the SIM only expects digits,
+         then the RequestDigit method is called.
+       * Otherwise the RequestKey method is called
+
+  SIM specified durations are handled, if the user agent does not respond
+  in the time allowed, then the 'No Response' Terminal Response is generated
+  automatically.
+
+- Get Input proactive command support.  When this command is received,
+  oFono decodes it and checks what "flavor" it is.  Depending on this, the
+  SimToolkitAgent is called with a different method call:
+       * If the Get Input flavor indicates that the SIM only expects digits,
+         then the RequestDigits method is called.
+       * Otherwise the RequestInput method is called
+
+- More Time proactive command support.  This command is intended to notify
+  that the SIM is still busy processing a command.  For oFono, this proactive
+  command is a no-op.  It is always replied to successfully.
+
+- Setup Menu proactive command support. When this command is received, oFono
+  parses the new main menu structure and updates its MainMenu and MainMenuTitle
+  properties which reflect the items, icons and the alpha text of the
+  proactive command.  Soft key support and menu help system are ignored by
+  oFono.
+
+- Select Item proactive command support.  When this command is received, oFono
+  decodes it and dispatches it to the SimToolkitAgent by calling the
+  RequestSelection method.  This method is passed the menu selection title,
+  the selectable items and the default, if any.
+
+- Timer Management proactive command support.  oFono supports starting,
+  stopping and querying timer state flavors of this command.  Up to eight
+  timers are supported.  This proactive command is handled completely
+  inside oFono and no external user interaction is required.
+
+- Set Up Idle Mode Text proactive command support.  Whenever oFono receives
+  this proactive command, it updates the IdleText property on the main
+  SimToolkit interface.  Indications that this property has changed are
+  handled by the usual means.
+
+- Send DTMF proactive command.  Whenever oFono receives the Send DTMF command,
+  it checks that there are calls in progress and DTMF is possible.  If so,
+  DTMF characters are passed to the voicecall atom to be transmitted to the
+  modem.  The appropriate terminal response is sent to the SIM once the DTMF
+  tones have been played or the call has been disconnected.
+
+  NOTE: This command can also be handled by the modem.
+
+- Play Tone proactive command.  Whenever oFono receives a Play Tone proactive
+  command it checks whether the tone is to be continuous/looped or played once.
+  It then calls the SimToolkitAgent PlayTone or LoopTone method as appropriate.
+  The sound that will be played will be determined based on the sound type
+  that is passed to the agent.  It is up to the system integrator to provide
+  the appropriate sounds.
+
+- Send USSD proactive command.  Whenever oFono receives a Send USSD proactive
+  command it checks whether there are any USSD / SS operations in progress.
+  If an operation is in progress, the appropriate terminal response is sent
+  without performing the Send USSD operation.  Otherwise the USSD string
+  is sent to the network, and the response is sent back to the SIM in the
+  terminal response.
+
+  NOTE: This command can also be handled by the modem.
+
+- Language Notification proactive command. Whenever oFono receives a Language
+  Notification proactive command, it prints the language code and sends
+  terminal response to the SIM immediately.
+
+- Provide Local Information proactive command.  Whenever oFono receives a
+  Provide Local Information proactive command, it checks the type of the
+  information requested.  If the information is about the time/date or the
+  language of the terminal, it responds to the command with the appropriate
+  terminal response. The time/date information is obtained using localtime().
+  The language information is obtained by checking the LANG environment
+  variable.  All other information requests are expected to be handled by
+  the modem.
+
+- Send Short Message proactive command.  Whenever oFono receives a Send SMS
+  proactive command, it parses the PDU and submits it to the outgoing SMS
+  queue.  A terminal response is sent to the SIM When the raw PDU has been
+  sent, or failed to be sent.
+
+  NOTE: This command can also be handled by the modem.
+
+- Set Up Call proactive command.  When oFono receives a Set Up Call proactive
+  command, it checks whether the UICC has indicated that the user should be
+  informed.  In this case the SimToolkitAgent is called with the
+  ConfirmCallSetup method.  If the user has authorized the operation, or if
+  the user's confirmation was not indicated oFono will setup the call and
+  optionally inform the user.  The information sent by the SIM will be
+  available on the resulting voice call object.  The relevant properties are
+  Information and Icon.
+
+  NOTE: This command can also be handled by the modem.
+
+- Refresh proactive command.  The specification defines 7 types
+  of Refresh requests:
+       - NAA Initialization
+       - NAA File Change Notification
+       - NAA Initialization and File Change Notification
+       - NAA Initialization and Full File Change Notification
+       - UICC Reset
+       - NAA Application Reset (2G only)
+       - NAA Session Reset (3G only)
+
+  oFono can fully perform the the first four types of Refresh.  The
+  remaining three must be handled by the modem or its driver with a
+  notification sent to ofono.  Regardless of whether the command is
+  handled by the modem or not, oFono will check whether there are any
+  calls or ussd operations active.  If there are, the appropriate
+  response will be sent (e.g. busy on call or screen busy terminal
+  response.)  Otherwise a positive response will be sent to the driver.
+  In the case of a 'UICC Reset' the driver / modem can interpret this
+  that it is safe to reset the UICC.
+
+  Alternatively, the driver / modem can notify the core of the SIM removal
+  / SIM insertion events without using the Refresh proactive command.  It
+  is up to the driver / modem to perform a warm reset.  In particular, 3GPP
+  31.111 mandates that any change to EFimsi is done by using 'UICC Reset',
+  'NAA Application Reset' or 'NAA Session Reset'.  Please see 3GPP 31.111
+  Section 6.4.7.1.
+
+  Other types are handled by oFono flushing the EF cache of the files
+  affected (or the entire SIM cache in case of Full File Change Notifications)
+  and re-reading the affected files.  Any properties derived from these
+  Elementary Files will be updated and signaled using PropertyChanged.
+
+  NOTE: This command can also be handled by the modem.
+
+- Sim icon support.  oFono supports icons that are stored on the SIM.  If the
+  SIM notifies oFono that an icon is available for a particular proactive
+  command, oFono passes this information to the UI.  The UI is able to obtain
+  the icons by using GetIcon method on the SimManager interface.  The icons
+  are read from the SIM and converted into XPM format.
+
+- Text attribute support.  Whenever oFono detects that text attributes have
+  been given to any text or alpha string, it applies them and converts the
+  resulting text to HTML.  The UI is expected to be able to display such
+  HTML formatted text.
+
+Envelopes:
+
+- Timer Expiration envelope support.  Whenever a timer expires (as started by
+  the Timer Management proactive command) oFono forwards, this envelope type
+  to the SIM.  No external user interaction is required.
+
+- Menu Selection envelope support.  The user can initiate a proactive command
+  session by selecting something from the Sim Toolkit main menu.  This is done
+  by using the SimToolkit's SelectItem method.  As a result, oFono will send
+  the Menu Selection envelope to the SIM.
+
+- CBS-PP Download envelope support.  When oFono receives a cell broadcast
+  and on a channel listed in EFcbmid, it is dispatched to the SIM using the
+  CBS-PP Download envelope.  No user interaction is required or signaled
+  whenever this occurs.
+
+- SMS-PP Download envelope support.  When oFono receives an sms message
+  addressed to the SIM, it is dispatched to the SIM using the SMS-PP Download
+  envelope.  No user interaction is required or signaled whenever this occurs.
+
+  Please note that many current modems do not support returning RP-ACK and
+  RP-ERROR acknowledgement PDUs back to the network.  This is required by the
+  CAT specification for SMS-PP Download.  E.g. the sim response to the SMS-PP
+  Download Envelope is to be stored in an RP-ACK / RP-ERROR PDU and returned to
+  the network.  It is thus anticipated that modems will transparently handle
+  this feature in the firmware.
+
+The following commands are expected to be handled by the modem:
+
+- Send SS proactive command.  oFono does not explicitly support this proactive
+  command since AT modems do not provide the low level information necessary
+  for oFono to generate a valid response.  The modem (or driver) shall handle
+  this command.  Optionally the modem (or driver) can inform oFono that the
+  proactive command has been received and is being handled by the modem, as
+  well as when the terminal response has been sent by the modem.  oFono will
+  display the necessary user information for this time period if this
+  information is included in the proactive command.
+
+ -----------------------------------------------------------------------
+|      Feature                 |       Support |       Implemented by  |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|Profile Download              |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SMS-PP Data Download          |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|Cell Broadcast data Download  |       YES     |       BASEBAND or ME  |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|CALL CONTROL BY SIM           |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|DISPLAY TEXT                  |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|GET INPUT                     |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|GET INKEY                     |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|MORE TIME                     |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PLAY TONE                     |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|POLL INTERVAL                 |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|POLLING OFF                   |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|REFRESH                       |       YES     |       BASEBAND-ME     |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SELECT ITEM                   |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SEND SHORT MESSAGE            |       YES     |       BASEBAND-ME     |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SEND SS                       |       YES     |       BASEBAND-ME     |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SEND USSD                     |       YES     |       BASEBAND-ME     |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SET UP CALL                   |       YES     |       BASEBAND-ME     |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SET UP MENU                   |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(MCC, MNC, LAC, cellId &      |               |                       |
+|IMEI)                         |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(NMR)                         |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SET UP EVENT LIST             |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: MT CALL                        |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: CALL CONNECTED         |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: CALL DISCONNECTED      |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: LOCATION STATUS                |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: USER ACTIVITY          |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: IDLE SCREEN AVAILABLE  |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: CARD READER STATUS     |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: LANGUAGE SELECTION     |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: BROWSER TERMINATION    |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: DATA AVAILABLE         |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: CHANNEL STATUS         |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: ACCESS TECHNOLOGY      |       YES     |       BASEBAND        |
+|      CHANGE                  |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: DISPLAY PARAMETERS     |       NO      |                       |
+|      CHANGED                 |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: LOCAL CONNECTION       |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: NETWORK SEARCH MODE    |       YES     |       BASEBAND        |
+|      CHANGE                  |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|POWER ON CARD                 |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|POWER OFF CARD                        |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PERFORM CARD APDU             |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|GET READER STATUS             |       NO      |                       |
+|(Card reader status)          |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|GET READER STATUS             |       NO      |                       |
+|(Card reader identifier)      |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|TIMER MANAGEMENT              |       YES     |       BASEBAND-ME     |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       ME              |
+|(Date, Time, & Time Zone)     |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SET UP IDLE MODE TEXT         |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|RUN AT COMMAND                        |       YES     |       BASEBAND        |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SEND DTMF                     |       YES     |       BASEBAND-ME     |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       ME              |
+|(Language)                    |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(Timing Advance)              |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|Language Notification         |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|LAUNCH BROWSER                        |       YES     |       ME              |
+|                              |       (MIN)   |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(ACCESS TECHNOLOGY)           |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|OPEN CHANNEL                  |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|CLOSE CHANNEL                 |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|RECEIVE DATA                  |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SEND DATA                     |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|GET CHANNEL STATUS            |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SERVICE SEARCH                        |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|GET SERVICE INFORMATION       |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|DECLARE SERVICE               |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|Text Attributes               |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|ICONS                         |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|Bearer Independent Protocol   |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|VARIABLE TIMEOUT              |       YES     |       ME              |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(IMEISV)                      |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(SEARCH MODE CHANGE)          |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|Extended Launch Browser       |       NO      |                       |
+|Capability                    |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(battery state)               |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|RETRIEVE MULTIMEDIA MESSAGE   |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SUBMIT MULTIMEDIA MESSAGE     |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|DISPLAY MULTIMEDIA MESSAGE    |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|SET FRAMES                    |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|GET FRAMES STATUS             |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|PROVIDE LOCAL INFORMATION     |       YES     |       BASEBAND        |
+|(NMR(UTRAN))                  |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|USSD Data Download and                |       NO      |                       |
+|application mode              |               |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: BROWSING STATUS                |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+|EVENT: MMS TRANSFER STATUS    |       NO      |                       |
+|                              |               |                       |
+ -----------------------------------------------------------------------
+
+Modem
+=====
+
+- 'silent' modem reset. Sometimes modems get in a bad state and have to reset
+  itself. Now oFono can be notified when a reset happens and do proper work to
+  put the modem back to work restoring the state before the reset.
+
+- Lockdown support. Through the Lockdown property on the Modem D-Bus API, one
+  can power down the modem and lock it disallowing any other application to
+  use that modem. Useful for firmware update and similar stuff.
+
+Short Messaging Service
+=======================
+
+- Unique identifier for incoming and outgoing SMS messages.  Whenever a
+  message is received or sent by oFono, a unique ID is generated for that
+  message.  The id is relevant to all segments of an SMS and in the case of
+  incoming messages is only generated when the SMS is fully assembled.  The
+  hash used is SHA1.  This unique identifier is used to identify the SMS
+  message to history plugins as well.
+
+- SMS Status Report support.  oFono allows requesting of SMS Status Reports
+  via the MessageManager UseDeliveryReports property.  If enabled, oFono
+  will set the SRR bit and process incoming status reports.  oFono takes
+  care of collating the status reports for multi-fragment messages.  Once all
+  status reports are received, the UI is notified either via DBus or history
+  plugin API.
+
+- Source / Destination port addressing scheme from 3GPP 23.040.  A special
+  header is used to indicate the source / destination port of the application
+  this SMS message belongs to.  oFono provides a handler registration
+  framework where plugins can handle the reception of such messages.  The
+  handler can be registered to receive messages which contain a specific
+  source and destination port, or a wildcard.  When such messages are received,
+  they are matched against the registered handlers and dispatched appropriately.
+
+  oFono takes care of de-fragmentation of such SMS messages, so the handler
+  is informed only once the entire message has been received, and the data
+  payload has been extracted.
+
+- Smart Messaging Service - vCard support.  oFono provides the ability to send
+  and receive vCard objects through the SmartMessaging interface.  vCards can
+  be sent using the SendBusinessCard method and received using the
+  SmartMessagingAgent framework.
+
+- Smart Messaging Service - vCalendar support.  oFono provides the ability to
+  send and receive vCalendar objects through the SmartMessaging interface.
+  vCalendars can be sent using the SendAppointment method and received using
+  the SmartMessagingAgent framework.
+
+- WAP PUSH notification support.  oFono allows the reception of WAP PUSH
+  messages via SMS through the use of the PushNotification interface and the
+  PushNotificationAgent framework.
+
+- Persisting of outgoing SMS messages.  Whenever oFono is asked to send an SMS
+  message, it adds it to an internal queue and persists it on disk.  The queue
+  is persistent across reboots of oFono and allows to queue SMS messages even
+  while the modem is offline.
+
+GPRS
+====
+
+- GPRS suspension event support.  The packet data service may be temporarily
+  suspended while a circuit switched service such as voice call or SMS is
+  active.
+
+- GPRS context protocol support.  oFono supports the followig types of GPRS
+  contexts:
+       - IPv4
+       - IPv6
+       - Dual Stack (IPv4 + IPv6)
+
+- GPRS provisioning support.  oFono will automatically configure GPRS contexts
+  if no prior configuration (e.g. user or provisioning) has been detected.
+  If the GPRS atom detects that provisioning is required it will query the
+  inserted sim card for the Service Provider Name and call each registered
+  provisioning plugin with the MCC, MNC, SPN of the inserted SIM card.  If the
+  provisioning information is provided, then it is used to populate the
+  initial context configuration.
+
+- GPRS provisioning via 'Mobile Broadband Provider Info'.  oFono supports
+  provisioning of GPRS contexts via Mobile Broadband Provider Info project's
+  database.
+
+Location Reporting
+==================
+
+- GPS support. Many modem manufacturers provide a GPS unit with their modem
+  hardware.  Upon client request oFono can turn this unit on or off and pass a
+  file descriptor in which client may receive the desired location reporting
+  data.
+
+SIM
+===
+
+- Fixed Dialing support.  oFono reads the necessary bits from the SIM to
+  check if FDN support is allocated and enabled in the SIM.  If enabled,
+  oFono halts the SIM initialization procedure and the modem remains in the
+  PRESIM state.  In this state oFono will only allow emergency calls.
+
+- Barred Dialing support.  oFono reads the necessary bits from the SIM to
+  check if BDN support is allocated and enabled in the SIM.  If enabled,
+  oFono halts the SIM initialization procedure and the modem remains in the
+  PRESIM state.  In this state oFono will only allow emergency calls.
+
+- Read / Write EFcfis / EFcphs-cff.  oFono reads EFcfis/EFcphs-cff SIM files
+  to check if VoiceUnconditional call forwarding rule is enabled.  If enabled,
+  ForwardingFlagOnSim will be set and VoiceUnconditional may contain the
+  "forwarded to" number if the number is available.
+
+- Support SIM retry counters. oFono exports all the retry counters available on
+  SIM, e.g., PIN, PIN2, PUK and PUK2. Whenever an action changes them, a signal
+  is sent with the updated values, so user can keep track of how many times
+  he/she can still give a wrong pin before the SIM locking down.
+
+Radio settings
+==============
+
+- Fast dormancy support. A fast dormancy feature can be enabled in the
+  cellular modem to conserve power when the end user is not actively
+  using the device but some networking applications are online using
+  packet data.
+
+- Frequency Band Selection support. This feature allows the user to limit the
+  frequency bands in which the cellular modem can operate.
+
+Text Telephony
+==============
+
+- TTY (hearing impaired) support, also known as Cellular Text Modem (CTM).
+  In case it's supported by the modem, oFono allows the user to enabled
+  or disable it through the TextTelephony interface.
+
+Emergency Calls
+===============
+
+- Emergency number reporting.  During SIM initialization phase oFono reads
+  EFecc in order to bootstrap the emergency calling codes provided by the SIM.
+  Emergency number list is exposed via the EmergencyNumbers property on the
+  VoicecallManager interface.  If SIM is present, list is the union of default
+  emergency numbers(112, 911), numbers in EFecc and Network / Modem reported
+  emergency numbers.  If SIM is not present or EFecc has not been read yet,
+  list is the union of default emergency numbers(112, 911) and emergency
+  numbers without SIM(119, 118, 999, 110, 08 and 000).
+
+- Emergency call reporting.  When a voicecall is made to a number present
+  on the EmergencyNumbers list it is automatically flagged as an emergency
+  call.  This is done by setting the Emergency property on the Voicecall
+  interface to TRUE.
+
+- Emergency Mode.  oFono supports a concept of an 'Emergency Mode'.  This mode
+  is activated when any emergency procedure is ongoing and restricts certain
+  actions (e.g. setting the modem offline).  Emergency procedures are e.g.
+  ongoing emergency calls, or network initiated location requests related to
+  the emergency call.  The state of the emergency mode is exposed via the
+  Emergency property on the org.ofono.Modem interface.  This property is
+  intended to be used by power management daemons and other entities which
+  need to be aware of the ongoing emergency operation.
+
+Supplementary Services
+======================
+
+- CNAP support.  The Calling Name Presentation supplementary service is
+  supported by oFono.  One can query whether the service is provisioned in the
+  network by using the CallSettings interface.  If the network reports the
+  calling name, it is presented on the Voicecall interface using the Name
+  property.  If no calling name is given, the Name is empty.
+
+- CDIP support.  The Called Line Presentation is supported by oFono. One can
+  query whether the service is provisioned in the network by using the
+  CallSettings interface.  If the network supports this service, the number
+  dialed by the remote party is presented through the Voicecall interface using
+  the IncomingLine property.
+
+Voice Calls
+===========
+
+- Long phone number support. oFono supports dialing of phone numbers up to
+  80 digits long.
+
+- Supplementary service notifications related to mobile originated (MO) and
+  mobile terminated (MT) calls.  oFono supports certain supplementary service
+  notifications, typically reported by CSSU and CSSI, that are related to
+  MT/MO calls:
+       - outgoing call has been forwarded (+CSSI: 2)
+       - outgoing calls are barred (+CSSI: 5)
+       - outgoing call barred due to call barring on remote party (+CSSI: 6)
+       - incoming call is a forwarded call (+CSSU: 0)
+       - call has been put on hold by the remote party (+CSSU: 2)
+       - call has been retrieved by the remote party (+CSSU: 3)
+       - call has been added to a mpty call by the remote party (+CSSU: 4)
+
+Flight Mode
+===========
+
+- Flight Mode support.  oFono uses the 'Online' property on the Modem interface
+  that controls the state of the radio.  When Online state is False, all
+  interfaces that depend on the radio being on are removed.  Other interfaces
+  enter reduced functionality mode where only certain actions are available
+  that do not depend on the radio.  E.g. setting various settings on the local
+  store or the SIM.
+
+Network Registration
+====================
+
+- Support for PLMN_MODE bit from CPHS Customer Service Profile (CSP) within
+  the 'Value Added Services' service group.  oFono reads this file when the
+  network registration atom has been initialized.  If EFcsp indicates that
+  manual network registration is not allowed, oFono enters into 'auto-only'
+  registration mode.  Updates to this file via STK Refresh is also supported.
+
+- Support for 3GPP Service Provider Name (EFspn), CPHS Operator Name
+  String (ONS) and CPHS Short Operator Name String fields.  oFono always
+  tries to read the EFspn field first.  If this is not available, then oFono
+  tries to read the CPHS variant.  If neither are available and the
+  appropriate bits are set in the CPHS Information field, oFono tries to read
+  the CPHS Short ONS field.  oFono then reports the network name via the
+  'Name' property.
+
+PPP Stack
+=========
+
+- Support for Protocol Field Compression (PFC) packets.  The oFono PPP stack
+  supports both receiving and sending of packets with PFC enabled.  The user
+  can also control whether PFC capability is advertised to the peer, and used
+  during transmission.
+
+- Support for Address & Control Field Compression (ACFC) packets.  The oFono
+  PPP stack supports both receiving and sending of packets with ACFC enabled.
+  The user can also control whether ACFC capability is advertised to the peer,
+  and used during transmission.
+
+Modem Emulator
+==============
+
+- Support for Bluetooth HandsFree Profile Audio Gateway (HFP AG).  oFono
+  supports the underlying AT command protocol specified by BT HFP version 1.6.
+  Supported features include 3-way calling, ability to reject a call,
+  enhanced call status, enhanced call control, report of extended error results
+  code and indicator activation. Audio management is assumed to be performed in
+  another system component, e.g. PulseAudio.
+
+- Support for Bluetooth DUN profile.  oFono supports the Dial Up Networking
+  profile and all mandatory commands specified by BT DUN 1.1.  For a list
+  of supported commands please see doc/dialup-command-set.txt.
+
+CDMA Connection Manager
+=======================
+
+- Support Network Initiated disconnection of Packet Data Service over CDMA
+  (1xRTT and 1xEV-DO) systems.
+
+CDMA Network Acquisition
+========================
+
+- Support reporting of the received signal strength indicator (RSSI)
+  measurement for the currently acquired CDMA network.
+
+- Support reporting of the received signal strength indicator (RSSI)
+  measurement for the currently acquired 1xEV-DO data network.
+
+Bluetooth Sim Access Profile
+============================
+
+- oFono supports certain modems that can utilize remote sim cards
+  (e.g. via SAP).  This is done transparently to the user and all of oFono's
+  APIs are supported on such devices (assuming the device itself supports
+  the required features).  Today the Telit UC864-G is supported in this mode.
+
+Bluetooth Handsfree Profile
+===========================
+
+- Voicecall support.  oFono supports the use of Bluetooth Handsfree capable
+  devices to make voicecalls.  All features are supported, including 3-way
+  calls, multiparty calls, call waiting, etc.
+
+- Support for Handsfree specific protocol features.  oFono clients can gain
+  access to Bluetooth HFP specific features via the oFono Handsfree interface.
+  These features include voice recognition activation, last number redial,
+  etc.
diff --git a/doc/location-reporting-api.txt b/doc/location-reporting-api.txt
new file mode 100644 (file)
index 0000000..e6e5d54
--- /dev/null
@@ -0,0 +1,39 @@
+Location Reporting Hierarchy [experimental]
+=================
+
+Service                org.ofono
+Interface      org.ofono.LocationReporting
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns all LocationReporting properties. See the
+                       properties section for available properties.
+
+               filedescriptor Request()
+
+                       Asks to turn ON the NMEA stream and supplies the
+                       gps device file descriptor. The external cliend should
+                       use the file descriptor to receive the NMEA data.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InUse
+                                        [service].Error.Failed
+
+               void Release()
+
+                       Releases the gps device file descriptor and turns
+                       OFF the NMEA stream.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotAvailable
+                                        [service].Error.Failed
+
+Properties     boolean Enabled [readonly]
+
+                        Boolean representing the state of the NMEA stream.
+
+               string Type [readonly]
+
+                       Holds the type of the device. Currently only NMEA is
+                       supported.
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
new file mode 100644 (file)
index 0000000..1cdedec
--- /dev/null
@@ -0,0 +1,28 @@
+Manager hierarchy
+=================
+
+Service                org.ofono
+Interface      org.ofono.Manager
+Object path    /
+
+Methods                array{object,dict} GetModems()
+
+                       Get an array of modem objects and properties
+                       that represents the currently attached modems.
+
+                       This method call should only be used once when an
+                       application starts up.  Further modem additions
+                       and removal shall be monitored via ModemAdded and
+                       ModemRemoved signals.
+
+Signals                ModemAdded(object path, dict properties)
+
+                       Signal that is sent when a new modem is added.  It
+                       contains the object path of new modem and also its
+                       properties.
+
+               ModemRemoved(object path)
+
+                       Signal that is sent when a modem has been removed.
+                       The object path is no longer accessible after this
+                       signal and only emitted for reference.
diff --git a/doc/message-api.txt b/doc/message-api.txt
new file mode 100644 (file)
index 0000000..3eb3d2b
--- /dev/null
@@ -0,0 +1,33 @@
+Message hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.Message
+Object path    [variable prefix]/{modem0,modem1,...}/{message_01,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the message object. See
+                       the properties section for available properties.
+
+               void Cancel()
+
+                       Cancel a message that was previously sent. Only
+                       messages that are waiting on queue can be cancelled and
+                       it's not possible to cancel messages that already had
+                       some parts sent.
+
+                       Possible Errors: [service].Error.AccessDenied
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string State
+
+                       Contains the state of the message object.  Possible
+                       values are:
+                               "pending",
+                               "sent",
+                               "failed"
diff --git a/doc/message-waiting-api.txt b/doc/message-waiting-api.txt
new file mode 100644 (file)
index 0000000..83c030b
--- /dev/null
@@ -0,0 +1,49 @@
+MessageWaiting hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.MessageWaiting
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the MessageWaiting object. See
+                       the properties section for available properties.
+
+               void SetProperty(string property, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as readwrite are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.NotSupported
+                                        [service].Error.SimNotReady
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean VoicemailWaiting [readonly]
+
+                       Boolean representing whether there is a voicemail
+                       message waiting for the user on the voicemail server.
+
+               byte VoicemailMessageCount [readonly]
+
+                       The total number of voicemail messages waiting.
+                       Values of 255 indicate 255 messages or more.  Value
+                       0 when VoicemailWaiting is true indicates that the
+                       mailbox is not empty and the message count is not
+                       known.
+
+               string VoicemailMailboxNumber [readwrite]
+
+                       String containing the dialing number to be used for
+                       voice mailbox access.  This number is generally
+                       pre-provisioned on the SIM.  However, the user can
+                       change this number if required.
diff --git a/doc/messagemanager-api.txt b/doc/messagemanager-api.txt
new file mode 100644 (file)
index 0000000..43c4d07
--- /dev/null
@@ -0,0 +1,110 @@
+Message Manager hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.MessageManager
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the manager object. See
+                       the properties section for available properties.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+
+               array{object,dict} GetMessages()
+
+                       Get an array of message object paths and properties
+                       that represents the currently pending messages.
+
+                       This method call should only be used once when an
+                       application starts up.  Further message additions
+                       and removal shall be monitored via MessageAdded and
+                       MessageRemoved signals.
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as readwrite are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               object SendMessage(string to, string text)
+
+                       Send the message in text to the number in to.  If the
+                       message could be queued successfully, this method
+                       returns an object path to the created Message object.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               ImmediateMessage(string message, dict info)
+
+                       New immediate (class 0) SMS received. Info has Sender,
+                       LocalSentTime, and SentTime information.  Sender
+                       address is given in string format.  LocalSentTime and
+                       SentTime are given in string form using ISO8601 format.
+
+               IncomingMessage(string message, dict info)
+
+                       New incoming text SMS received. Info has Sender,
+                       LocalSentTime, and SentTime information.
+
+               MessageAdded(object path, dict properties)
+
+                       This signal is emitted whenever a new Message object
+                       has been created.
+
+               MessageRemoved(object path)
+
+                       This signal is emitted whenever a Message object
+                       has been removed, e.g. when it reaches a final state.
+
+Properties     string ServiceCenterAddress
+
+                       Contains the number of the SMS service center.
+
+               boolean UseDeliveryReports
+
+                       This property controls whether SMS Status Reports,
+                       sometimes better known as Delivery Reports are to be
+                       used.  If enabled, all outgoing SMS messages will be
+                       flagged to request a status report from the SMSC.
+
+               string Bearer
+
+                       Contains the bearer to use for SMS messages.  Possible
+                       values are:
+                               "cs-only" - Circuit Switched only
+                               "ps-only" - Packet Domain only
+                               "cs-preferred" - Use PS if CS is unavailable
+                               "ps-preferred" - Use CS if PS is unavailable
+
+                       By default oFono uses "cs-preferred" setting.
+
+               string Alphabet
+
+                       Contains the alphabet setting for outgoing SMSs.
+                       Possible values are:
+
+                               "default" - Default GSM alphabet
+                               "turkish" - Turkish alphabet
+                               "spanish" - Spanish alphabet
+                               "portuguese" - Portuguese alphabet
+
+                       The standard, language-specific alphabets are defined
+                       in 3GPP TS23.038, Annex A.  By default, oFono uses
+                       the "default" setting.
diff --git a/doc/modem-api.txt b/doc/modem-api.txt
new file mode 100644 (file)
index 0000000..95ce782
--- /dev/null
@@ -0,0 +1,111 @@
+Modem hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.Modem
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the modem object. See
+                       the properties section for available properties.
+
+               void SetProperty(string property, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as readwrite are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.NotAvailable
+                                        [service].Error.AccessDenied
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Powered [readwrite]
+
+                       Boolean representing the power state of the modem
+                       device.
+
+               boolean Online [readwrite]
+
+                       Boolean representing the rf state of the modem.
+                       Online is false in flight mode.
+
+               boolean Lockdown [readwrite]
+
+                       Boolean representing the lock state of the modem.
+                       Setting it to true, makes the calling application hold
+                       the modem lock and power it down. Setting to false
+                       makes the it release the modem lock. Only the
+                       application that holds the lock can power up the modem.
+                       If the the application exits Lockdown is set to false.
+
+               boolean Emergency [readonly, optional, experimental]
+
+                       Boolean representing the emergency mode of the
+                       modem. The Emergency is true if an emergency call or
+                       related operation is currently active.
+
+               string Name [readonly, optional]
+
+                       Friendly name of the modem device.
+
+               string Manufacturer [readonly, optional]
+
+                       String representing the manufacturer of the modem
+                       device.
+
+                       This is usually obtained by the +CGMI AT command.
+
+               string Model [readonly, optional]
+
+                       String representing the model of the modem device.
+
+                       This is usually obtained by the +CGMM AT command.
+
+               string Revision [readonly, optional]
+
+                       String representing the revision of the modem device.
+
+                       This is usually obtained by using the +CGMR AT command.
+
+               string Serial [readonly, optional]
+
+                       String represeting the serial number of the modem
+                       device.
+
+                       This is usually obtained by using the +CGSN AT command.
+
+               array{string} Features [readonly]
+
+                       List of currently enabled features. It uses simple
+                       string abbreviations like "sms", "sim" etc.
+
+               array{string} Interfaces [readonly]
+
+                       Set of interfaces currently supported by the mode
+                       device. The set depends on the state of the device
+                       (registration status, SIM inserted status,
+                       network capabilities, device capabilities, etc.)
+
+                       TODO: Better to split this into Status and
+                             Capabilites properties?
+
+               string Type [readonly]
+
+                       Indicates whether the modem is virtual or a real
+                       hardware one. This information should only be used
+                       to identify which componet (e.g. ConnMan or a phone
+                       dialer) should take control over the modem. It does
+                       not give any hints on which Interfaces will be
+                       supported by this modem.
+
+                       Possible values are "hfp", "sap" and "hardware".
diff --git a/doc/network-api.txt b/doc/network-api.txt
new file mode 100644 (file)
index 0000000..83a2bc0
--- /dev/null
@@ -0,0 +1,208 @@
+Network registration hierarchy
+==============================
+
+Service                org.ofono
+Interface      org.ofono.NetworkRegistration
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns all network registration properties. See the
+                       properties section for available properties.
+
+               void Register()
+
+                       Attempts to register to the default network. The
+                       default network is normally selected by the settings
+                       from the SIM card.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+                                        [service].Error.AccessDenied
+
+               array{object,dict} GetOperators()
+
+                       Retrieve array of operator object and properties.
+
+                       This method can be used to retrieve the current
+                       operator list.  This is either an empty list (when
+                       not registered to any network) or a list with one
+                       or more operators (when registered).
+
+                       This list will also return cached values of previously
+                       seen networks.  Manual updates to list can only be
+                       done via the Scan method call.
+
+               array{object,dict} Scan()
+
+                       Runs a network operator scan to discover the currently
+                       available operators.  This operation can take several
+                       seconds, and up to several minutes on some modems.
+                       This can be used to help the user determine what is
+                       the best operator to use if forced to roam on a
+                       foreign network.
+
+                       NOTE: The operator scan can interfere with any active
+                       GPRS contexts.  Expect the context to be unavailable
+                       for the duration of the operator scan.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+                                        [service].Error.AccessDenied
+
+Signals                PropertyChanged(string property, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string Mode [readonly]
+
+                       The current registration mode. The default of this
+                       is "auto", but can be changed during operation. This
+                       property will change to "manual" if the Register()
+                       method of an operator is called.
+
+                       The possible values are:
+                               "auto"       Network registration is performed
+                                            automatically.
+                               "auto-only"  Network registration is performed
+                                            automatically, and manual
+                                            selection is disabled.
+                               "manual"     Network operator is selected
+                                            manually. If the operator is
+                                            currently not selected,
+                                            registration is not attempted.
+
+               string Status [readonly]
+
+                       The current registration status of a modem.
+
+                       The possible values are:
+                               "unregistered"  Not registered to any network
+                               "registered"    Registered to home network
+                               "searching"     Not registered, but searching
+                               "denied"        Registration has been denied
+                               "unknown"       Status is unknown
+                               "roaming"       Registered, but roaming
+
+               uint16 LocationAreaCode [readonly, optional]
+
+                       Contains the current location area code.
+
+                       TODO: Agent based location signalling would be better.
+
+               uint32 CellId [readonly, optional]
+
+                       Contains the current network cell id.
+
+                       TODO: Agent based location signalling would be better.
+
+               string MobileCountryCode [readonly, optional]
+
+                       Contains the Mobile Country Code (MCC).  This is
+                       repeated here for convenience.  It can also be obtained
+                       from the NetworkOperator interface.
+
+               string MobileNetworkCode [readonly, optional]
+
+                       Contains the Mobile Network Code (MNC).  This is
+                       repeated here for convenience.  It can also be obtained
+                       from the NetworkOperator interface.
+
+               string Technology [readonly, optional]
+
+                       Contains the technology of the current network.
+
+                       The possible values are: "gsm", "edge", "umts", "hspa",
+                                                       "lte"
+
+               string Name [readonly]
+
+                       Contains the current operator name, suitable for
+                       display on the idle screen or an empty string if
+                       not registered to a network.
+
+               byte Strength [readonly, optional]
+
+                       Contains the current signal strength as a percentage
+                       between 0-100 percent.
+
+               string BaseStation [readonly, optional]
+
+                       If the Cell Broadcast service is available and
+                       properly configured, this attribute will contain the
+                       name of the current service cell.  This is typically a
+                       descriptive name of the local area, e.g. name of the
+                       suburb.  If no name is provided or becomes
+                       unavailable, this property will not be returned by
+                       GetProperties or will be set to an empty string.
+
+
+Network operator hierarchy
+==========================
+
+Service                org.ofono
+Interface      org.ofono.NetworkOperator
+Object path    [variable prefix]/{modem0,modem1,...}/{operator0,operator1,...}
+
+Methods                dict GetProperties()
+
+                       Returns all network operator properties. See the
+                       properties section for available properties.
+
+               void Register()
+
+                       Attempts to register to this network operator.
+
+                       The method will return immediately, the result should
+                       be observed by tracking the NetworkRegistration Status
+                       property.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+                                        [service].Error.AccessDenied
+
+Signals                PropertyChanged(string property, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string Name [readonly]
+
+                       Contains the name of the operator, suitable for using
+                       as a string handle in a UI widget used for manual
+                       network selection. For a proper display name of the
+                       current operator, use the Name property of the
+                       NetworkRegistration interface instead.
+
+               string Status [readonly]
+
+                       Contains the status of the operator.
+
+                       The possible values are: "unknown", "available",
+                                                "current" and "forbidden"
+
+               string MobileCountryCode [readonly, optional]
+
+                       Contains the Mobile Country Code (MCC).
+
+               string MobileNetworkCode [readonly, optional]
+
+                       Contains the Mobile Network Code (MNC)
+
+               array{string} Technologies [readonly, optional]
+
+                       Contains a list of technologies currently available
+                       from this network provider.
+
+                       The possible values are: "gsm", "edge", "umts", "hspa",
+                                                       "lte"
+
+               string AdditionalInformation [readonly, optional]
+
+                       Contains a short description of the operator.  This is
+                       typically stored on the SIM and might be available
+                       only for select operators.
diff --git a/doc/ofono-paper.txt b/doc/ofono-paper.txt
new file mode 100644 (file)
index 0000000..ec6d01b
--- /dev/null
@@ -0,0 +1,172 @@
+oFono - Open Source Telephony
+*******************************************************************************
+
+1.0 Introduction
+
+Linux and other open source components are now used extensively on both desktop
+and mobile embedded devices.  They provide networking, power management,
+database and other core OS infrastructure.  However, up to this point no
+viable open source solution for mobile telephony existed.  oFono aims to
+change that; it is a telephony host stack specifically targeted at both
+mobile embedded and desktop systems.
+
+Launched on May 11, 2009 oFono aims to provide a solid framework for builidng
+3GPP GSM/UMTS User Equipment (UE) standard compliant devices.  Support for
+CDMA/EVDO technologies is also planned.  The goal of oFono is to provide an
+easy to use, high-level API for applications.  This is accomplished by keeping
+the core logic within the daemon, taking care of standards compliance and
+exposing only the need-to-know aspects to the application.
+
+The license for oFono was chosen as GPLv2.  This means that all core services
+and plugins for oFono must be Open Source.  oFono accepts GPLv2 or any
+GPL-compatible BSD license.  However, since oFono provides a D-Bus API, user
+interface applications can be of any license.
+
+2.0 Design Philosophy
+
+2.1 Modern
+
+oFono aims to be a modern implementation, ready for the 21st century.  From
+the very beginning oFono was designed with support of multiple technologies
+and device types in mind.  It is also designed to support multiple active
+devices simultenously.  This enables greater flexibility and enables usecases
+not possible with current solutions.
+
+oFono explicitly chooses not to support some of the more archaic features
+of GSM.  Specifically only limited use of the SIM for Phonebook support is
+enabled.  SIM storage for incoming and outgoing Short Messages (SMS) is also
+not supported.  The use of these features does not make sense on the current
+generation of devices, and introduces unnessary complexity.
+
+2.2 Fast and Light
+
+One of the main constraints for oFono's design was to make it extremely
+performant on resource-constrainted embedded devices.  This means that
+high-level languages like Python could not be used and library dependencies
+had to be kept to a minimum.  oFono is thus implemented in C and has minimial
+dependencies: libdbus, glib.  The reference drivers introduce two other library
+dependencies, gatchat and gisi, which are linked statically.
+
+2.3 Standards Compliant
+
+oFono is meant to be used in commercial systems, so standards compliance is a
+primary consideration from the very beginning.  Whenever possible oFono takes
+care of the gory details.  This includes the particulars of SMS decoding,
+de-fragmentation and duplicate detection; network operator name display;
+message waiting indicator processing and reporting; emergency dialing numbers,
+service numbers and subscriber number management; supplementary service
+control via strings defined in 3GPP TS 22.030.
+
+3.0 Architecture
+
+oFono provides a flexible, modular and extensible architecture with four main
+components: core daemon, oFono atoms, drivers and plugins.
+
+3.1 Core Daemon
+
+Core daemon provides base level services within oFono, namely the loading of
+plugins and drivers; utility APIs for decoding, encoding and fragmentation of
+binary SMS pdus; utility APIs for reading and writing to the SIM, and
+interpreting the contents of the low-level Element File (EF) contents; utility
+APIs for character set conversion; utility APIs for decoding, duplicate
+detection and pagination of cell broadcasts; and detection of and communication
+between oFono atoms.
+
+A big part of the core daemon is the modem device abstraction.  Each device is
+managed independently, and several devices can be present and active in the
+system at the same time.  The technologies for each device are not restricted
+in any way, and can be customized via the use of drivers.
+
+3.2 oFono Atoms
+
+oFono atoms provide a clear abstraction API for the applications based on
+D-Bus.  There are currently over a dozen atoms within oFono, providing access
+to core functionality like voice calls, supplementary services, short message
+service (SMS), cell broadcast (CBS) and sim management.
+
+Atoms can detect the presence of other atoms and use information provided by
+other atoms to provide extra functionality.  For instance, the Network
+Registration atom will read low-level EF files whenever a SIM is present, and
+provide enhanced operator information if the SIM is thus provisioned.
+
+3.3 Drivers
+
+oFono provides a way to integrate multiple device technologies through its
+driver mechanism.  All modem devices and atoms provide an abstract interface
+for low-level operations.  This interface is based on 3GPP TS 27.007 "AT
+command set for User Equipment" and 3GPP TS 27.005 "DTE-DCE interface for SMS
+and CBS".  oFono assumes that all operations are fully asynchronous.
+
+This means that oFono can accommodate a wide variety of devices, including
+full-featured modems (AT command based and otherwise), data-only cards, and
+modem like devices (e.g. Bluetooth Handsfree and Sim Access Profile devices,
+etc.)
+
+oFono provides a reference AT command driver, which should work for the
+majority of AT command based modems in the market.  oFono also includes an ISI
+protocol based driver, which will enable the majority of Nokia devices to be
+used.  Finally a Bluetooth Handsfree Profile (HFP) driver is also planned.
+
+3.4 Plugins
+
+Plugins provide a final piece of the puzzle.  These are used to provide device
+drivers and atom drivers.  They can also be used to extend oFono or interact
+with other system services.  For example, Moblin uses oFono plugins to store
+all call history information within Evolution Data Server.
+
+4.0 D-Bus API
+
+Much thought has been given to how user interface applications will interact
+with oFono.  The goal of the oFono API is to make the User Interface (UI)
+application writer's job as easy as possible.  This is accomplished in two
+ways: exposing only the essential details to the application and provide a
+high level API.  To accomplish this, oFono sticks to the following four
+basic principles of API design: Consistent, Minimal, Complete and Easy to Use.
+
+4.1 Consistent
+
+As mentioned previously, each atom provides a high-level D-Bus API, which is
+referred to as an interface.  Each interface has a well-defined set of
+properties and two special methods for managing them: GetProperties and
+SetProperty.
+
+All names within oFono are CamelCased and this naming convention is strictly
+enforced.  This means that once the application writer is comfortable using
+one Interface, they should be able to quickly pick up others.
+
+4.2 Minimal & Complete
+
+A common pitfal of API design is exposing too much and assuming that the user
+has the same level of knowledge as the designer.  Almost always these
+assumptions are incorrect and lead to incorrect and inefficient use of the
+API.  This in turn leads to applications that are hard to write, maintain and
+replace.
+
+Instead the API should be minimal; it should make it easy and apparent to the
+user how to accomplish a particular task he or she is interested in.  oFono
+accomplishes this by doing as much as possible within the core and only
+exposing the information which is actually required to be shown to the user.
+
+4.3 Easy to Use
+
+While the above three principles generally provide good results, a process of
+refinement can still be applied.  oFono works with user interface designers
+and implementers to continually improve its API.  This means that if a
+particular feature is found to be inefficient in practice, it refined and
+improved as quickly as possible.
+
+5.0 Conclusion
+
+oFono provides a full host protocol stack for telephony aware applications.
+Today, it enables most of the commonly used features defined by 3GPP standards,
+including voicecalls, sms, cbs, supplementary services and network registration.
+Data connections using GPRS and 3G features are being actively worked on.  It
+thus provides a viable, open source solution to system implementors seeking to
+add telephony capabilities to Linux desktop and mobile devices.
+
+6.0 Resources
+
+Website: http://ofono.org
+Mailing List: ofono@ofono.org
+IRC: #ofono on freenode
+
diff --git a/doc/ofonod.8 b/doc/ofonod.8
new file mode 100644 (file)
index 0000000..7bb908c
--- /dev/null
@@ -0,0 +1,38 @@
+.\"
+.\" ofonod(8)
+.\"
+.\" Copyright (C) 2009  Collabora Ltd.
+.TH ofonod 8 "Jul 2009"
+.SH NAME
+ofonod \- oFono mobile telephony daemon
+.SH SYNOPSIS
+.B "ofonod [options]"
+.SH DESCRIPTION
+.B ofonod
+is a daemon which provides an oFono stack for interfacing mobile telephony devices.
+oFono is controlled through \fID-Bus\fP; for example, one can tell
+.B ofonod
+to send AT commands over /dev/rfcomm0 by calling the \fID-Bus\fP method org.ofono.at.Manager.Create.
+.I "/etc/dbus-1/system.d/ofono.conf"
+is used to manage \fID-Bus\fP permissions for oFono.
+.SH OPTIONS
+.TP
+.B --debug, -d
+Enable debug information output. Note multiple arguments to -d can be
+specified, colon, comma or space separated. The arguments are relative
+source code filenames for which debugging output should be enabled;
+output shell-style globs are accepted (e.g.: "plugins/*:src/main.c").
+.TP
+.B --nodetach, -n
+Don't run as daemon in background.
+.TP
+.SH SEE ALSO
+.PP
+\&\fIdbus-send\fR\|(1)
+
+.SH FILES
+.BR /etc/dbus-1/system.d/ofono.conf
+.SH AUTHOR
+.br
+This man page was written by Andres Salomon <dilinger@collabora.co.uk>.
+
diff --git a/doc/overview.txt b/doc/overview.txt
new file mode 100644 (file)
index 0000000..b121b90
--- /dev/null
@@ -0,0 +1,211 @@
+oFono - Open Source Telephony
+*****************************
+
+Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+
+
+Mission statement
+=================
+
+The overall goal of the oFono project is to create a telephony host stack
+for embedded/mobile and desktop systems based on Linux.
+
+It currently targets GSM/UMTS User Equipment (UE) based on 3GPP standards,
+but is of course not limited to that. Extensions for other telephony systems
+like CDMA/EVDO are more than welcome.
+
+Within oFono there is clear abstraction between the application interfaces
+based on D-Bus, the hardware level (via drivers) and the integration with
+other system components (plugins). The whole architecture is modular and
+flexible.
+
+
+Telephony features
+==================
+
+The oFono stack will support the majority of the features from the 3GPP
+specification, but not all of them. The whole standard is pretty complex
+and some features are not used in any mobile network so far, some of them
+are outdated and have no relevance anymore. This paragraph tries to give
+some insights on what has been implemented so far, what is coming in the
+future and especially what will not be part of oFono.
+
+Current implemented features:
+
+       Modem abstraction
+
+               Currently there are two modem drivers available.
+
+               The "atmodem" driver handles hardware based on the 3GPP
+               TS 27.007 standard.
+
+               The "isimodem" driver handles Nokia based PhoNet modems.
+
+       Network registration
+
+               The network registration interface contains support for
+               network detection. It also handles the automatic or manual
+               registration to a mobile network.
+
+               Additional information about the current network can be
+               easily retrieved and displayed to the user.
+
+       Network time indications
+
+               oFono includes support for receiving Network Identity and
+               Timezone (NITZ) indications, and handles processing of
+               this information via system-specific plugins.
+
+               An example network time plugin is provided that simply
+               prints out the received time information. A more advanced,
+               real-world plugin could automatically set the system time
+               based on the received information.
+
+       Voice call handling
+
+               The voice call interface handles simple call creation and
+               termination. It also supports 3way-calling and multi-party
+               features.
+
+               This is only for voice control. It doesn't contain support
+               for the voice data path/routing.
+
+       Advanced voice call control
+
+               Features like COLR/CLIR/CLIP/COLP are supported by the
+               voice call handling interface.
+
+               The support for call forwarding and call waiting is also
+               present.
+
+               Interfaces for call barring and advice of charge do exist,
+               but highly depend on if the operator supports them.
+
+       Call history
+
+               The call history is realized via a plugin interface and
+               not handled directly. This allows an easy integration with
+               storage systems like Evolution-Data-Server.
+
+       Phonebook support
+
+               The storage of the SIM card is not used. The only supported
+               feature is to export the contacts stored on the SIM card to
+               some third-part entity.
+
+               SIM card storage is limited, slow and not flexible enough
+               for modem telephony applications.
+
+       Short message service
+
+               The SMS support for text messages is available.  oFono
+               supports concatenated messages of up to 255 segments, UCS2 and
+               GSM alphabets as well as extended alphabets.  Delivery
+               confirmations (Status Reports) are also supported.
+
+               Selection of SMS bearer settings is supported through the
+               'Bearer' property on the SmsManager interface.
+
+       SIM PIN handling
+
+               SIM PIN locks, network and service provider locks are
+               supported. The SIM Manager also handles retry counter for PIN,
+               PIN2, PUK and PUK2.
+
+       Cell Broadcast
+
+               Cell broadcasts should be fully supported by oFono, but have
+               not been well tested.  Base station name ids have been
+               confirmed to work.
+
+       GPRS
+
+               GPRS data connections are fully supported by oFono.  Multiple
+               active GPRS contexts are supported. IPv6 context support is
+               in progress.
+
+       Radio Access Settings
+
+               The radio settings interface contains support for selecting
+               the access selection mode and used frequency bands, and allows
+               enabling and disabling fast dormancy on hardware that support
+               this feature.
+
+       Sim Toolkit
+
+               The Sim Toolkit interface handles the SAT commands. Sim Toolkit
+               relies on the SimToolkit agent not only to get confirmation
+               from the user but also to inform user of the SAT initiated
+               operation status. Information on the Sim Toolkit feature
+               list can be found in features.txt.
+
+       Supplementary Services
+
+               The Supplementary Services interface handles both recognized
+               supplementary service control string and user/network initiated
+               unstructured supplementary service data (USSD).
+
+       GPS/Location Services
+
+               oFono provides a Location Reporting interface that enables
+               taking advantage of on-board GPS capabilities of modern modems.
+               oFono also provides an Assisted Satellite Navigation interface
+               that allows feeding assistance data to the GPS unit from the
+               network as well as E911 services.
+
+Work in progress features:
+
+       GPRS
+
+               IPv6 PS context support is under development.
+
+       Modem Emulator
+
+               To enable DialUp Networking (over Bluetooth or USB), and to
+               allow Bluetooth HandsFree / Headset support, oFono will expose
+               some 'fake' modem to enable the communication with car kits, or
+               other devices.
+
+Not implemented features:
+
+       SIM card storage
+
+               The SIM card storage will not be used by oFono. It is slow,
+               limited in size and flexibility. It is an outdated interface
+               that makes no sense in a modern telephony system.
+
+               Export of stored contacts from the SIM card is supported for
+               legacy reasons and to allow a smooth transition.
+
+       WAP support
+
+               The WAP feature is outdated and using full Internet access
+               with an embedded browser is the future.
+
+       MMS support
+
+               The MMS support should not be part of oFono itself. The best
+               idea is to implement a MMS service that runs in the user
+               session and uses oFono's SMS interface to listen for
+               notifications.
+
+               Similar to the split between BlueZ and OBEX daemon.
+
+               Especially when it comes to image conversion and other tasks
+               that MMS support requires it is important to NOT do this as
+               a system daemon.
+
+       EMS support
+
+               This is an Ericsson specific standard and not widely spread
+               across the handset manufactures.
+
+       Video telephony
+
+               Currently there are no plans to support this. The support
+               from the networks and available handsets are still limited.
+
+               This needs re-evaluation once such a service becomes more
+               prominent.
+
+               A similar split like with MMS might be a good idea.
diff --git a/doc/phonebook-api.txt b/doc/phonebook-api.txt
new file mode 100644 (file)
index 0000000..92c0e0b
--- /dev/null
@@ -0,0 +1,18 @@
+Phonebook hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.Phonebook
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                string Import()
+
+                       Returns the contents of the SIM and ME phonebook in
+                       VCard 3.0 format.  If several entries are determined
+                       to be related to the same contact, then they are
+                       merged into a single VCard entry.
+
+                       The phonebook is returned as a single UTF8 encoded
+                       string with zero or more VCard entries.
+
+                       Possible Errors: [service].Error.InProgress
diff --git a/doc/pushnotification-api.txt b/doc/pushnotification-api.txt
new file mode 100644 (file)
index 0000000..6938826
--- /dev/null
@@ -0,0 +1,45 @@
+Push Notification hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.PushNotification
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                void RegisterAgent(object path)
+
+                       Registers an agent which will be called whenever a
+                       new Smart Messaging based SMS arrives.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void UnregisterAgent(object path)
+
+                       Unregisters an agent.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.Failed
+
+PushNotificationAgent Hierarchy [experimental]
+===============
+
+Service                unique name
+Interface      org.ofono.PushNotificationAgent
+Object path    freely definable
+
+Methods                void ReceiveNotification(array{byte} notification, dict info)
+
+                       Requests the agent to process a new SMS that has
+                       arrived containing a WAP PUSH.  The dictionary
+                       info contains 'Sender', 'LocalSentTime' and
+                       'SentTime' properties.
+
+                       Possible Errors: None
+
+               void Release() [noreply]
+
+                       Agent is being released, possibly because of oFono
+                       terminating, SMS interface is being torn down or modem
+                       off.  No UnregisterAgent call is needed.
diff --git a/doc/radio-settings-api.txt b/doc/radio-settings-api.txt
new file mode 100644 (file)
index 0000000..9f9256f
--- /dev/null
@@ -0,0 +1,134 @@
+Radio settings hierarchy
+========================
+
+Service                org.ofono
+Interface      org.ofono.RadioSettings
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns all radio access properties. See the
+                       properties section for available properties.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as readwrite are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string TechnologyPreference [readwrite]
+
+                       The current radio access selection mode, also known
+                       as network preference.
+
+                       The possible values are:
+                               "any"   Radio access technology
+                                       selection is done automatically,
+                                       based on reception and
+                                       availability.
+                               "gsm"   Only GSM used for radio access.
+                               "umts"  Only UMTS used for radio access.
+                               "lte"   Only LTE used for radio access.
+
+               string GsmBand [readwrite, optional]
+
+                       Frequency band in which the modem is allowed to
+                       operate when using "gsm" mode. Setting this property
+                       has an immediate effect on modem only if
+                       TechnologyPreference is set to "gsm" or "any".
+                       Otherwise the value is kept and applied whenever modem
+                       uses this mode.
+
+                       The possible values are:
+                               "any"   Frequency band is selected
+                                       automatically by modem.
+                               "850"   Operate only on 850 MHz.
+                               "900P"  Operate only on 900 MHz, known as
+                                       Primary GSM-900 Band
+                               "900E"  Operate only on 900 MHz, known as
+                                       Extended GSM-900 Band.
+                               "1800"  Operate only on 1800 MHz, known as DCS.
+                               "1900"  Operate only on 1900 MHz, known as PCS.
+
+               string UmtsBand [readwrite, optional]
+
+                       Frequency band in which the modem is allowed to
+                       operate when using "umts" mode. Setting this property
+                       has an immediate effect on modem only if
+                       TechnologyPreference is set to "umts" or "any".
+                       Otherwise the value is kept and applied whenever modem
+                       uses this mode.
+
+                       The possible values are:
+                               "any"           Frequency band is selected
+                                               automatically by modem.
+                               "850"           Operate only on 850 MHz, known
+                                               as CLR (class V).
+                               "900"           Operate only on 900 MHz, known
+                                               as GSM (class VIII).
+                               "1700AWS"       Operate only on 1700 MHz, known
+                                               as AWS (class IV).
+                               "1900"          Operate only on 1900 MHz, known
+                                               as PCS (class II).
+                               "2100"          Operate only on 2100 MHz, known
+                                               as IMT (class I).
+
+               boolean FastDormancy [readwrite, optional]
+
+                       This property will enable or disable the fast
+                       dormancy feature in the modem. Fast dormancy
+                       refers to a modem feature that allows the
+                       modem to quickly release radio resources after
+                       a burst of data transfer has ended. Normally,
+                       radio resources are released by the network
+                       after a timeout configured by the network.
+                       Fast dormancy allows the modem to release the
+                       radio resources more quickly.
+
+                       Fast dormancy is a major power-saving feature
+                       for mobile devices. Typically, fast dormancy
+                       would be enabled when the device is not being
+                       interactively used by a human user and only
+                       networking applications with keep-alive
+                       traffic are active (e.g. mail client or a
+                       presence application). In this case it is
+                       desirable to release radio resources quickly
+                       after a keep-alive transaction has ended,
+                       since typically no network traffic will occur
+                       until the next keep-alive transaction. Fast
+                       dormancy should not be enabled during
+                       interactive use because the release and setup
+                       of radio resources introduces perceivable
+                       delay for the end user.
+
+                       The fast dormancy implementation in the modem
+                       is vendor specific. The implementation should
+                       try to release radio resources more quickly,
+                       when the situation allows it, but should also
+                       take care not to increase the signalling load
+                       on the cellular network by releasing and
+                       re-establishing radio resources too often. The
+                       modem should adjust its behaviour to the 3GPP
+                       release supported by the network and the
+                       parameters set by the operator.
+
+                       Fast dormancy can be ignored for externally
+                       powered modems such as USB sticks or PCI
+                       devices. If the modem does not support such a
+                       feature the property should never be exposed
+                       to the user.
diff --git a/doc/release-faq.txt b/doc/release-faq.txt
new file mode 100644 (file)
index 0000000..4174ba2
--- /dev/null
@@ -0,0 +1,42 @@
+oFono release FAQ
+*****************
+
+What is the release cycle?
+==========================
+
+There is no clear specified release cycle.  The project follows the general
+open source paradigm of release early and release often.  Historically the
+release cadence has been about two to three weeks.
+
+The two weeks release cycle is a general rule of thumb.  It will never be
+precise down to an exact day.  The decision to release is driven by the
+code flow and changes made during that cycle.  When a closed set of features
+has been merged, then a new release is considered.  When a lot changes are
+merged, then the release cycle can be decreased down to weekly releases.
+And in case not many changes have been made and no new features are merged,
+it can happen that the release cycle becomes as long as one month.
+
+In addition to the normal releases during a cycle, there could be also
+so called brown-paper-bag type of releases.  The releases are fixing
+critical issues with the previous release.  Normally they happen in case a
+serious bug or regression slipped into the source and where it makes sense
+to fix it right away.  The goal is to keep this type of releases to a bare
+minimum, but they do happen every now and then.  From past releases they
+are to be expected around three times per year.
+
+
+What does the release number mean?
+==================================
+
+Every release contains a major and a minor version.  The major version only
+indicates an API version and it should not change until the D-Bus API is
+backwards incompatible.  The same major version can contain new version of
+the API as long as it is backward compatible.  This allows for extensions
+of the D-Bus API within the same major version.
+
+The minor version number is just an increasing number and has itself no
+special meaning.  The minor version will increase until a new backwards
+incompatible API is required and a new major number is used.
+
+There is no stable API guarantee for the internal plugin API.  The release
+numbers are not covering this.
diff --git a/doc/sim-api.txt b/doc/sim-api.txt
new file mode 100644 (file)
index 0000000..ba8fdb1
--- /dev/null
@@ -0,0 +1,197 @@
+SimManager hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.SimManager
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns SIM properties for the modem object.  See
+                       the properties section for available properties.
+
+               void ChangePin(string type, string oldpin, string newpin)
+
+                       Changes the pin given by string type.
+
+                       Possible Errors: [service].Error.NotImplemented
+                                        [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void EnterPin(string type, string pin)
+
+                       Enters the currently pending pin.  The type value must
+                       match the pin type being asked in the PinRequired
+                       property.
+
+                       Possible Errors: [service].Error.NotImplemented
+                                        [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void ResetPin(string type, string puk, string newpin)
+
+                       Provides the unblock key to the modem and if correct
+                       resets the pin to the new value of newpin.
+
+                       Possible Errors: [service].Error.NotImplemented
+                                        [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void LockPin(string type, string pin)
+
+                       Activates the lock for the particular pin type.  The
+                       device will ask for a PIN automatically next time the
+                       device is turned on or the SIM is removed and
+                       re-inserted.  The current PIN is required for the
+                       operation to succeed.
+
+                       Possible Errors: [service].Error.NotImplemented
+                                        [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void UnlockPin(string type, string pin)
+
+                       Deactivates the lock for the particular pin type.  The
+                       current PIN is required for the operation to succeed.
+
+                       Possible Errors: [service].Error.NotImplemented
+                                        [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               array{byte} GetIcon(byte id)
+
+                       Obtain the icon given by id.  Only ids greater than 1
+                       are valid.  XPM format is currently used to return the
+                       icon data.
+
+                       Possible Errors: [service].Error.NotImplemented
+                                        [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Present [readonly]
+
+                       True if a SIM card is detected.  There are
+                       no other properties if false.
+
+               string SubscriberIdentity [readonly, optional]
+
+                       Contains the IMSI of the SIM, if available.
+
+               string MobileCountryCode [readonly, optional]
+
+                       Contains the Mobile Country Code (MCC) of the home
+                       network (not to be confused with the currently
+                       registered network reported on NetworkRegistration
+                       interface) and is read directly from the SIM if
+                       available.
+
+               string MobileNetworkCode [readonly, optional]
+
+                       Contains the Mobile Network Code (MNC) of the home
+                       network (not to be confused with the currently
+                       registered network reported on NetworkRegistration
+                       interface) and is read directly from the SIM if
+                       available.
+
+               array{string} SubscriberNumbers [readwrite]
+
+                       Contains the list of subscriber numbers.  This is
+                       usually stored in the EFmsisdn sim file.
+
+               array{string} PreferredLanguages [readonly, optional]
+
+                       Contains the list of preferred languages from the SIM,
+                       if available.
+
+               dict{string,string} ServiceNumbers [readonly, optional]
+
+                       Contains a dictionary of service dialing numbers from
+                       the SIM, if available.
+
+               string PinRequired [readonly]
+
+                       Contains the string type of the pin required by the
+                       modem.  The possible values are:
+                               "none" - Nothing is required
+                               "pin" - SIM PIN is required
+                               "phone" - Phone-to-SIM PIN is required
+                               "firstphone" - Phone-to-very-first SIM
+                                               PIN is required
+                               "pin2" - SIM PIN2 is required
+                               "network" - Network Personalization password is
+                                               required
+                               "netsub" - Network subset personalization
+                                               password is required
+                               "service" - Service Provider personalization
+                                               password is required
+                               "corp" - Corporate personalization password
+                                               is required
+                               "puk" - SIM PUK is required
+                               "firstphonepuk" - Phone-to-very-first SIM PUK is
+                                               required
+                               "puk2" - SIM PUK2 is required
+                               "networkpuk" - Network personalization unblocking
+                                               password is required
+                               "netsubpuk" - Network subset personalization
+                                               unblocking password is required
+                               "servicepuk" - Service provider personalization
+                                               unblocking password is required
+                               "corppuk" - Corporate personalization unblocking
+                                               password is required
+
+               array{string} LockedPins [readonly]
+
+                       Contains the pins that are currently locked and will
+                       require the user to enter the password at startup.
+                       Using LockPin and UnlockPin will result in changes to
+                       this property.
+
+                       The list contains elements of the same format as the
+                       PinRequired property.
+
+               string CardIdentifier [readonly]
+
+                       Contains the Integrated Circuit Card Identifer (ICCID)
+                       which is read directly from the SIM.
+
+               boolean FixedDialing [readonly]
+
+                       True if Fixed Dialing service is enabled in SIM card.
+
+                       If FDN is enabled, oFono halts the SIM initialization
+                       procedure and only emergency calls are allowed.
+
+               boolean BarredDialing [readonly]
+
+                       True if Barred Dialing service is enabled in SIM card.
+
+                       If BDN is enabled, oFono halts the SIM initialization
+                       procedure and only emergency calls are allowed.
+
+               dict{string,byte} Retries [readonly]
+
+                       Contains all the retry counters available. The possible
+                       values for the first field are the same as in
+                       PinRequired property. The second field contains is the
+                       counter for that pin type.
+
+                       This property is updated after each operation that
+                       might have changed the retry counters, i.e. calls to
+                       ChangePin(), EnterPin(), ResetPin() LockPin(),
+                       UnlockPin().
diff --git a/doc/smartmessaging-api.txt b/doc/smartmessaging-api.txt
new file mode 100644 (file)
index 0000000..637a5ef
--- /dev/null
@@ -0,0 +1,63 @@
+Smart Messaging hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.SmartMessaging
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                object SendAppointment(string to, array{bytes} appointment)
+
+                       Sends a vCalendar object in appointment to the number
+                       in to.  The object in appointment is not interpreted
+                       by oFono in any way.  If the object is too large to
+                       fit into a single SMS, it is fragmented as appropriate.
+                       This method call returns the object path of the queued
+                       SMS.
+
+               object SendBusinessCard(string to, array{bytes} card)
+
+                       Sends a vCard object in card to the number in to. The
+                       object in card is not interpreted by oFono in any way.
+                       If the object is too large to fit into a single SMS,
+                       it is fragmented as appropriate.  This method call
+                       returns the object path of the queued SMS.
+
+               void RegisterAgent(object path)
+
+                       Registers an agent which will be called whenever a
+                       new Smart Messaging based SMS arrives.
+
+               void UnregisterAgent(object path)
+
+                       Unregisters an agent.
+
+SmartMessagingAgent Hierarchy [experimental]
+===============
+
+Service                unique name
+Interface      org.ofono.SmartMessagingAgent
+Object path    freely definable
+
+Methods                void ReceiveAppointment(array{byte} appointment, dict info)
+
+                       Requests the agent to process a new SMS that has
+                       arrived containing a vCalendar object.  The info
+                       dictionary contains 'Sender', 'LocalSentTime' and
+                       'SentTime' properties.
+
+                       Possible Errors: None
+
+               void ReceiveBusinessCard(array{byte} card, dict info)
+
+                       Requests the agent to process a new SMS that has
+                       arrived containing a vCard object.  The info
+                       dictionary contains 'Sender', 'LocalSentTime' and
+                       'SentTime' properties.
+
+                       Possible Errors: None
+
+               void Release() [noreply]
+
+                       Agent is being released, possibly because of oFono
+                       terminating, SMS interface is being torn down or modem
+                       off.  No UnregisterAgent call is needed.
diff --git a/doc/stk-api.txt b/doc/stk-api.txt
new file mode 100644 (file)
index 0000000..471e5d6
--- /dev/null
@@ -0,0 +1,303 @@
+SimToolkit Hierarchy
+===============
+
+Service                org.ofono
+Interface      org.ofono.SimToolkit
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the SimToolkit object.  See the
+                       properties section for available properties.
+
+               void SelectItem(byte item, object agent)
+
+                       Selects an item from the main menu, thus triggering
+                       a new user session.  The agent parameter specifies
+                       a new agent to be used for the duration of the
+                       user session.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotSupported
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void RegisterAgent(object path)
+
+                       Registers a default agent to be used for SIM initiated
+                       actions such as Display Text, Get Inkey or Get Input.
+                       These can typically occur when a special SMS is
+                       received and might not involve interaction from the
+                       user.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void UnregisterAgent(object path)
+
+                       Unregisters the default agent.  If no agent is
+                       registered then unsolicited commands from the SIM
+                       are rejected.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+Properties     string IdleModeText [readonly]
+
+                       Contains the text to be used when the home screen is
+                       idle.  This text is set by the SIM and can change
+                       at any time.
+
+               byte IdleModeIcon [readonly]
+
+                       Contains the identifier of the icon accompanying
+                       the idle mode text.
+
+               array{struct{string, byte}} MainMenu [readonly]
+
+                       Contains the items that make up the main menu.  This
+                       is populated by the SIM when it sends the Setup Menu
+                       Proactive Command.  The main menu is always available,
+                       but its contents can be changed at any time.  Each
+                       item contains the item label and icon identifier.
+
+               string MainMenuTitle [readonly]
+
+                       Contains the title of the main menu.
+
+               string MainMenuIcon [readonly]
+
+                       Contains the identifier of the icon for the main menu.
+
+SimToolkitAgent Hierarchy
+===============
+
+Service                unique name
+Interface      org.ofono.SimToolkitAgent
+Object path    freely definable
+
+Methods                byte RequestSelection(string title, byte icon_id,
+                                       array{struct(string, byte)} items,
+                                       int16 default)
+
+                       Tells the agent to ask the user to select an item
+                       from the menu.  The default is set the the default
+                       item index or -1 if no default is provided.
+
+                       This function should return the index of the item or
+                       an error given below.
+
+                       Possible Errors: [service].Error.SimToolkit.GoBack
+                                        [service].Error.SimToolkit.EndSession
+
+                       Implementation notes:
+
+                       - Data / Navigation type not indicated
+                       - Soft key preferred not indicated
+                       - Help available ignored
+
+               void DisplayText(string text, byte icon_id, boolean urgent)
+
+                       Tells the agent to display text from the SIM.  The
+                       boolean urgent parameter tells the agent whether this
+                       is an urgent message and all other messages should be
+                       cleared prior to the display of this text.
+
+                       Possible Errors: [service].Error.SimToolkit.GoBack
+                                        [service].Error.SimToolkit.EndSession
+                                        [service].Error.SimToolkit.Busy
+
+                       Implementation notes:
+
+                       - High / normal priority indicated by urgent
+
+                       - The clear message after delay / wait for user to
+                         clear flags are handled as follows.  If the wait for
+                         user flag is set, then oFono sets a higher timeout
+                         for the Agent DisplayText method call.  It will then
+                         call Cancel() on the agent after this timeout
+                         expires and the "No response from user" terminal
+                         response is sent.  If the agent returns earlier from
+                         this method call, a terminal response "Command
+                         "performed successfully" is sent.
+
+                         It might be required to pass a flag to the UI to
+                         hint it that allocation of an 'acknowledgement'
+                         button is required in the case of a longer timeout.
+                         However, the UI can figure this out itself as well
+                         based on a timer threshold.  More input needed.
+
+                       - Immediate Response indication is handled internally,
+                         terminal response is sent immediately and no other
+                         indication is given to the user / agent.  Response
+                         from this method call is ignored and a short
+                         timeout is used for the method call.  Once another
+                         proactive command arrives, and the DisplayText is
+                         still pending, Cancel() is called.
+
+               string RequestInput(string alpha, byte icon_id,
+                               string default, byte min, byte max,
+                               boolean hide_typing)
+
+                       Tells the agent to request an input string from the
+                       user.  The alpha parameter and icon_id gives context
+                       to the user.  The default string contains the suggested
+                       default by the SIM.  The min and max parameters contain
+                       how many characters the user should enter.  The
+                       parameter hide_typing indicates whether user's typing
+                       should be opaque.
+
+                       Possible Errors: [service].Error.SimToolkit.GoBack
+                                        [service].Error.SimToolkit.EndSession
+
+                       Implementation notes:
+
+                       - It is still unclear how to handle gsm vs ucs2
+                         accepted alphabet selection.  Can the reduced
+                         character set functionality from SMS be applied
+                         here?  If not, then an extra gsm vs ucs2 acceptance
+                         argument needs to be added.
+
+               string RequestDigits(string alpha, byte icon_id,
+                                       string default, byte min, byte max,
+                                       boolean hide_typing)
+
+                       Same as GetInput but only digit characters (0-9, *#+)
+                       are expected.
+
+                       Possible Errors: [service].Error.SimToolkit.GoBack
+                                        [service].Error.SimToolkit.EndSession
+
+               string RequestKey(string alpha, byte icon_id)
+
+                       Tells the agent to request a single input key from
+                       the user.  The alpha parameter contains the context
+                       for the request.
+
+                       Possible Errors: [service].Error.SimToolkit.GoBack
+                                        [service].Error.SimToolkit.EndSession
+
+               string RequestDigit(string alpha, byte icon_id)
+
+                       Same as above, but only digits (0-9, *#+) are
+                       expected.
+
+                       Possible Errors: [service].Error.SimToolkit.GoBack
+                                        [service].Error.SimToolkit.EndSession
+
+               boolean RequestConfirmation(string alpha, byte icon_id)
+
+                       Asks the agent to get confirmation from the user.
+
+                       Possible Errors: [service].Error.SimToolkit.GoBack
+                                        [service].Error.SimToolkit.EndSession
+
+               boolean ConfirmCallSetup(string information, byte icon_id)
+
+                       Asks the agent to request user to confirm an
+                       outgoing call setup.  If confirmed, the next new
+                       outgoing call reported by VoiceCallManager will
+                       have the Information and Icon properties set to
+                       inform the user.  Hanging up before the call is
+                       connected will cause EndSession reply to be sent.
+
+                       Possible Errors: [service].Error.SimToolkit.EndSession
+
+               void PlayTone(string tone, string text, byte icon_id)
+
+                       Tells the agent to play an audio tone once.  The
+                       method should return once the tone is finished
+                       playing.  The text parameter contains an optional
+                       text to be displayed to the user.  The following
+                       tones are defined:
+                               "dial-tone"
+                               "busy"
+                               "congestion"
+                               "radio-path-acknowledge"
+                               "radio-path-not-available"
+                               "error"
+                               "call-waiting"
+                               "ringing-tone"
+                               "general-beep"
+                               "positive-acknowledgement"
+                               "negative-acknowledgement"
+                               "user-ringing-tone"
+                               "user-sms-alert"
+                               "critical" (high priority)
+                               "vibrate"
+                               "happy"
+                               "sad"
+                               "urgent-action"
+                               "question"
+                               "message-received"
+
+                       Possible Errors: [service].Error.SimToolkit.EndSession
+
+               void LoopTone(string tone, string text, byte icon_id)
+
+                       Tells the agent to reproduce an audio tone in a
+                       loop until the method call is cancelled.  See
+                       PlayTone() above for the list of possible tone names.
+                       The text parameter contains an optional text to
+                       be displayed to the user.
+
+                       Possible Errors: [service].Error.SimToolkit.EndSession
+
+               void DisplayActionInformation(string text,
+                                               byte icon_id)
+
+                       Supplies a text string and/or icon concerning the
+                       current activity in the terminal and UICC.  The
+                       text should be displayed to the user on screen
+                       until the call is canceled using Cancel().  If the
+                       method returns it is assumed that the user has
+                       explicitly dismissed the dialog and no Cancel() is
+                       sent.
+
+               boolean ConfirmLaunchBrowser(string information,
+                                               byte icon_id, string url)
+
+                       Asks the agent to request user to confirm launch
+                       browser. If confirmed, then the agent should send
+                       confirmation message to oFono and then should open
+                       the launch browser with the given url.
+
+               void DisplayAction(string text, byte icon_id)
+
+                       Supplies a text string and/or icon concerning the
+                       current activity in the terminal and UICC. The
+                       text should be displayed to the user on screen
+                       until the call is canceled using Cancel() or until the
+                       user decides to end the session.
+
+                       Possible Errors: [service].Error.SimToolkit.EndSession
+
+               boolean ConfirmOpenChannel(string information,
+                                               byte icon_id) [experimental]
+
+                       Asks the agent to request user to confirm the channel
+                       set-up.
+
+                       Possible Errors: [service].Error.SimToolkit.EndSession
+
+               void Cancel() [noreply]
+
+                       Asks the agent to cancel any ongoing operation in
+                       progress.  This is usually either because the agent
+                       is taking too long to respond, the Sim Application
+                       has terminated the session or a task has finished.
+
+               void Release() [noreply]
+
+                       Agent is being released, possibly because of oFono
+                       terminating, SimToolkit interface torn down or modem
+                       off.  If the agent is registered as a global agent,
+                       no UnregisterAgent call is expected.
diff --git a/doc/supplementaryservices-api.txt b/doc/supplementaryservices-api.txt
new file mode 100644 (file)
index 0000000..e855ead
--- /dev/null
@@ -0,0 +1,179 @@
+SupplementaryServices hierarchy
+==========================
+
+Service                org.ofono
+Interface      org.ofono.SupplementaryServices
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                string, variant Initiate(string command)
+
+                       If the command is a recognized supplementary service
+                       control string, the corresponding SS request is made
+                       and the result is returned.
+
+                       Otherwise the command is sent to the network
+                       initiating a USSD session. When the request is handled
+                       by the appropriate node of the network, the
+                       method returns the response or an appropriate
+                       error. The network may be awaiting further response
+                       from the ME after returning from this method and no
+                       new command can be initiated until this one is
+                       cancelled or ended.
+
+                       The output arguments are described in section
+                       "Initiate method outptut arguments" below.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               string Respond(string reply)
+
+                       Send a response to the network either when
+                       it is awaiting further input after Initiate()
+                       was called or after a network-initiated request.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotActive
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void Cancel()
+
+                       Cancel an ongoing USSD session, mobile- or
+                       network-initiated.
+
+                       Possible Errors: [service].Error.NotActive
+                                        [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               dict GetProperties()
+
+                       Returns Supplementary Services related properties. See
+                       the properties section for available properties.
+
+Signals                NotificationReceived(string message)
+
+                       Signal is emitted on a network-initiated USSD
+                       request for which no response is needed.
+
+               RequestReceived(string message)
+
+                       Signal is emitted on a network-initiated USSD
+                       request for which a response must be sent using
+                       the Respond method unless it is cancelled or
+                       the request is not supported.
+
+               PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+Properties     string State [readonly]
+
+                       Reflects the state of current USSD session.  The
+                       values have the following meanings:
+
+                       "idle"                  No active USSD session.
+                       "active"                A session is active between the
+                                               network and the ME, the ME is
+                                               waiting for a reply from the
+                                               network.
+                       "user-response"         The network is waiting for the
+                                               user's response, client must
+                                               call Respond().
+
+
+Initiate method output arguments
+================================
+
+The first return argument and the corresponding second return argument are:
+
+       "USSD"                          string ussd_response
+       "CallBarring"                   (string ss_op, string cb_service,
+                                       dict cb_dict)
+       "CallForwarding"                (string ss_op, string cf_service,
+                                       dict cf_dict)
+       "CallWaiting"                   (string ss_op, dict cw_dict)
+       "CallingLinePresentation"       (string ss_op, string status)
+       "ConnectedLinePresentation"     (string ss_op, string status)
+       "CallingLineRestriction"        (string ss_op, string clir_status)
+       "ConnectedLineRestriction"      (string ss_op, string status)
+
+ss_op contains the supplementary service operation:
+
+       "activation"
+       "registration"
+       "interrogation"
+       "deactivation"
+       "erasure"
+
+cb_service contains the call barring service for which the operation was
+requested:
+
+       "AllOutgoing"
+       "InternationalOutgoing"
+       "InternationalOutgoingExceptHome"
+       "AllIncoming"
+       "IncomingWhenRoaming"
+       "AllBarringServices"
+       "AllOutgoingServices"
+       "AllIncomingServices"
+
+cf_service contains the call forwarding service for which the operation was
+requested:
+
+       "Unconditional"
+       "Busy"
+       "NoReply"
+       "NotReachable"
+       "All"
+       "AllConditional"
+
+cb_dict contains basic service/call barring service combinations that were
+affected by SS operation and their current status ("enabled" or "disabled").
+The basic services are:
+
+       "Voice"
+       "Data"
+       "Fax"
+       "Sms"
+       "DataSync"
+       "DataAsync"
+       "DataPad"
+       "DataPacket"
+
+To those the name of call barring service is appended, so the property and
+value is for example:
+
+       "FaxIncomingWhenRoaming" : "disabled"
+
+cf_dict contains call forwarding properties affected by the operation.
+Propery names are formed from basic service name and call forwarding
+service name, for example:
+
+       "VoiceNoReply" : "+12345678"
+
+The property value is the phone number to which the call is forwarded.
+
+For "NoReply" service, there is also a timeout property, holding the timeout
+in seconds, for example:
+
+       "VoiceNoReplyTimeout" : 20
+
+cw_dict contains basic services with "CallWaiting" suffix that were affected
+by call waiting operation and their current status ("enabled" or "disabled"),
+for example:
+
+       "VoiceCallWaiting" : "enabled"
+
+status can be "disabled" or "enabled".
+clir_status can be "disabled", "permanent", "on" or "off".
+
+More information about supplementary services is provided in
+call-barring-api.txt, call-forwarding-api.txt and call-settings-api.txt
diff --git a/doc/text-telephony-api.txt b/doc/text-telephony-api.txt
new file mode 100644 (file)
index 0000000..adaba84
--- /dev/null
@@ -0,0 +1,39 @@
+Text Telephony hierarchy
+========================
+
+Service                org.ofono
+Interface      org.ofono.TextTelephony
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns all Text Telephony properties. See the
+                       properties section for available properties.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.Failed
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as read-write are
+                       changeable. On success a PropertyChanged signal
+                       will be emitted.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InProgress
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Enabled [readwrite]
+
+                       This property will enable or disable the text
+                       telephony feature in the modem.
+
+                       Text telephony (TTY), also known as Cellular Text Modem
+                       (CTM), is a feature present in some modems that allow
+                       them to be used by hearing impaired people.
diff --git a/doc/voicecall-api.txt b/doc/voicecall-api.txt
new file mode 100644 (file)
index 0000000..fb8f099
--- /dev/null
@@ -0,0 +1,174 @@
+VoiceCall hierarchy
+===================
+
+Service                org.ofono
+Interface      org.ofono.VoiceCall
+Object path    [variable prefix]/{modem0,modem1,...}/{voicecall01,voicecall02,...}
+
+Methods                dict GetProperties()
+
+                       Returns all properties for this object. See the
+                       properties section for available properties.
+
+               void Deflect(string number)
+
+                       Deflects the incoming or waiting call to number given
+                       in the argument.  This method is only valid if the
+                       call is in "incoming" or "waiting" state and the
+                       Call Deflection supplementary service is subscribed to.
+
+                       This functionality is generally implemented by using
+                       the +CHLD=4 * NUMBER command.
+
+                       This method should not be confused with the Transfer()
+                       method.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+               void Hangup()
+
+                       Hangs up the voice call.
+
+                       For an incoming call, the call is hung up using ATH or
+                       equivalent.  For a waiting call, the remote party is
+                       notified by using the User Determined User Busy (UDUB)
+                       condition.  This is generally implemented using CHLD=0.
+
+                       Please note that the GSM specification does not allow
+                       the release of a held call when a waiting call exists,
+                       or the release of a particular party in a held
+                       multiparty call.
+
+                       Note that releasing a held call or a particular party
+                       of a held multiparty call might not be possible on some
+                       implementations.
+
+                       NOTE: Releasing active calls does not produce
+                       side-effects.  That is the state of held or waiting
+                       calls is not affected.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.Failed
+                                        [service].Error.NotImplemented
+
+               void Answer()
+
+                       Answers the incoming call.  Only valid if the state
+                       of the call is "incoming."
+
+                       This functionality is generally implemented by ATA
+                       AT command.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.Failed
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+Signals                PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+               DisconnectReason(string reason)
+
+                       This signal is emitted when the modem manager can
+                       provide extra information about why this call was
+                       released. The possible reason values are:
+                               "local" - The call was release due to local
+                                               user action
+                               "remote" - Remote party released the call
+                               "network" - Network released the call, most
+                                               likely due to low signal or
+                                               other network failure
+
+                       Not all implementations are able to provide this
+                       information, so applications should treat the emission
+                       of this signal as optional. This signal will be
+                       emitted before the PropertyChanged signal.
+
+Properties     string LineIdentification [readonly]
+
+                       Contains the Line Identification information returned
+                       by the network, if present. For incoming calls this is
+                       effectively the CLIP. For outgoing calls this attribute
+                       will hold the dialed number, or the COLP if received by
+                       the underlying implementation.
+
+                       Please note that COLP may be different from the
+                       dialed number. A special "withheld" value means the
+                       remote party refused to provide caller ID and the
+                       "override category" option was not provisioned for
+                       the current subscriber.
+
+               string IncomingLine [readonly, optional]
+
+                       Contains the Called Line Identification information
+                       returned by the network. This is only available for
+                       incoming calls and indicates the local subscriber
+                       number which was dialed by the remote party. This is
+                       useful for subscribers which have a multiple line
+                       service with their network provider and would like
+                       to know what line the call is coming in on.
+
+               string Name [readonly]
+
+                       Contains the Name Identification information returned
+                       by the network, if present.
+
+               boolean Multiparty [readonly]
+
+                       Contains the indication if the voice call is part
+                       of a multiparty call or not.
+
+                       Notifications if a call becomes part or leaves a
+                       multipart call are sent.
+
+               string State [readonly]
+
+                       Contains the state of the current call.  The state
+                       can be one of:
+                               - "active" - The call is active
+                               - "held" - The call is on hold
+                               - "dialing" - The call is being dialed
+                               - "alerting" - The remote party is being alerted
+                               - "incoming" - Incoming call in progress
+                               - "waiting" - Call is waiting
+                               - "disconnected" - No further use of this object
+                                       is allowed, it will be destroyed shortly
+
+               string StartTime [readonly, optional]
+
+                       Contains the starting time of the call.  The time is
+                       stamped when the call enters the "active" state.
+                       Client applications can use this to infer somewhat
+                       reliable call duration information.
+
+               string Information [readonly, optional]
+
+                       Contains information related to the call for the
+                       user.  Currently this property is set for calls
+                       initiated by SIM Toolkit applications.
+
+               byte Icon [readonly, optional]
+
+                       Icon identifier to be used instead of or together
+                       with the text information.
+
+               boolean Emergency [readonly]
+
+                       Contains the indication if the voice call is an
+                       emergency call or not.
+
+               boolean RemoteHeld [experimental]
+
+                       Contains the indication whether the voice call is put
+                       on hold by the remote party or not.
+
+               boolean RemoteMultiparty [experimental]
+
+                       Contains the indication whether the voice call is
+                       joined in a multiparty call by the remote party or not.
diff --git a/doc/voicecallmanager-api.txt b/doc/voicecallmanager-api.txt
new file mode 100644 (file)
index 0000000..56dba8b
--- /dev/null
@@ -0,0 +1,228 @@
+VoiceCallManager hierarchy
+==========================
+
+Service                org.ofono
+Interface      org.ofono.VoiceCallManager
+Object path    [variable prefix]/{modem0,modem1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the VoiceCallManager Interface.
+                       See the properties section for available properties.
+
+               array{object,dict} GetCalls()
+
+                       Get an array of call object paths and properties
+                       that represents the currently present calls.
+
+                       This method call should only be used once when an
+                       application starts up.  Further call additions and
+                       removal shall be monitored via CallAdded and
+                       CallRemoved signals.
+
+               object Dial(string number, string hide_callerid)
+
+                       Initiates a new outgoing call. Returns the object path
+                       to the newly created call. The hide_callerid variable
+                       holds the CLIR override for this call.
+                       The defines values are:
+                               "" or "default" - Default (Network) CLIR mode
+                                                       is used
+                               "enabled" - Hides callerid, CLIR Invocation
+                                               is used
+                               "disabled" - Shows callerid, CLIR Suppression
+                                               is used
+
+                       This is usually implemented using the ATD AT command.
+
+                       NOTE: If an active call (single or multiparty) exists,
+                       then it is automatically put on hold if the dial
+                       procedure is successful.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void Transfer()
+
+                       Joins the currently Active (or Outgoing, depending
+                       on network support) and Held calls together and
+                       disconnects both calls. In effect transferring
+                       one party to the other. This procedure requires
+                       an Active and Held call and the Explicit Call Transfer
+                       (ECT) supplementary service to be active.
+
+                       This functionality is generally implemented by using
+                       the +CHLD=4 AT command.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void SwapCalls()
+
+                       Swaps Active and Held calls.  The effect of this
+                       is that all calls (0 or more including calls in a
+                       multi-party conversation) that were Active are now Held,
+                       and all calls (0 or more) that were Held are now Active.
+
+                       GSM specification does not allow calls to be swapped
+                       in the case where Held, Active and Waiting calls exist.
+                       Some modems implement this anyway, thus it is
+                       manufacturer specific whether this method will succeed
+                       in the case of Held, Active and Waiting calls.
+
+                       This functionality is generally implemented by using
+                       the +CHLD=2 AT command.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void ReleaseAndAnswer()
+
+                       Releases currently active call (0 or more) and
+                       answers the currently waiting call. Please note that
+                       if the current call is a multiparty call, then all
+                       parties in the multi-party call will be released.
+
+                       Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void HoldAndAnswer()
+
+                       Puts the current call (including multi-party calls) on
+                       hold and answers the currently waiting call. Calling
+                       this function when a user already has a both Active and
+                       Held calls is invalid, since in GSM a user can have
+                       only a single Held call at a time.
+
+                        Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void HangupAll()
+
+                       Releases all calls except waiting calls. This includes
+                       multiparty calls.
+
+                        Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               array{object} PrivateChat(object call)
+
+                       Places the multi-party call on hold and makes desired
+                       call active. This is used to accomplish private chat
+                       functionality.  Note that if there are only two calls
+                       (three parties) in the multi-party call the result will
+                       be two regular calls, one held and one active. The
+                       Multiparty call will need to be setup again by using the
+                       CreateMultiparty method.  Returns the new list of calls
+                       participating in the multiparty call.
+
+                       This is usually implemented using the +CHLD=2X command.
+
+                        Possible Errors: [service].Error.InProgress
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.NotFound
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               array{object} CreateMultiparty()
+
+                       Joins active and held calls together into a multi-party
+                       call. If one of the calls is already a multi-party
+                       call, then the other call is added to the multiparty
+                       conversation. Returns the new list of calls
+                       participating in the multiparty call.
+
+                       There can only be one subscriber controlled multi-party
+                       call according to the GSM specification.
+
+                       This is usually implemented using the +CHLD=3 AT
+                       command.
+
+                        Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void HangupMultiparty()
+
+                       Hangs up the multi-party call.  All participating
+                       calls are released.
+
+                        Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.Failed
+
+               void SendTones(string tones)
+
+                       Sends the DTMF tones to the network.  The tones have
+                       a fixed duration.  Tones can be one of: '0' - '9',
+                       '*', '#', 'A', 'B', 'C', 'D'.  The last four are
+                       typically not used in normal circumstances.
+
+                        Possible Errors: [service].Error.InProgress
+                                        [service].Error.NotImplemented
+                                        [service].Error.InvalidArguments
+                                        [service].Error.InvalidFormat
+                                        [service].Error.Failed
+
+Signals                CallAdded(object path, dict properties)
+
+                       Signal that is sent when a new call is added.  It
+                       contains the object path of the new voice call and
+                       also its properties.
+
+                       Applications get the whole properties via this
+                       signal and don't need to call GetProperties on
+                       the voice call object.
+
+               CallRemoved(object path)
+
+                       Signal that is sent when a voice call has been
+                       released.  The object path is no longer accessible
+                       after this signal and only emitted for reference.
+
+               PropertyChanged(string property, variant value)
+
+                       Signal is emitted whenever a property has changed.
+                       The new value is passed as the signal argument.
+
+               BarringActive(string type) [experimental]
+
+                       Signal emitted when an outgoing voice call is made and
+                       the call has been barred by the network due to the
+                       remote party's "Call Barring" Supplementary Services
+                       settings for incoming calls. In this case the type
+                       parameter in the signal set to "remote".
+                       The signal is also emitted when an outgoing voice call
+                       is made and the call has been barred by the network due
+                       to the local "Call Barring" Supplementary Services
+                       settings for outgoing calls. In this case the type
+                       parameter in the signal is set to "local".
+
+               Forwarded(string type) [experimental]
+
+                       Signal emitted when an outgoing voice call is made and
+                       the call has been redirected to another number due to
+                       the remote party's "Call Forwarding" Supplementary
+                       Services settings. In this case the type parameter in
+                       the signal is set to "outgoing".
+                       The signal is also emitted when the incoming voice call
+                       is a redirected call due to a call forwarding operation.
+                       In this case the type parameter in the signal is set to
+                       "incoming".
+
+Properties     array{string} EmergencyNumbers [readonly]
+
+                       Contains the list of emergency numbers recognized
+                       by oFono.  This list is based on the default set
+                       of numbers provided by the specification and any
+                       extra numbers provisioned by the carrier on the
+                       SIM.
diff --git a/drivers/atmodem/atmodem.c b/drivers/atmodem/atmodem.c
new file mode 100644 (file)
index 0000000..3a55ac2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "atmodem.h"
+
+static int atmodem_init(void)
+{
+       at_voicecall_init();
+       at_devinfo_init();
+       at_call_barring_init();
+       at_call_forwarding_init();
+       at_call_meter_init();
+       at_call_settings_init();
+       at_phonebook_init();
+       at_ussd_init();
+       at_sms_init();
+       at_sim_init();
+       at_stk_init();
+       at_netreg_init();
+       at_cbs_init();
+       at_call_volume_init();
+       at_gprs_init();
+       at_gprs_context_init();
+       at_sim_auth_init();
+       at_gnss_init();
+
+       return 0;
+}
+
+static void atmodem_exit(void)
+{
+       at_sim_auth_exit();
+       at_stk_exit();
+       at_sim_exit();
+       at_sms_exit();
+       at_ussd_exit();
+       at_phonebook_exit();
+       at_call_settings_exit();
+       at_call_meter_exit();
+       at_call_forwarding_exit();
+       at_call_barring_exit();
+       at_netreg_exit();
+       at_devinfo_exit();
+       at_voicecall_exit();
+       at_cbs_exit();
+       at_call_volume_exit();
+       at_gprs_exit();
+       at_gprs_context_exit();
+       at_gnss_exit();
+}
+
+OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, atmodem_init, atmodem_exit)
diff --git a/drivers/atmodem/atmodem.h b/drivers/atmodem/atmodem.h
new file mode 100644 (file)
index 0000000..6be1fe5
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "atutil.h"
+
+extern void at_netreg_init(void);
+extern void at_netreg_exit(void);
+
+extern void at_call_forwarding_init(void);
+extern void at_call_forwarding_exit(void);
+
+extern void at_call_settings_init(void);
+extern void at_call_settings_exit(void);
+
+extern void at_ussd_init(void);
+extern void at_ussd_exit(void);
+
+extern void at_voicecall_init(void);
+extern void at_voicecall_exit(void);
+
+extern void at_call_meter_init(void);
+extern void at_call_meter_exit(void);
+
+extern void at_call_barring_init(void);
+extern void at_call_barring_exit(void);
+
+extern void at_sim_init(void);
+extern void at_sim_exit(void);
+
+extern void at_stk_init(void);
+extern void at_stk_exit(void);
+
+extern void at_sms_init(void);
+extern void at_sms_exit(void);
+
+extern void at_phonebook_init(void);
+extern void at_phonebook_exit(void);
+
+extern void at_devinfo_init(void);
+extern void at_devinfo_exit(void);
+
+extern void at_cbs_init(void);
+extern void at_cbs_exit(void);
+
+extern void at_call_volume_init(void);
+extern void at_call_volume_exit(void);
+
+extern void at_gprs_init(void);
+extern void at_gprs_exit(void);
+
+extern void at_gprs_context_init(void);
+extern void at_gprs_context_exit(void);
+
+extern void at_sim_auth_init(void);
+extern void at_sim_auth_exit(void);
+
+extern void at_gnss_init(void);
+extern void at_gnss_exit(void);
diff --git a/drivers/atmodem/atutil.c b/drivers/atmodem/atutil.c
new file mode 100644 (file)
index 0000000..b82ed20
--- /dev/null
@@ -0,0 +1,597 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/log.h>
+#include <ofono/types.h>
+
+#include "atutil.h"
+#include "vendor.h"
+
+static const char *cpin_prefix[] = { "+CPIN:", NULL };
+
+struct at_util_sim_state_query {
+       GAtChat *chat;
+       guint cpin_poll_source;
+       guint cpin_poll_count;
+       guint interval;
+       guint num_times;
+       at_util_sim_inserted_cb_t cb;
+       void *userdata;
+};
+
+static gboolean cpin_check(gpointer userdata);
+
+void decode_at_error(struct ofono_error *error, const char *final)
+{
+       if (!strcmp(final, "OK")) {
+               error->type = OFONO_ERROR_TYPE_NO_ERROR;
+               error->error = 0;
+       } else if (g_str_has_prefix(final, "+CMS ERROR:")) {
+               error->type = OFONO_ERROR_TYPE_CMS;
+               error->error = strtol(&final[11], NULL, 0);
+       } else if (g_str_has_prefix(final, "+CME ERROR:")) {
+               error->type = OFONO_ERROR_TYPE_CME;
+               error->error = strtol(&final[11], NULL, 0);
+       } else {
+               error->type = OFONO_ERROR_TYPE_FAILURE;
+               error->error = 0;
+       }
+}
+
+gint at_util_call_compare_by_status(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_call *call = a;
+       int status = GPOINTER_TO_INT(b);
+
+       if (status != call->status)
+               return 1;
+
+       return 0;
+}
+
+gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_call *call = a;
+       const struct ofono_phone_number *pb = b;
+
+       return memcmp(&call->phone_number, pb,
+                               sizeof(struct ofono_phone_number));
+}
+
+gint at_util_call_compare_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_call *call = a;
+       unsigned int id = GPOINTER_TO_UINT(b);
+
+       if (id < call->id)
+               return -1;
+
+       if (id > call->id)
+               return 1;
+
+       return 0;
+}
+
+gint at_util_call_compare(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_call *ca = a;
+       const struct ofono_call *cb = b;
+
+       if (ca->id < cb->id)
+               return -1;
+
+       if (ca->id > cb->id)
+               return 1;
+
+       return 0;
+}
+
+GSList *at_util_parse_clcc(GAtResult *result)
+{
+       GAtResultIter iter;
+       GSList *l = NULL;
+       int id, dir, status, type;
+       ofono_bool_t mpty;
+       struct ofono_call *call;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+CLCC:")) {
+               const char *str = "";
+               int number_type = 129;
+
+               if (!g_at_result_iter_next_number(&iter, &id))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &dir))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &status))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &type))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &mpty))
+                       continue;
+
+               if (g_at_result_iter_next_string(&iter, &str))
+                       g_at_result_iter_next_number(&iter, &number_type);
+
+               call = g_try_new(struct ofono_call, 1);
+               if (call == NULL)
+                       break;
+
+               ofono_call_init(call);
+
+               call->id = id;
+               call->direction = dir;
+               call->status = status;
+               call->type = type;
+               strncpy(call->phone_number.number, str,
+                               OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = number_type;
+
+               if (strlen(call->phone_number.number) > 0)
+                       call->clip_validity = 0;
+               else
+                       call->clip_validity = 2;
+
+               l = g_slist_insert_sorted(l, call, at_util_call_compare);
+       }
+
+       return l;
+}
+
+gboolean at_util_parse_reg_unsolicited(GAtResult *result, const char *prefix,
+                                       int *status,
+                                       int *lac, int *ci, int *tech,
+                                       unsigned int vendor)
+{
+       GAtResultIter iter;
+       int s;
+       int l = -1, c = -1, t = -1;
+       const char *str;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, prefix) == FALSE)
+               return FALSE;
+
+       if (g_at_result_iter_next_number(&iter, &s) == FALSE)
+               return FALSE;
+
+       /* Some firmware will report bogus lac/ci when unregistered */
+       if (s != 1 && s != 5)
+               goto out;
+
+       switch (vendor) {
+       case OFONO_VENDOR_GOBI:
+       case OFONO_VENDOR_ZTE:
+       case OFONO_VENDOR_HUAWEI:
+       case OFONO_VENDOR_NOVATEL:
+       case OFONO_VENDOR_SPEEDUP:
+               if (g_at_result_iter_next_unquoted_string(&iter, &str) == TRUE)
+                       l = strtol(str, NULL, 16);
+               else
+                       goto out;
+
+               if (g_at_result_iter_next_unquoted_string(&iter, &str) == TRUE)
+                       c = strtol(str, NULL, 16);
+               else
+                       goto out;
+
+               break;
+       default:
+               if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+                       l = strtol(str, NULL, 16);
+               else
+                       goto out;
+
+               if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+                       c = strtol(str, NULL, 16);
+               else
+                       goto out;
+       }
+
+       g_at_result_iter_next_number(&iter, &t);
+
+out:
+       if (status)
+               *status = s;
+
+       if (lac)
+               *lac = l;
+
+       if (ci)
+               *ci = c;
+
+       if (tech)
+               *tech = t;
+
+       return TRUE;
+}
+
+gboolean at_util_parse_reg(GAtResult *result, const char *prefix,
+                               int *mode, int *status,
+                               int *lac, int *ci, int *tech,
+                               unsigned int vendor)
+{
+       GAtResultIter iter;
+       int m, s;
+       int l = -1, c = -1, t = -1;
+       const char *str;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, prefix)) {
+               gboolean r;
+
+               g_at_result_iter_next_number(&iter, &m);
+
+               /* Sometimes we get an unsolicited CREG/CGREG here, skip it */
+               switch (vendor) {
+               case OFONO_VENDOR_ZTE:
+               case OFONO_VENDOR_HUAWEI:
+               case OFONO_VENDOR_NOVATEL:
+               case OFONO_VENDOR_SPEEDUP:
+                       r = g_at_result_iter_next_unquoted_string(&iter, &str);
+
+                       if (r == FALSE || strlen(str) != 1)
+                               continue;
+
+                       s = strtol(str, NULL, 10);
+
+                       break;
+               default:
+                       if (g_at_result_iter_next_number(&iter, &s) == FALSE)
+                               continue;
+
+                       break;
+               }
+
+               /* Some firmware will report bogus lac/ci when unregistered */
+               if (s != 1 && s != 5)
+                       goto out;
+
+               switch (vendor) {
+               case OFONO_VENDOR_GOBI:
+               case OFONO_VENDOR_ZTE:
+               case OFONO_VENDOR_HUAWEI:
+               case OFONO_VENDOR_NOVATEL:
+               case OFONO_VENDOR_SPEEDUP:
+                       r = g_at_result_iter_next_unquoted_string(&iter, &str);
+
+                       if (r == TRUE)
+                               l = strtol(str, NULL, 16);
+                       else
+                               goto out;
+
+                       r = g_at_result_iter_next_unquoted_string(&iter, &str);
+
+                       if (r == TRUE)
+                               c = strtol(str, NULL, 16);
+                       else
+                               goto out;
+
+                       break;
+               default:
+                       if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+                               l = strtol(str, NULL, 16);
+                       else
+                               goto out;
+
+                       if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+                               c = strtol(str, NULL, 16);
+                       else
+                               goto out;
+               }
+
+               g_at_result_iter_next_number(&iter, &t);
+
+out:
+               if (mode)
+                       *mode = m;
+
+               if (status)
+                       *status = s;
+
+               if (lac)
+                       *lac = l;
+
+               if (ci)
+                       *ci = c;
+
+               if (tech)
+                       *tech = t;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+gboolean at_util_parse_sms_index_delivery(GAtResult *result, const char *prefix,
+                                               enum at_util_sms_store *out_st,
+                                               int *out_index)
+{
+       GAtResultIter iter;
+       const char *strstore;
+       enum at_util_sms_store st;
+       int index;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, prefix))
+               return FALSE;
+
+       if (!g_at_result_iter_next_string(&iter, &strstore))
+               return FALSE;
+
+       if (g_str_equal(strstore, "ME"))
+               st = AT_UTIL_SMS_STORE_ME;
+       else if (g_str_equal(strstore, "SM"))
+               st = AT_UTIL_SMS_STORE_SM;
+       else if (g_str_equal(strstore, "SR"))
+               st = AT_UTIL_SMS_STORE_SR;
+       else if (g_str_equal(strstore, "BM"))
+               st = AT_UTIL_SMS_STORE_BM;
+       else
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, &index))
+               return FALSE;
+
+       if (out_index)
+               *out_index = index;
+
+       if (out_st)
+               *out_st = st;
+
+       return TRUE;
+}
+
+static gboolean at_util_charset_string_to_charset(const char *str,
+                                       enum at_util_charset *charset)
+{
+       if (!g_strcmp0(str, "GSM"))
+               *charset = AT_UTIL_CHARSET_GSM;
+       else if (!g_strcmp0(str, "HEX"))
+               *charset = AT_UTIL_CHARSET_HEX;
+       else if (!g_strcmp0(str, "IRA"))
+               *charset = AT_UTIL_CHARSET_IRA;
+       else if (!g_strcmp0(str, "PCCP437"))
+               *charset = AT_UTIL_CHARSET_PCCP437;
+       else if (!g_strcmp0(str, "PCDN"))
+               *charset = AT_UTIL_CHARSET_PCDN;
+       else if (!g_strcmp0(str, "UCS2"))
+               *charset = AT_UTIL_CHARSET_UCS2;
+       else if (!g_strcmp0(str, "UTF-8"))
+               *charset = AT_UTIL_CHARSET_UTF8;
+       else if (!g_strcmp0(str, "8859-1"))
+               *charset = AT_UTIL_CHARSET_8859_1;
+       else if (!g_strcmp0(str, "8859-2"))
+               *charset = AT_UTIL_CHARSET_8859_2;
+       else if (!g_strcmp0(str, "8859-3"))
+               *charset = AT_UTIL_CHARSET_8859_3;
+       else if (!g_strcmp0(str, "8859-4"))
+               *charset = AT_UTIL_CHARSET_8859_4;
+       else if (!g_strcmp0(str, "8859-5"))
+               *charset = AT_UTIL_CHARSET_8859_5;
+       else if (!g_strcmp0(str, "8859-6"))
+               *charset = AT_UTIL_CHARSET_8859_6;
+       else if (!g_strcmp0(str, "8859-C"))
+               *charset = AT_UTIL_CHARSET_8859_C;
+       else if (!g_strcmp0(str, "8859-A"))
+               *charset = AT_UTIL_CHARSET_8859_A;
+       else if (!g_strcmp0(str, "8859-G"))
+               *charset = AT_UTIL_CHARSET_8859_G;
+       else if (!g_strcmp0(str, "8859-H"))
+               *charset = AT_UTIL_CHARSET_8859_H;
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+gboolean at_util_parse_cscs_supported(GAtResult *result, int *supported)
+{
+       GAtResultIter iter;
+       const char *str;
+       enum at_util_charset charset;
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSCS:"))
+               return FALSE;
+
+       /* Some modems don't report CSCS in a proper list */
+       g_at_result_iter_open_list(&iter);
+
+       while (g_at_result_iter_next_string(&iter, &str)) {
+               if (at_util_charset_string_to_charset(str, &charset))
+                       *supported |= charset;
+       }
+
+       g_at_result_iter_close_list(&iter);
+
+       return TRUE;
+}
+
+gboolean at_util_parse_cscs_query(GAtResult *result,
+                               enum at_util_charset *charset)
+{
+       GAtResultIter iter;
+       const char *str;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSCS:"))
+               return FALSE;
+
+       if (g_at_result_iter_next_string(&iter, &str))
+               return at_util_charset_string_to_charset(str, charset);
+
+       return FALSE;
+}
+
+static const char *at_util_fixup_return(const char *line, const char *prefix)
+{
+       if (g_str_has_prefix(line, prefix) == FALSE)
+               return line;
+
+       line += strlen(prefix);
+
+       while (line[0] == ' ')
+               line++;
+
+       return line;
+}
+
+gboolean at_util_parse_attr(GAtResult *result, const char *prefix,
+                               const char **out_attr)
+{
+       int numlines = g_at_result_num_response_lines(result);
+       GAtResultIter iter;
+       const char *line;
+       int i;
+
+       if (numlines == 0)
+               return FALSE;
+
+       g_at_result_iter_init(&iter, result);
+
+       /*
+        * We have to be careful here, sometimes a stray unsolicited
+        * notification will appear as part of the response and we
+        * cannot rely on having a prefix to recognize the actual
+        * response line.  So use the last line only as the response
+        */
+       for (i = 0; i < numlines; i++)
+               g_at_result_iter_next(&iter, NULL);
+
+       line = g_at_result_iter_raw_line(&iter);
+
+       if (out_attr)
+               *out_attr = at_util_fixup_return(line, prefix);
+
+       return TRUE;
+}
+
+static void cpin_check_cb(gboolean ok, GAtResult *result, gpointer userdata)
+{
+       struct at_util_sim_state_query *req = userdata;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (error.type == OFONO_ERROR_TYPE_NO_ERROR)
+               goto done;
+
+       /*
+        * If we got a generic error the AT port might not be ready,
+        * try again
+        */
+       if (error.type == OFONO_ERROR_TYPE_FAILURE)
+               goto tryagain;
+
+       /* If we got any other error besides CME, fail */
+       if (error.type != OFONO_ERROR_TYPE_CME)
+               goto done;
+
+       switch (error.error) {
+       case 10:
+       case 13:
+               goto done;
+
+       case 14:
+               goto tryagain;
+
+       default:
+               /* Assume SIM is present */
+               ok = TRUE;
+               goto done;
+       }
+
+tryagain:
+       if (req->cpin_poll_count++ < req->num_times) {
+               req->cpin_poll_source = g_timeout_add_seconds(req->interval,
+                                                               cpin_check,
+                                                               req);
+               return;
+       }
+
+done:
+       if (req->cb)
+               req->cb(ok, req->userdata);
+}
+
+static gboolean cpin_check(gpointer userdata)
+{
+       struct at_util_sim_state_query *req = userdata;
+
+       req->cpin_poll_source = 0;
+
+       g_at_chat_send(req->chat, "AT+CPIN?", cpin_prefix,
+                       cpin_check_cb, req, NULL);
+
+       return FALSE;
+}
+
+struct at_util_sim_state_query *at_util_sim_state_query_new(GAtChat *chat,
+                                               guint interval, guint num_times,
+                                               at_util_sim_inserted_cb_t cb,
+                                               void *userdata)
+{
+       struct at_util_sim_state_query *req;
+
+       req = g_new0(struct at_util_sim_state_query, 1);
+
+       req->chat = chat;
+       req->interval = interval;
+       req->num_times = num_times;
+       req->cb = cb;
+       req->userdata = userdata;
+
+       cpin_check(req);
+
+       return req;
+}
+
+void at_util_sim_state_query_free(struct at_util_sim_state_query *req)
+{
+       if (req == NULL)
+               return;
+
+       if (req->cpin_poll_source > 0)
+               g_source_remove(req->cpin_poll_source);
+
+       g_free(req);
+}
diff --git a/drivers/atmodem/atutil.h b/drivers/atmodem/atutil.h
new file mode 100644 (file)
index 0000000..5046547
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum at_util_sms_store {
+       AT_UTIL_SMS_STORE_SM =  0,
+       AT_UTIL_SMS_STORE_ME =  1,
+       AT_UTIL_SMS_STORE_MT =  2,
+       AT_UTIL_SMS_STORE_SR =  3,
+       AT_UTIL_SMS_STORE_BM =  4,
+};
+
+/* 3GPP TS 27.007 Release 8 Section 5.5 */
+enum at_util_charset {
+       AT_UTIL_CHARSET_GSM =           0x1,
+       AT_UTIL_CHARSET_HEX =           0x2,
+       AT_UTIL_CHARSET_IRA =           0x4,
+       AT_UTIL_CHARSET_PCCP437 =       0x8,
+       AT_UTIL_CHARSET_PCDN =          0x10,
+       AT_UTIL_CHARSET_UCS2 =          0x20,
+       AT_UTIL_CHARSET_UTF8 =          0x40,
+       AT_UTIL_CHARSET_8859_1 =        0x80,
+       AT_UTIL_CHARSET_8859_2 =        0x100,
+       AT_UTIL_CHARSET_8859_3 =        0x200,
+       AT_UTIL_CHARSET_8859_4 =        0x400,
+       AT_UTIL_CHARSET_8859_5 =        0x800,
+       AT_UTIL_CHARSET_8859_6 =        0x1000,
+       AT_UTIL_CHARSET_8859_C =        0x2000,
+       AT_UTIL_CHARSET_8859_A =        0x4000,
+       AT_UTIL_CHARSET_8859_G =        0x8000,
+       AT_UTIL_CHARSET_8859_H =        0x10000,
+};
+
+typedef void (*at_util_sim_inserted_cb_t)(gboolean present, void *userdata);
+
+void decode_at_error(struct ofono_error *error, const char *final);
+gint at_util_call_compare_by_status(gconstpointer a, gconstpointer b);
+gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b);
+gint at_util_call_compare_by_id(gconstpointer a, gconstpointer b);
+gint at_util_call_compare(gconstpointer a, gconstpointer b);
+GSList *at_util_parse_clcc(GAtResult *result);
+gboolean at_util_parse_reg(GAtResult *result, const char *prefix,
+                               int *mode, int *status,
+                               int *lac, int *ci, int *tech,
+                               unsigned int vendor);
+gboolean at_util_parse_reg_unsolicited(GAtResult *result, const char *prefix,
+                                       int *status, int *lac,
+                                       int *ci, int *tech,
+                                       unsigned int vendor);
+
+gboolean at_util_parse_sms_index_delivery(GAtResult *result, const char *prefix,
+                                               enum at_util_sms_store *store,
+                                               int *index);
+
+gboolean at_util_parse_cscs_supported(GAtResult *result, int *supported);
+gboolean at_util_parse_cscs_query(GAtResult *result,
+                               enum at_util_charset *charset);
+
+gboolean at_util_parse_attr(GAtResult *result, const char *prefix,
+                               const char **out_attr);
+
+struct at_util_sim_state_query *at_util_sim_state_query_new(GAtChat *chat,
+                                               guint interval, guint num_times,
+                                               at_util_sim_inserted_cb_t cb,
+                                               void *userdata);
+void at_util_sim_state_query_free(struct at_util_sim_state_query *req);
+
+struct cb_data {
+       void *cb;
+       void *data;
+       void *user;
+};
+
+static inline struct cb_data *cb_data_new(void *cb, void *data)
+{
+       struct cb_data *ret;
+
+       ret = g_new0(struct cb_data, 1);
+       ret->cb = cb;
+       ret->data = data;
+
+       return ret;
+}
+
+static inline int at_util_convert_signal_strength(int strength)
+{
+       int result;
+
+       if (strength == 99)
+               result = -1;
+       else
+               result = (strength * 100) / 31;
+
+       return result;
+}
+
+#define DECLARE_FAILURE(e)                     \
+       struct ofono_error e;                   \
+       e.type = OFONO_ERROR_TYPE_FAILURE;      \
+       e.error = 0                             \
+
+#define CALLBACK_WITH_FAILURE(cb, args...)             \
+       do {                                            \
+               struct ofono_error cb_e;                \
+               cb_e.type = OFONO_ERROR_TYPE_FAILURE;   \
+               cb_e.error = 0;                         \
+                                                       \
+               cb(&cb_e, ##args);                      \
+       } while (0)                                     \
+
+#define CALLBACK_WITH_SUCCESS(f, args...)              \
+       do {                                            \
+               struct ofono_error e;                   \
+               e.type = OFONO_ERROR_TYPE_NO_ERROR;     \
+               e.error = 0;                            \
+               f(&e, ##args);                          \
+       } while (0)
diff --git a/drivers/atmodem/call-barring.c b/drivers/atmodem/call-barring.c
new file mode 100644 (file)
index 0000000..2efd4e9
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *clck_prefix[] = { "+CLCK:", NULL };
+static const char *none_prefix[] = { NULL };
+
+static void clck_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_barring_query_cb_t callback = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int status_mask, status, class, line;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       status_mask = 0;
+       line = 0;
+       g_at_result_iter_init(&iter, result);
+       while (g_at_result_iter_next(&iter, "+CLCK:")) {
+               line++;
+
+               if (!g_at_result_iter_next_number(&iter, &status))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &class)) {
+                       if (line > 1)
+                               continue;
+                       else
+                               class = 7;
+               }
+
+               if (status)
+                       status_mask |= class;
+               else
+                       status_mask &= ~class;
+       }
+
+       callback(&error, status_mask, cbd->data);
+}
+
+static void at_call_barring_query(struct ofono_call_barring *cb,
+                                       const char *lock, int cls,
+                                       ofono_call_barring_query_cb_t callback,
+                                       void *data)
+{
+       GAtChat *chat = ofono_call_barring_get_data(cb);
+       struct cb_data *cbd = cb_data_new(callback, data);
+       char buf[64];
+
+       if (strlen(lock) != 2)
+               goto error;
+
+       snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2", lock);
+
+       if (g_at_chat_send(chat, buf, clck_prefix,
+                               clck_query_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(callback, 0, data);
+}
+
+static void clck_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_barring_set_cb_t callback = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       callback(&error, cbd->data);
+}
+
+static void at_call_barring_set(struct ofono_call_barring *cb, const char *lock,
+                               int enable, const char *passwd, int cls,
+                               ofono_call_barring_set_cb_t callback,
+                               void *data)
+{
+       GAtChat *chat = ofono_call_barring_get_data(cb);
+       struct cb_data *cbd = cb_data_new(callback, data);
+       char buf[64];
+       int len;
+
+       if (strlen(lock) != 2 || (cls && passwd == NULL))
+               goto error;
+
+       len = snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",%i", lock, enable);
+       if (passwd) {
+               len += snprintf(buf + len, sizeof(buf) - len,
+                               ",\"%s\"", passwd);
+               /* Assume cls == 7 means use defaults */
+               if (cls != 7)
+                       snprintf(buf + len, sizeof(buf) - len, ",%i", cls);
+       }
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               clck_set_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(callback, data);
+}
+
+static void cpwd_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_barring_set_cb_t callback = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       callback(&error, cbd->data);
+}
+
+static void at_call_barring_set_passwd(struct ofono_call_barring *cb,
+                                       const char *lock,
+                                       const char *old_passwd,
+                                       const char *new_passwd,
+                                       ofono_call_barring_set_cb_t callback,
+                                       void *data)
+{
+       GAtChat *chat = ofono_call_barring_get_data(cb);
+       struct cb_data *cbd = cb_data_new(callback, data);
+       char buf[64];
+
+       if (strlen(lock) != 2)
+               goto error;
+
+       snprintf(buf, sizeof(buf), "AT+CPWD=\"%s\",\"%s\",\"%s\"",
+                       lock, old_passwd, new_passwd);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               cpwd_set_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(callback, data);
+}
+
+static gboolean at_call_barring_register(gpointer user)
+{
+       struct ofono_call_barring *cb = user;
+
+       ofono_call_barring_register(cb);
+
+       return FALSE;
+}
+
+static int at_call_barring_probe(struct ofono_call_barring *cb,
+                                       unsigned int vendor, void *user)
+{
+       GAtChat *chat = user;
+
+       ofono_call_barring_set_data(cb, g_at_chat_clone(chat));
+       g_idle_add(at_call_barring_register, cb);
+
+       return 0;
+}
+
+static void at_call_barring_remove(struct ofono_call_barring *cb)
+{
+       GAtChat *chat = ofono_call_barring_get_data(cb);
+
+       g_at_chat_unref(chat);
+       ofono_call_barring_set_data(cb, NULL);
+}
+
+static struct ofono_call_barring_driver driver = {
+       .name           = "atmodem",
+       .probe          = at_call_barring_probe,
+       .remove         = at_call_barring_remove,
+       .set            = at_call_barring_set,
+       .query          = at_call_barring_query,
+       .set_passwd     = at_call_barring_set_passwd,
+};
+
+void at_call_barring_init(void)
+{
+       ofono_call_barring_driver_register(&driver);
+}
+
+void at_call_barring_exit(void)
+{
+       ofono_call_barring_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/call-forwarding.c b/drivers/atmodem/call-forwarding.c
new file mode 100644 (file)
index 0000000..cbe4b24
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-forwarding.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *ccfc_prefix[] = { "+CCFC:", NULL };
+
+static void ccfc_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_forwarding_query_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int num = 0;
+       struct ofono_call_forwarding_condition *list = NULL;
+       int i;
+       int maxlen;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto out;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+CCFC:"))
+               num += 1;
+
+       /* Specification is really unclear about this
+        * generate status=0 for all classes just in case
+        */
+       if (num == 0) {
+               list = g_new0(struct ofono_call_forwarding_condition, 1);
+               num = 1;
+
+               list->status = 0;
+               list->cls = GPOINTER_TO_INT(cbd->user);
+
+               goto out;
+       }
+
+       list = g_new(struct ofono_call_forwarding_condition, num);
+
+       g_at_result_iter_init(&iter, result);
+
+       maxlen = OFONO_MAX_PHONE_NUMBER_LENGTH;
+
+       for (num = 0; g_at_result_iter_next(&iter, "+CCFC:"); num++) {
+               const char *str;
+
+               g_at_result_iter_next_number(&iter, &(list[num].status));
+               g_at_result_iter_next_number(&iter, &(list[num].cls));
+
+               list[num].phone_number.number[0] = '\0';
+               list[num].phone_number.type = 129;
+               list[num].time = 20;
+
+               if (!g_at_result_iter_next_string(&iter, &str))
+                       continue;
+
+               strncpy(list[num].phone_number.number, str, maxlen);
+               list[num].phone_number.number[maxlen] = '\0';
+
+               g_at_result_iter_next_number(&iter,
+                                               &(list[num].phone_number.type));
+
+               if (!g_at_result_iter_skip_next(&iter))
+                       continue;
+
+               if (!g_at_result_iter_skip_next(&iter))
+                       continue;
+
+               g_at_result_iter_next_number(&iter, &(list[num].time));
+       }
+
+       for (i = 0; i < num; i++)
+               DBG("ccfc_cb: %d, %d, %s(%d) - %d sec",
+                       list[i].status, list[i].cls,
+                       list[i].phone_number.number,
+                       list[i].phone_number.type, list[i].time);
+
+out:
+       cb(&error, num, list, cbd->data);
+       g_free(list);
+}
+
+static void at_ccfc_query(struct ofono_call_forwarding *cf, int type, int cls,
+                               ofono_call_forwarding_query_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_forwarding_get_data(cf);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       cbd->user = GINT_TO_POINTER(cls);
+
+       if (cls == 7)
+               snprintf(buf, sizeof(buf), "AT+CCFC=%d,2", type);
+       else
+               snprintf(buf, sizeof(buf), "AT+CCFC=%d,2,,,%d", type, cls);
+
+       if (g_at_chat_send(chat, buf, ccfc_prefix,
+                               ccfc_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
+}
+
+static void ccfc_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_forwarding_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_ccfc_set(struct ofono_call_forwarding *cf, const char *buf,
+                               ofono_call_forwarding_set_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_forwarding_get_data(cf);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               ccfc_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_ccfc_erasure(struct ofono_call_forwarding *cf,
+                               int type, int cls,
+                               ofono_call_forwarding_set_cb_t cb, void *data)
+{
+       char buf[128];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "AT+CCFC=%d,4", type);
+
+       if (cls != 7)
+               snprintf(buf + len, sizeof(buf) - len, ",,,%d", cls);
+
+       at_ccfc_set(cf, buf, cb, data);
+}
+
+static void at_ccfc_deactivation(struct ofono_call_forwarding *cf,
+                                       int type, int cls,
+                                       ofono_call_forwarding_set_cb_t cb,
+                                       void *data)
+{
+       char buf[128];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "AT+CCFC=%d,0", type);
+
+       if (cls != 7)
+               snprintf(buf + len, sizeof(buf) - len, ",,,%d", cls);
+
+       at_ccfc_set(cf, buf, cb, data);
+}
+
+static void at_ccfc_activation(struct ofono_call_forwarding *cf,
+                               int type, int cls,
+                               ofono_call_forwarding_set_cb_t cb, void *data)
+{
+       char buf[128];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "AT+CCFC=%d,1", type);
+
+       if (cls != 7)
+               snprintf(buf + len, sizeof(buf) - len, ",,,%d", cls);
+
+       at_ccfc_set(cf, buf, cb, data);
+}
+
+static void at_ccfc_registration(struct ofono_call_forwarding *cf,
+                                       int type, int cls,
+                                       const struct ofono_phone_number *ph,
+                                       int time,
+                                       ofono_call_forwarding_set_cb_t cb,
+                                       void *data)
+{
+       char buf[128];
+       int offset;
+
+       offset = snprintf(buf, sizeof(buf), "AT+CCFC=%d,3,\"%s\",%d,%d", type,
+                               ph->number, ph->type, cls);
+
+       if (type == 2 || type == 4 || type == 5)
+               snprintf(buf+offset, sizeof(buf) - offset, ",,,%d", time);
+
+       at_ccfc_set(cf, buf, cb, data);
+}
+
+static gboolean at_ccfc_register(gpointer user)
+{
+       struct ofono_call_forwarding *cf = user;
+
+       ofono_call_forwarding_register(cf);
+
+       return FALSE;
+}
+
+static int at_ccfc_probe(struct ofono_call_forwarding *cf, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+
+       ofono_call_forwarding_set_data(cf, g_at_chat_clone(chat));
+       g_idle_add(at_ccfc_register, cf);
+
+       return 0;
+}
+
+static void at_ccfc_remove(struct ofono_call_forwarding *cf)
+{
+       GAtChat *chat = ofono_call_forwarding_get_data(cf);
+
+       g_at_chat_unref(chat);
+       ofono_call_forwarding_set_data(cf, NULL);
+}
+
+static struct ofono_call_forwarding_driver driver = {
+       .name           = "atmodem",
+       .probe          = at_ccfc_probe,
+       .remove         = at_ccfc_remove,
+       .registration   = at_ccfc_registration,
+       .activation     = at_ccfc_activation,
+       .query          = at_ccfc_query,
+       .deactivation   = at_ccfc_deactivation,
+       .erasure        = at_ccfc_erasure
+};
+
+void at_call_forwarding_init(void)
+{
+       ofono_call_forwarding_driver_register(&driver);
+}
+
+void at_call_forwarding_exit(void)
+{
+       ofono_call_forwarding_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/call-meter.c b/drivers/atmodem/call-meter.c
new file mode 100644 (file)
index 0000000..430d546
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-meter.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *caoc_prefix[] = { "+CAOC:", NULL };
+static const char *cacm_prefix[] = { "+CACM:", NULL };
+static const char *camm_prefix[] = { "+CAMM:", NULL };
+static const char *cpuc_prefix[] = { "+CPUC:", NULL };
+
+static void caoc_cacm_camm_query_cb(gboolean ok,
+               GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_meter_query_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       const char *meter_hex;
+       char *end;
+       int meter;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, cbd->user))
+               goto error;
+
+       if (g_at_result_iter_next_string(&iter, &meter_hex) == FALSE)
+               goto error;
+
+       meter = strtol(meter_hex, &end, 16);
+       if (*end)
+               goto error;
+
+       cb(&error, meter, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void cccm_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_call_meter *cm = user_data;
+       GAtResultIter iter;
+       const char *meter_hex;
+       char *end;
+       int meter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CCCM:"))
+               return;
+
+       if (g_at_result_iter_next_string(&iter, &meter_hex) == FALSE)
+               goto error;
+
+       meter = strtol(meter_hex, &end, 16);
+       if (*end)
+               goto error;
+
+       ofono_call_meter_changed_notify(cm, meter);
+       return;
+
+error:
+       ofono_error("Invalid CCCM value");
+}
+
+static void at_caoc_query(struct ofono_call_meter *cm,
+                               ofono_call_meter_query_cb_t cb,
+                               void *data)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = "+CAOC:";
+       if (g_at_chat_send(chat, "AT+CAOC=0", caoc_prefix,
+                               caoc_cacm_camm_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void at_cacm_query(struct ofono_call_meter *cm,
+                               ofono_call_meter_query_cb_t cb,
+                               void *data)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = "+CACM:";
+       if (g_at_chat_send(chat, "AT+CACM?", cacm_prefix,
+                               caoc_cacm_camm_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void generic_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_meter_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_cacm_set(struct ofono_call_meter *cm, const char *passwd,
+                       ofono_call_meter_set_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CACM=\"%s\"", passwd);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               generic_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_camm_query(struct ofono_call_meter *cm,
+                               ofono_call_meter_query_cb_t cb,
+                               void *data)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = "+CAMM:";
+       if (g_at_chat_send(chat, "AT+CAMM?", camm_prefix,
+                               caoc_cacm_camm_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void at_camm_set(struct ofono_call_meter *cm,
+                       int accmax, const char *passwd,
+                       ofono_call_meter_set_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CAMM=\"%06X\",\"%s\"", accmax, passwd);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               generic_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void cpuc_query_cb(gboolean ok,
+                               GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_meter_puct_query_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       const char *currency, *ppu;
+       char currency_buf[64];
+       double ppuval;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, 0, 0, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, cbd->user) != TRUE)
+               goto error;
+
+       if (g_at_result_iter_next_string(&iter, &currency) != TRUE)
+               goto error;
+
+       strncpy(currency_buf, currency, sizeof(currency_buf));
+
+       if (g_at_result_iter_next_string(&iter, &ppu) != TRUE)
+               goto error;
+
+       ppuval = strtod(ppu, NULL);
+
+       cb(&error, currency_buf, ppuval, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, 0, cbd->data);
+}
+
+static void at_cpuc_query(struct ofono_call_meter *cm,
+                               ofono_call_meter_puct_query_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = "+CPUC:";
+       if (g_at_chat_send(chat, "AT+CPUC?", cpuc_prefix,
+                               cpuc_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, 0, 0, data);
+}
+
+static void at_cpuc_set(struct ofono_call_meter *cm, const char *currency,
+                       double ppu, const char *passwd,
+                       ofono_call_meter_set_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CPUC=\"%s\",\"%f\",\"%s\"",
+                       currency, ppu, passwd);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               generic_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ccwv_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_call_meter *cm = user_data;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+       if (!g_at_result_iter_next(&iter, "+CCWV"))
+               return;
+
+       ofono_call_meter_maximum_notify(cm);
+}
+
+static void at_call_meter_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_call_meter *cm = user_data;
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+
+       g_at_chat_register(chat, "+CCCM:", cccm_notify, FALSE, cm, NULL);
+       g_at_chat_register(chat, "+CCWV", ccwv_notify, FALSE, cm, NULL);
+
+       ofono_call_meter_register(cm);
+}
+
+static int at_caoc_probe(struct ofono_call_meter *cm, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+
+       chat = g_at_chat_clone(chat);
+       ofono_call_meter_set_data(cm, chat);
+
+       g_at_chat_send(chat, "AT+CAOC=2", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+CCWE=1", NULL,
+                       at_call_meter_initialized, cm, NULL);
+
+       return 0;
+}
+
+static void at_caoc_remove(struct ofono_call_meter *cm)
+{
+       GAtChat *chat = ofono_call_meter_get_data(cm);
+
+       g_at_chat_unref(chat);
+       ofono_call_meter_set_data(cm, NULL);
+}
+
+static struct ofono_call_meter_driver driver = {
+       .name = "atmodem",
+       .probe = at_caoc_probe,
+       .remove = at_caoc_remove,
+       .call_meter_query = at_caoc_query,
+       .acm_query = at_cacm_query,
+       .acm_reset = at_cacm_set,
+       .acm_max_query = at_camm_query,
+       .acm_max_set = at_camm_set,
+       .puct_query = at_cpuc_query,
+       .puct_set = at_cpuc_set,
+};
+
+void at_call_meter_init(void)
+{
+       ofono_call_meter_driver_register(&driver);
+}
+
+void at_call_meter_exit(void)
+{
+       ofono_call_meter_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/call-settings.c b/drivers/atmodem/call-settings.c
new file mode 100644 (file)
index 0000000..2dc16e4
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *clir_prefix[] = { "+CLIR:", NULL };
+static const char *colp_prefix[] = { "+COLP:", NULL };
+static const char *clip_prefix[] = { "+CLIP:", NULL };
+static const char *ccwa_prefix[] = { "+CCWA:", NULL };
+static const char *colr_prefix[] = { "+COLR:", NULL };
+static const char *cnap_prefix[] = { "+CNAP:", NULL };
+static const char *cdip_prefix[] = { "+CDIP:", NULL };
+
+static void ccwa_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_settings_status_cb_t cb = cbd->cb;
+       int conditions = 0;
+       int status;
+       int cls;
+       struct ofono_error error;
+       GAtResultIter iter;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto out;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+CCWA:")) {
+               g_at_result_iter_next_number(&iter, &status);
+               g_at_result_iter_next_number(&iter, &cls);
+
+               if (status == 1)
+                       conditions |= cls;
+       }
+
+       DBG("CW enabled for: %d", conditions);
+
+out:
+       cb(&error, conditions, cbd->data);
+}
+
+static void at_ccwa_query(struct ofono_call_settings *cs, int cls,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       cbd->user = GINT_TO_POINTER(cls);
+
+       if (cls == 7)
+               snprintf(buf, sizeof(buf), "AT+CCWA=1,2");
+       else
+               snprintf(buf, sizeof(buf), "AT+CCWA=1,2,%d", cls);
+
+       if (g_at_chat_send(chat, buf, ccwa_prefix,
+                               ccwa_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+}
+
+static void ccwa_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_settings_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_ccwa_set(struct ofono_call_settings *cs, int mode, int cls,
+                               ofono_call_settings_set_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CCWA=1,%d,%d", mode, cls);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               ccwa_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void query_template(const char *prefix, gboolean ok,
+                               GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_settings_status_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int status = -1;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, prefix) == FALSE)
+               goto error;
+
+       /* Skip the local presentation setting */
+       if (g_at_result_iter_skip_next(&iter) == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &status) == FALSE)
+               goto error;
+
+       DBG("prefix: %s, network: %d", prefix, status);
+
+       cb(&error, status, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void clip_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       query_template("+CLIP:", ok, result, user_data);
+}
+
+static void at_clip_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(chat, "AT+CLIP?", clip_prefix,
+                               clip_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void cdip_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       query_template("+CDIP:", ok, result, user_data);
+}
+
+static void at_cdip_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(chat, "AT+CDIP?", cdip_prefix,
+                               cdip_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void cnap_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       query_template("+CNAP:", ok, result, user_data);
+}
+
+static void at_cnap_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(chat, "AT+CNAP?", cnap_prefix,
+                               cnap_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void colp_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       query_template("+COLP:", ok, result, user_data);
+}
+
+static void at_colp_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(chat, "AT+COLP?", colp_prefix,
+                               colp_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void clir_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_settings_clir_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int override = 0, network = 2;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLIR:")) {
+               CALLBACK_WITH_FAILURE(cb, -1, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_next_number(&iter, &override);
+       g_at_result_iter_next_number(&iter, &network);
+
+       DBG("override: %d, network: %d", override, network);
+
+       cb(&error, override, network, cbd->data);
+}
+
+static void at_clir_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_clir_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(chat, "AT+CLIR?", clir_prefix,
+                               clir_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, -1, data);
+}
+
+static void clir_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_settings_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_clir_set(struct ofono_call_settings *cs, int mode,
+                               ofono_call_settings_set_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CLIR=%d", mode);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               clir_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void colr_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_settings_status_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int status;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+COLR:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &status) == FALSE)
+               goto error;
+
+       DBG("network: %d", status);
+
+       cb(&error, status, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void at_colr_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(chat, "AT+COLR", colr_prefix,
+                               colr_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static gboolean at_call_settings_register(gpointer user)
+{
+       struct ofono_call_settings *cs = user;
+
+       ofono_call_settings_register(cs);
+
+       return FALSE;
+}
+
+static int at_call_settings_probe(struct ofono_call_settings *cs,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+
+       ofono_call_settings_set_data(cs, g_at_chat_clone(chat));
+       g_idle_add(at_call_settings_register, cs);
+
+       return 0;
+}
+
+static void at_call_settings_remove(struct ofono_call_settings *cs)
+{
+       GAtChat *chat = ofono_call_settings_get_data(cs);
+
+       g_at_chat_unref(chat);
+       ofono_call_settings_set_data(cs, NULL);
+}
+
+static struct ofono_call_settings_driver driver = {
+       .name = "atmodem",
+       .probe = at_call_settings_probe,
+       .remove = at_call_settings_remove,
+       .clip_query = at_clip_query,
+       .cnap_query = at_cnap_query,
+       .cdip_query = at_cdip_query,
+       .colp_query = at_colp_query,
+       .clir_query = at_clir_query,
+       .clir_set = at_clir_set,
+       .colr_query = at_colr_query,
+       .cw_query = at_ccwa_query,
+       .cw_set = at_ccwa_set,
+};
+
+void at_call_settings_init(void)
+{
+       ofono_call_settings_driver_register(&driver);
+}
+
+void at_call_settings_exit(void)
+{
+       ofono_call_settings_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/call-volume.c b/drivers/atmodem/call-volume.c
new file mode 100644 (file)
index 0000000..e2535b1
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-volume.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *clvl_prefix[] = { "+CLVL:", NULL };
+static const char *cmut_prefix[] = { "+CMUT:", NULL };
+static const char *none_prefix[] = { NULL };
+
+struct cv_data {
+       int clvl_min;
+       int clvl_max;
+       GAtChat *chat;
+};
+
+static void cmut_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       GAtResultIter iter;
+       int muted;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CMUT:"))
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &muted) == FALSE)
+               return;
+
+       ofono_call_volume_set_muted(cv, muted);
+}
+
+static void clvl_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       struct cv_data *cvd = ofono_call_volume_get_data(cv);
+       GAtResultIter iter;
+       int lvl;
+       int percent;
+
+       if (!ok)
+               return;
+
+       if (cvd->clvl_max == 0 && cvd->clvl_min == 0)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLVL:"))
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &lvl) == FALSE)
+               return;
+
+       percent = ((lvl - cvd->clvl_min) * 100) /
+                               (cvd->clvl_max - cvd->clvl_min);
+
+       ofono_call_volume_set_speaker_volume(cv, percent);
+       ofono_call_volume_register(cv);
+}
+
+static void clvl_range_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       struct cv_data *cvd = ofono_call_volume_get_data(cv);
+       GAtResultIter iter;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLVL:"))
+               return;
+
+       /* Try opening the list, but don't fail */
+       g_at_result_iter_open_list(&iter);
+       g_at_result_iter_next_range(&iter, &cvd->clvl_min, &cvd->clvl_max);
+       g_at_result_iter_close_list(&iter);
+}
+
+static void cv_generic_set_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_volume_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_call_volume_speaker_volume(struct ofono_call_volume *cv,
+                                               unsigned char percent,
+                                               ofono_call_volume_cb_t cb,
+                                               void *data)
+{
+       struct cv_data *cvd = ofono_call_volume_get_data(cv);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+       int level;
+
+       level = ((cvd->clvl_max - cvd->clvl_min) *
+                       percent) / 100 + cvd->clvl_min;
+
+       snprintf(buf, sizeof(buf), "AT+CLVL=%d", level);
+
+       if (g_at_chat_send(cvd->chat, buf, none_prefix,
+                               cv_generic_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_call_volume_mute(struct ofono_call_volume *cv, int muted,
+                               ofono_call_volume_cb_t cb, void *data)
+{
+       struct cv_data *cvd = ofono_call_volume_get_data(cv);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CMUT=%d", muted);
+
+       if (g_at_chat_send(cvd->chat, buf, none_prefix,
+                               cv_generic_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static int at_call_volume_probe(struct ofono_call_volume *cv,
+                               unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct cv_data *cvd;
+
+       DBG("%p", cv);
+
+       cvd = g_new0(struct cv_data, 1);
+       cvd->chat = g_at_chat_clone(chat);
+
+       ofono_call_volume_set_data(cv, cvd);
+
+       g_at_chat_send(cvd->chat, "AT+CMUT?", cmut_prefix,
+                       cmut_query, cv, NULL);
+       g_at_chat_send(cvd->chat, "AT+CLVL=?", clvl_prefix,
+                       clvl_range_query, cv, NULL);
+       g_at_chat_send(cvd->chat, "AT+CLVL?", clvl_prefix,
+                       clvl_query, cv, NULL);
+
+       /* Generic driver does not support microphone level */
+       ofono_call_volume_set_microphone_volume(cv, 100);
+
+       return 0;
+}
+
+static void at_call_volume_remove(struct ofono_call_volume *cv)
+{
+       struct cv_data *cvd = ofono_call_volume_get_data(cv);
+
+       ofono_call_volume_set_data(cv, NULL);
+
+       g_at_chat_unref(cvd->chat);
+       g_free(cvd);
+}
+
+static struct ofono_call_volume_driver driver = {
+       .name = "atmodem",
+       .probe = at_call_volume_probe,
+       .remove = at_call_volume_remove,
+       .speaker_volume = at_call_volume_speaker_volume,
+       .mute = at_call_volume_mute,
+};
+
+void at_call_volume_init(void)
+{
+       ofono_call_volume_driver_register(&driver);
+}
+
+void at_call_volume_exit(void)
+{
+       ofono_call_volume_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/cbs.c b/drivers/atmodem/cbs.c
new file mode 100644 (file)
index 0000000..217445f
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cbs.h>
+#include "util.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+#include "vendor.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *cscb_prefix[] = { "+CSCB:", NULL };
+
+struct cbs_data {
+       GAtChat *chat;
+       gboolean cscb_mode_1;
+       unsigned int vendor;
+};
+
+static void at_cbm_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_cbs *cbs = user_data;
+       const char *hexpdu;
+       int pdulen;
+       GAtResultIter iter;
+       unsigned char pdu[88];
+       long hexpdulen;
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CBM:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &pdulen))
+               return;
+
+       if (pdulen != 88) {
+               ofono_error("Got a CBM message with invalid PDU size!");
+               return;
+       }
+
+       hexpdu = g_at_result_pdu(result);
+       if (hexpdu == NULL) {
+               ofono_error("Got a CBM, but no PDU.  Are we in text mode?");
+               return;
+       }
+
+       DBG("Got new Cell Broadcast via CBM: %s, %d", hexpdu, pdulen);
+
+       if (decode_hex_own_buf(hexpdu, -1, &hexpdulen, 0, pdu) == NULL) {
+               ofono_error("Unable to hex-decode the PDU");
+               return;
+       }
+
+       if (hexpdulen != pdulen) {
+               ofono_error("hexpdu length not equal to reported pdu length");
+               return;
+       }
+
+       ofono_cbs_notify(cbs, pdu, pdulen);
+}
+
+static void at_cscb_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_cbs_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_cbs_set_topics(struct ofono_cbs *cbs, const char *topics,
+                               ofono_cbs_set_cb_t cb, void *user_data)
+{
+       struct cbs_data *data = ofono_cbs_get_data(cbs);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char *buf;
+       unsigned int id;
+
+       DBG("");
+
+       /* For the Qualcomm based devices it is required to clear
+        * the list of topics first.  Otherwise setting the new
+        * topic ranges will fail.
+        *
+        * In addition only AT+CSCB=1 seems to work.  Providing
+        * a topic range for clearing makes AT+CSBC=0,... fail.
+        */
+       switch (data->vendor) {
+       case OFONO_VENDOR_GOBI:
+       case OFONO_VENDOR_QUALCOMM_MSM:
+               g_at_chat_send(data->chat, "AT+CSCB=1", none_prefix,
+                               NULL, NULL, NULL);
+               break;
+       default:
+               break;
+       }
+
+       buf = g_strdup_printf("AT+CSCB=0,\"%s\"", topics);
+
+       id = g_at_chat_send(data->chat, buf, none_prefix,
+                               at_cscb_set_cb, cbd, g_free);
+
+       g_free(buf);
+
+       if (id > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void at_cbs_clear_topics(struct ofono_cbs *cbs,
+                               ofono_cbs_set_cb_t cb, void *user_data)
+{
+       struct cbs_data *data = ofono_cbs_get_data(cbs);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char buf[256];
+
+       DBG("");
+
+       if (data->cscb_mode_1)
+               snprintf(buf, sizeof(buf), "AT+CSCB=1,\"0-65535\"");
+       else
+               snprintf(buf, sizeof(buf), "AT+CSCB=0,\"\"");
+
+       if (g_at_chat_send(data->chat, buf, none_prefix,
+                               at_cscb_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void at_cbs_register(gboolean ok, GAtResult *result, gpointer user)
+{
+       struct ofono_cbs *cbs = user;
+       struct cbs_data *data = ofono_cbs_get_data(cbs);
+
+       /* This driver assumes that something else will properly setup
+        * CNMI notifications to deliver CBS broadcasts via +CBM.  We do
+        * not setup CNMI string ourselves here to avoid race conditions
+        * with the SMS driver which will also be setting the CNMI itself
+        *
+        * The default SMS driver will setup the CNMI for +CBM delivery
+        * appropriately for us
+        */
+       g_at_chat_register(data->chat, "+CBM:", at_cbm_notify, TRUE, cbs, NULL);
+
+       ofono_cbs_register(cbs);
+}
+
+static void at_cscb_support_cb(gboolean ok, GAtResult *result, gpointer user)
+{
+       struct ofono_cbs *cbs = user;
+       struct cbs_data *data = ofono_cbs_get_data(cbs);
+       gint range[2];
+       GAtResultIter iter;
+       char buf[256];
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSCB:"))
+               goto error;
+
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       while (g_at_result_iter_next_range(&iter, &range[0], &range[1]))
+               if (1 >= range[0] && 1 <= range[1])
+                       data->cscb_mode_1 = TRUE;
+
+       g_at_result_iter_close_list(&iter);
+
+       /* Assume that if CSCB mode 1 is supported, then we need to use
+        * it to remove topics, otherwise we need to set the entire list
+        * of new topics using CSCB mode 0.
+        */
+       if (data->cscb_mode_1)
+               snprintf(buf, sizeof(buf), "AT+CSCB=1,\"0-65535\"");
+       else
+               snprintf(buf, sizeof(buf), "AT+CSCB=0,\"\"");
+
+       if (g_at_chat_send(data->chat, buf, none_prefix,
+                               at_cbs_register, cbs, NULL) > 0)
+               return;
+
+error:
+       ofono_error("CSCB not supported");
+       ofono_cbs_remove(cbs);
+}
+
+static int at_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
+                               void *user)
+{
+       GAtChat *chat = user;
+       struct cbs_data *data;
+
+       data = g_new0(struct cbs_data, 1);
+       data->chat = g_at_chat_clone(chat);
+       data->vendor = vendor;
+
+       ofono_cbs_set_data(cbs, data);
+
+       g_at_chat_send(data->chat, "AT+CSCB=?", cscb_prefix,
+                       at_cscb_support_cb, cbs, NULL);
+
+       return 0;
+}
+
+static void at_cbs_remove(struct ofono_cbs *cbs)
+{
+       struct cbs_data *data = ofono_cbs_get_data(cbs);
+
+       ofono_cbs_set_data(cbs, NULL);
+
+       g_at_chat_unref(data->chat);
+       g_free(data);
+}
+
+static struct ofono_cbs_driver driver = {
+       .name = "atmodem",
+       .probe = at_cbs_probe,
+       .remove = at_cbs_remove,
+       .set_topics = at_cbs_set_topics,
+       .clear_topics = at_cbs_clear_topics,
+};
+
+void at_cbs_init(void)
+{
+       ofono_cbs_driver_register(&driver);
+}
+
+void at_cbs_exit(void)
+{
+       ofono_cbs_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/devinfo.c b/drivers/atmodem/devinfo.c
new file mode 100644 (file)
index 0000000..c886835
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *gcap_prefix[] = { "+GCAP:", NULL };
+
+static void attr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_devinfo_query_cb_t cb = cbd->cb;
+       const char *prefix = cbd->user;
+       struct ofono_error error;
+       const char *attr;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       if (at_util_parse_attr(result, prefix, &attr) == FALSE) {
+               CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+               return;
+       }
+
+       cb(&error, attr, cbd->data);
+}
+
+static void at_query_manufacturer(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+CGMI:";
+
+       if (g_at_chat_send(chat, "AT+CGMI", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void at_query_model(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+CGMM:";
+
+       if (g_at_chat_send(chat, "AT+CGMM", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void at_query_revision(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+CGMR:";
+
+       if (g_at_chat_send(chat, "AT+CGMR", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void at_query_serial(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+CGSN:";
+
+       if (g_at_chat_send(chat, "AT+CGSN", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void capability_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_devinfo *info = user_data;
+
+       ofono_devinfo_register(info);
+}
+
+static int at_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = g_at_chat_clone(data);
+
+       ofono_devinfo_set_data(info, chat);
+
+       g_at_chat_send(chat, "AT+GCAP", gcap_prefix,
+                               capability_cb, info, NULL);
+
+       return 0;
+}
+
+static void at_devinfo_remove(struct ofono_devinfo *info)
+{
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       ofono_devinfo_set_data(info, NULL);
+
+       g_at_chat_unref(chat);
+}
+
+static struct ofono_devinfo_driver driver = {
+       .name                   = "atmodem",
+       .probe                  = at_devinfo_probe,
+       .remove                 = at_devinfo_remove,
+       .query_manufacturer     = at_query_manufacturer,
+       .query_model            = at_query_model,
+       .query_revision         = at_query_revision,
+       .query_serial           = at_query_serial,
+};
+
+void at_devinfo_init(void)
+{
+       ofono_devinfo_driver_register(&driver);
+}
+
+void at_devinfo_exit(void)
+{
+       ofono_devinfo_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/gnss.c b/drivers/atmodem/gnss.c
new file mode 100644 (file)
index 0000000..5d868dd
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gnss.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+#include "vendor.h"
+
+struct gnss_data {
+       GAtChat *chat;
+       unsigned int vendor;
+};
+
+static const char *none_prefix[] = { NULL };
+static const char *cpos_prefix[] = { "+CPOS:", NULL };
+static const char *cposr_prefix[] = { "+CPOSR:", NULL };
+
+static void gnss_pr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gnss_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_gnss_position_reporting(struct ofono_gnss *gnss,
+                                       ofono_bool_t enable,
+                                       ofono_gnss_cb_t cb,
+                                       void *data)
+{
+       struct gnss_data *ad = ofono_gnss_get_data(gnss);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       DBG("");
+
+       if (enable) {
+               g_at_chat_send(ad->chat, "AT+CPOSR=1",
+                               cposr_prefix, gnss_pr_cb, cbd, g_free);
+
+               if (ad->vendor == OFONO_VENDOR_STE)
+                       g_at_chat_send(ad->chat, "AT*EPOSADRR=1",
+                                       NULL, NULL, NULL, NULL);
+       } else {
+               g_at_chat_send(ad->chat, "AT+CPOSR=0",
+                               cposr_prefix, gnss_pr_cb, cbd, g_free);
+
+               if (ad->vendor == OFONO_VENDOR_STE)
+                       g_at_chat_send(ad->chat, "AT*EPOSADRR=0",
+                                       NULL, NULL, NULL, NULL);
+       }
+}
+
+static void gnss_se_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gnss_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_gnss_send_element(struct ofono_gnss *gnss,
+                               const char *xml,
+                               ofono_gnss_cb_t cb, void *data)
+{
+       struct gnss_data *ad = ofono_gnss_get_data(gnss);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, strlen(xml) + 10);
+       int len;
+
+       DBG("");
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT+CPOS\r");
+       len += sprintf(buf + len, "%s", xml);
+
+       if (g_at_chat_send_and_expect_short_prompt(ad->chat, buf, cpos_prefix,
+                                                       gnss_se_cb, cbd,
+                                                       g_free) > 0) {
+               g_free(buf);
+               return;
+       }
+
+error:
+       g_free(buf);
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static gboolean gnss_parse_report(GAtResult *result, const char *prefix,
+                                       const char **xml)
+{
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, prefix))
+               return FALSE;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, xml))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void gnss_report(GAtResult *result, gpointer user_data)
+{
+       const char *xml;
+
+       DBG("");
+
+       xml = NULL;
+
+       if (!gnss_parse_report(result, "+CPOSR:", &xml)) {
+               ofono_error("Unable to parse CPOSR notification");
+               return;
+       }
+
+       if (xml == NULL) {
+               ofono_error("Unable to parse CPOSR notification");
+               return;
+       }
+
+       DBG("%s", xml);
+}
+
+static void at_gnss_reset_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gnss *gnss = user_data;
+
+       DBG("");
+
+       ofono_gnss_notify_posr_reset(gnss);
+}
+
+static void at_gnss_not_supported(struct ofono_gnss *gnss)
+{
+       ofono_error("gnss not supported by this modem.");
+
+       ofono_gnss_remove(gnss);
+}
+
+static void at_gnss_cposr_support_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_gnss *gnss = user_data;
+       struct gnss_data *ad = ofono_gnss_get_data(gnss);
+
+       DBG("");
+
+       if (!ok) {
+               at_gnss_not_supported(gnss);
+               return;
+       }
+
+       g_at_chat_register(ad->chat, "+CPOSR:", gnss_report,
+                               FALSE, gnss, NULL);
+
+       if (ad->vendor == OFONO_VENDOR_STE)
+               g_at_chat_register(ad->chat, "*EPOSADRR:", at_gnss_reset_notify,
+                                       FALSE, gnss, NULL);
+
+       ofono_gnss_register(gnss);
+}
+
+static void at_gnss_cpos_support_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_gnss *gnss = user_data;
+       struct gnss_data *ad = ofono_gnss_get_data(gnss);
+
+       DBG("");
+
+       if (!ok) {
+               at_gnss_not_supported(gnss);
+               return;
+       }
+
+       g_at_chat_send(ad->chat, "AT+CPOSR=?",
+                       none_prefix, at_gnss_cposr_support_cb, gnss, NULL);
+}
+
+static int at_gnss_probe(struct ofono_gnss *gnss, unsigned int vendor,
+                               void *user)
+{
+       GAtChat *chat = user;
+       struct gnss_data *gd;
+
+       DBG("");
+
+       gd = g_try_new0(struct gnss_data, 1);
+       if (gd == NULL)
+               return -ENOMEM;
+
+       gd->chat = g_at_chat_clone(chat);
+       gd->vendor = vendor;
+
+       ofono_gnss_set_data(gnss, gd);
+
+       g_at_chat_send(gd->chat, "AT+CPOS=?",
+                       none_prefix, at_gnss_cpos_support_cb, gnss, NULL);
+
+       return 0;
+}
+
+static void at_gnss_remove(struct ofono_gnss *gnss)
+{
+       struct gnss_data *gd = ofono_gnss_get_data(gnss);
+
+       DBG("");
+
+       ofono_gnss_set_data(gnss, NULL);
+
+       g_at_chat_unref(gd->chat);
+       g_free(gd);
+}
+
+static struct ofono_gnss_driver driver = {
+       .name                   = "atmodem",
+       .probe                  = at_gnss_probe,
+       .remove                 = at_gnss_remove,
+       .send_element           = at_gnss_send_element,
+       .set_position_reporting = at_gnss_position_reporting,
+};
+
+void at_gnss_init(void)
+{
+       ofono_gnss_driver_register(&driver);
+}
+
+void at_gnss_exit(void)
+{
+       ofono_gnss_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/gprs-context.c b/drivers/atmodem/gprs-context.c
new file mode 100644 (file)
index 0000000..16893ce
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gatppp.h"
+
+#include "atmodem.h"
+#include "vendor.h"
+
+#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
+
+#define STATIC_IP_NETMASK "255.255.255.255"
+
+static const char *none_prefix[] = { NULL };
+
+enum state {
+       STATE_IDLE,
+       STATE_ENABLING,
+       STATE_DISABLING,
+       STATE_ACTIVE,
+};
+
+struct gprs_context_data {
+       GAtChat *chat;
+       unsigned int active_context;
+       char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+       char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+       GAtPPP *ppp;
+       enum state state;
+       ofono_gprs_context_cb_t cb;
+       void *cb_data;                                  /* Callback data */
+       unsigned int vendor;
+};
+
+static void ppp_debug(const char *str, void *data)
+{
+       ofono_info("%s: %s", (const char *) data, str);
+}
+
+static void ppp_connect(const char *interface, const char *local,
+                       const char *remote,
+                       const char *dns1, const char *dns2,
+                       gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       const char *dns[3];
+
+       DBG("");
+
+       dns[0] = dns1;
+       dns[1] = dns2;
+       dns[2] = 0;
+
+       ofono_info("IP: %s", local);
+       ofono_info("DNS: %s, %s", dns1, dns2);
+
+       gcd->state = STATE_ACTIVE;
+       ofono_gprs_context_set_interface(gc, interface);
+       ofono_gprs_context_set_ipv4_address(gc, local, TRUE);
+       ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK);
+       ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+}
+
+static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("");
+
+       g_at_ppp_unref(gcd->ppp);
+       gcd->ppp = NULL;
+
+       switch (gcd->state) {
+       case STATE_ENABLING:
+               CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+               break;
+       case STATE_DISABLING:
+               CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+               break;
+       default:
+               ofono_gprs_context_deactivated(gc, gcd->active_context);
+               break;
+       }
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+       /*
+        * If the channel of gcd->chat is NULL, it might cause
+        * gprs_context_remove get called and the gprs context will be
+        * removed.
+        */
+       g_at_chat_resume(gcd->chat);
+}
+
+static gboolean setup_ppp(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtIO *io;
+
+       DBG("");
+
+       io = g_at_chat_get_io(gcd->chat);
+
+       g_at_chat_suspend(gcd->chat);
+
+       /* open ppp */
+       gcd->ppp = g_at_ppp_new();
+
+       if (gcd->ppp == NULL) {
+               g_at_chat_resume(gcd->chat);
+               return FALSE;
+       }
+
+       if (getenv("OFONO_PPP_DEBUG"))
+               g_at_ppp_set_debug(gcd->ppp, ppp_debug, "PPP");
+
+       g_at_ppp_set_credentials(gcd->ppp, gcd->username, gcd->password);
+
+       /* set connect and disconnect callbacks */
+       g_at_ppp_set_connect_function(gcd->ppp, ppp_connect, gc);
+       g_at_ppp_set_disconnect_function(gcd->ppp, ppp_disconnect, gc);
+
+       /* open the ppp connection */
+       g_at_ppp_open(gcd->ppp, io);
+
+       return TRUE;
+}
+
+static void at_cgdata_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               struct ofono_error error;
+
+               ofono_info("Unable to enter data state");
+
+               gcd->active_context = 0;
+               gcd->state = STATE_IDLE;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               gcd->cb(&error, gcd->cb_data);
+               return;
+       }
+
+       setup_ppp(gc);
+}
+
+static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[64];
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               struct ofono_error error;
+
+               gcd->active_context = 0;
+               gcd->state = STATE_IDLE;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               gcd->cb(&error, gcd->cb_data);
+               return;
+       }
+
+       sprintf(buf, "AT+CGDATA=\"PPP\",%u", gcd->active_context);
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_cgdata_cb, gc, NULL) > 0)
+               return;
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+
+       CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+}
+
+static void at_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+       int len;
+
+       /* IPv6 support not implemented */
+       if (ctx->proto != OFONO_GPRS_PROTO_IP)
+               goto error;
+
+       DBG("cid %u", ctx->cid);
+
+       gcd->active_context = ctx->cid;
+       gcd->cb = cb;
+       gcd->cb_data = data;
+       memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+       memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+
+       gcd->state = STATE_ENABLING;
+
+       if (gcd->vendor == OFONO_VENDOR_ZTE) {
+               GAtChat *chat = g_at_chat_get_slave(gcd->chat);
+
+               /*
+                * The modem port of ZTE devices with certain firmware
+                * versions ends up getting suspended. It will no longer
+                * signal POLLOUT and becomes pretty unresponsive.
+                *
+                * To wake up the modem port, the only reliable method
+                * found so far is AT+ZOPRT power mode command. It is
+                * enough to ask for the current mode and the modem
+                * port wakes up and accepts commands again.
+                *
+                * And since the modem port is suspended, this command
+                * needs to be send on the control port of course.
+                *
+                */
+               g_at_chat_send(chat, "AT+ZOPRT?", none_prefix,
+                                               NULL, NULL, NULL);
+       }
+
+       len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+                               ctx->apn);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_cgdcont_cb, gc, NULL) > 0)
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int cid,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("cid %u", cid);
+
+       gcd->state = STATE_DISABLING;
+       gcd->cb = cb;
+       gcd->cb_data = data;
+
+       g_at_ppp_shutdown(gcd->ppp);
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       const char *event;
+       int cid;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CGEV:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+               return;
+
+       if (g_str_has_prefix(event, "NW DEACT") == FALSE)
+               return;
+
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &cid))
+               return;
+
+       DBG("cid %d", cid);
+
+       if ((unsigned int) cid != gcd->active_context)
+               return;
+
+       if (gcd->state != STATE_IDLE && gcd->ppp)
+               g_at_ppp_shutdown(gcd->ppp);
+}
+
+static int at_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+       struct stat st;
+
+       DBG("");
+
+       if (stat(TUN_SYSFS_DIR, &st) < 0) {
+               ofono_error("Missing support for TUN/TAP devices");
+               return -ENODEV;
+       }
+
+       gcd = g_try_new0(struct gprs_context_data, 1);
+       if (gcd == NULL)
+               return -ENOMEM;
+
+       gcd->chat = g_at_chat_clone(chat);
+       gcd->vendor = vendor;
+
+       ofono_gprs_context_set_data(gc, gcd);
+
+       chat = g_at_chat_get_slave(gcd->chat);
+       if (chat == NULL)
+               return 0;
+
+       g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+       return 0;
+}
+
+static void at_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("");
+
+       if (gcd->state != STATE_IDLE && gcd->ppp) {
+               g_at_ppp_unref(gcd->ppp);
+               g_at_chat_resume(gcd->chat);
+       }
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "atmodem",
+       .probe                  = at_gprs_context_probe,
+       .remove                 = at_gprs_context_remove,
+       .activate_primary       = at_gprs_activate_primary,
+       .deactivate_primary     = at_gprs_deactivate_primary,
+};
+
+void at_gprs_context_init(void)
+{
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void at_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/gprs.c b/drivers/atmodem/gprs.c
new file mode 100644 (file)
index 0000000..5f1d610
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+#include "vendor.h"
+
+static const char *cgreg_prefix[] = { "+CGREG:", NULL };
+static const char *cgdcont_prefix[] = { "+CGDCONT:", NULL };
+static const char *none_prefix[] = { NULL };
+
+struct gprs_data {
+       GAtChat *chat;
+       unsigned int vendor;
+};
+
+static void at_cgatt_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_gprs_set_attached(struct ofono_gprs *gprs, int attached,
+                                       ofono_gprs_cb_t cb, void *data)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CGATT=%i", attached ? 1 : 0);
+
+       if (g_at_chat_send(gd->chat, buf, none_prefix,
+                               at_cgatt_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_cgreg_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_status_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       int status;
+       struct gprs_data *gd = cbd->user;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       if (at_util_parse_reg(result, "+CGREG:", NULL, &status,
+                               NULL, NULL, NULL, gd->vendor) == FALSE) {
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       cb(&error, status, cbd->data);
+}
+
+static void at_gprs_registration_status(struct ofono_gprs *gprs,
+                                       ofono_gprs_status_cb_t cb,
+                                       void *data)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = gd;
+
+       switch (gd->vendor) {
+       case OFONO_VENDOR_GOBI:
+               /*
+                * Send *CNTI=0 to find out the current tech, it will be
+                * intercepted in gobi_cnti_notify in network registration
+                */
+               g_at_chat_send(gd->chat, "AT*CNTI=0", none_prefix,
+                               NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_NOVATEL:
+               /*
+                * Send $CNTI=0 to find out the current tech, it will be
+                * intercepted in nw_cnti_notify in network registration
+                */
+               g_at_chat_send(gd->chat, "AT$CNTI=0", none_prefix,
+                               NULL, NULL, NULL);
+               break;
+       }
+
+       if (g_at_chat_send(gd->chat, "AT+CGREG?", cgreg_prefix,
+                               at_cgreg_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void cgreg_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       int status;
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+
+       if (at_util_parse_reg_unsolicited(result, "+CGREG:", &status,
+                               NULL, NULL, NULL, gd->vendor) == FALSE)
+               return;
+
+       ofono_gprs_status_notify(gprs, status);
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       GAtResultIter iter;
+       const char *event;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CGEV:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+               return;
+
+       if (g_str_equal(event, "NW DETACH") ||
+                       g_str_equal(event, "ME DETACH")) {
+               ofono_gprs_detached_notify(gprs);
+               return;
+       }
+}
+
+static void xdatastat_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       GAtResultIter iter;
+       int stat;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XDATASTAT:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &stat))
+
+       DBG("stat %d", stat);
+
+       switch (stat) {
+       case 0:
+               ofono_gprs_suspend_notify(gprs, GPRS_SUSPENDED_UNKNOWN_CAUSE);
+               break;
+       case 1:
+               ofono_gprs_resume_notify(gprs);
+               break;
+       }
+}
+
+static void huawei_mode_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       GAtResultIter iter;
+       int mode, submode;
+       gint bearer;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^MODE:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &mode))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &submode))
+               return;
+
+       switch (submode) {
+       case 1:
+       case 2:
+               bearer = 1;     /* GPRS */
+               break;
+       case 3:
+               bearer = 2;     /* EDGE */
+               break;
+       case 4:
+               bearer = 3;     /* UMTS */
+               break;
+       case 5:
+               bearer = 5;     /* HSDPA */
+               break;
+       case 6:
+               bearer = 4;     /* HSUPA */
+               break;
+       case 7:
+       case 9:
+               bearer = 6;     /* HSUPA + HSDPA */
+               break;
+       default:
+               bearer = 0;
+               break;
+       }
+
+       ofono_gprs_bearer_notify(gprs, bearer);
+}
+
+static void cpsb_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       GAtResultIter iter;
+       gint bearer;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CPSB:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, NULL))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &bearer))
+               return;
+
+       ofono_gprs_bearer_notify(gprs, bearer);
+}
+
+static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+
+       g_at_chat_register(gd->chat, "+CGEV:", cgev_notify, FALSE, gprs, NULL);
+       g_at_chat_register(gd->chat, "+CGREG:", cgreg_notify,
+                                               FALSE, gprs, NULL);
+
+       switch (gd->vendor) {
+       case OFONO_VENDOR_HUAWEI:
+               g_at_chat_register(gd->chat, "^MODE:", huawei_mode_notify,
+                                               FALSE, gprs, NULL);
+               break;
+       default:
+               g_at_chat_register(gd->chat, "+CPSB:", cpsb_notify,
+                                               FALSE, gprs, NULL);
+               g_at_chat_send(gd->chat, "AT+CPSB=1", none_prefix,
+                                               NULL, NULL, NULL);
+               break;
+       }
+
+       switch (gd->vendor) {
+       case OFONO_VENDOR_IFX:
+               /* Register for GPRS suspend notifications */
+               g_at_chat_register(gd->chat, "+XDATASTAT:", xdatastat_notify,
+                                               FALSE, gprs, NULL);
+               g_at_chat_send(gd->chat, "AT+XDATASTAT=1", none_prefix,
+                                               NULL, NULL, NULL);
+               break;
+       }
+
+       ofono_gprs_register(gprs);
+}
+
+static void at_cgreg_test_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       gint range[2];
+       GAtResultIter iter;
+       int cgreg1 = 0;
+       int cgreg2 = 0;
+       const char *cmd;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CGREG:"))
+               goto error;
+
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) {
+               if (1 >= range[0] && 1 <= range[1])
+                       cgreg1 = 1;
+               if (2 >= range[0] && 2 <= range[1])
+                       cgreg2 = 1;
+       }
+
+       g_at_result_iter_close_list(&iter);
+
+       if (cgreg2)
+               cmd = "AT+CGREG=2";
+       else if (cgreg1)
+               cmd = "AT+CGREG=1";
+       else
+               goto error;
+
+       g_at_chat_send(gd->chat, cmd, none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(gd->chat, "AT+CGAUTO=0", none_prefix, NULL, NULL, NULL);
+
+       switch (gd->vendor) {
+       case OFONO_VENDOR_MBM:
+               /* Ericsson MBM and ST-E modems don't support AT+CGEREP=2,1 */
+               g_at_chat_send(gd->chat, "AT+CGEREP=1,0", none_prefix,
+                       gprs_initialized, gprs, NULL);
+               break;
+       case OFONO_VENDOR_NOKIA:
+               /* Nokia data cards don't support AT+CGEREP=1,0 either */
+               g_at_chat_send(gd->chat, "AT+CGEREP=1", none_prefix,
+                       gprs_initialized, gprs, NULL);
+               break;
+       default:
+               g_at_chat_send(gd->chat, "AT+CGEREP=2,1", none_prefix,
+                       gprs_initialized, gprs, NULL);
+               break;
+       }
+
+       return;
+
+error:
+       ofono_info("GPRS not supported on this device");
+       ofono_gprs_remove(gprs);
+}
+
+static void at_cgdcont_test_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       GAtResultIter iter;
+       int min, max;
+       const char *pdp_type;
+       gboolean found = FALSE;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (!found && g_at_result_iter_next(&iter, "+CGDCONT:")) {
+               gboolean in_list = FALSE;
+
+               if (!g_at_result_iter_open_list(&iter))
+                       continue;
+
+               if (g_at_result_iter_next_range(&iter, &min, &max) == FALSE)
+                       continue;
+
+               if (!g_at_result_iter_close_list(&iter))
+                       continue;
+
+               if (g_at_result_iter_open_list(&iter))
+                       in_list = TRUE;
+
+               if (!g_at_result_iter_next_string(&iter, &pdp_type))
+                       continue;
+
+               if (in_list && !g_at_result_iter_close_list(&iter))
+                       continue;
+
+               /* We look for IP PDPs */
+               if (g_str_equal(pdp_type, "IP"))
+                       found = TRUE;
+       }
+
+       if (found == FALSE)
+               goto error;
+
+       ofono_gprs_set_cid_range(gprs, min, max);
+
+       g_at_chat_send(gd->chat, "AT+CGREG=?", cgreg_prefix,
+                       at_cgreg_test_cb, gprs, NULL);
+
+       return;
+
+error:
+       ofono_info("GPRS not supported on this device");
+       ofono_gprs_remove(gprs);
+}
+
+static int at_gprs_probe(struct ofono_gprs *gprs,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_data *gd;
+
+       gd = g_try_new0(struct gprs_data, 1);
+       if (gd == NULL)
+               return -ENOMEM;
+
+       gd->chat = g_at_chat_clone(chat);
+       gd->vendor = vendor;
+
+       ofono_gprs_set_data(gprs, gd);
+
+       g_at_chat_send(gd->chat, "AT+CGDCONT=?", cgdcont_prefix,
+                       at_cgdcont_test_cb, gprs, NULL);
+
+       return 0;
+}
+
+static void at_gprs_remove(struct ofono_gprs *gprs)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+
+       ofono_gprs_set_data(gprs, NULL);
+
+       g_at_chat_unref(gd->chat);
+       g_free(gd);
+}
+
+static struct ofono_gprs_driver driver = {
+       .name                   = "atmodem",
+       .probe                  = at_gprs_probe,
+       .remove                 = at_gprs_remove,
+       .set_attached           = at_gprs_set_attached,
+       .attached_status        = at_gprs_registration_status,
+};
+
+void at_gprs_init(void)
+{
+       ofono_gprs_driver_register(&driver);
+}
+
+void at_gprs_exit(void)
+{
+       ofono_gprs_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/network-registration.c b/drivers/atmodem/network-registration.c
new file mode 100644 (file)
index 0000000..936a674
--- /dev/null
@@ -0,0 +1,1619 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/netreg.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "common.h"
+#include "atmodem.h"
+#include "vendor.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *creg_prefix[] = { "+CREG:", NULL };
+static const char *cops_prefix[] = { "+COPS:", NULL };
+static const char *csq_prefix[] = { "+CSQ:", NULL };
+static const char *cind_prefix[] = { "+CIND:", NULL };
+static const char *zpas_prefix[] = { "+ZPAS:", NULL };
+static const char *option_tech_prefix[] = { "_OCTI:", "_OUWCTI:", NULL };
+
+struct netreg_data {
+       GAtChat *chat;
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       int signal_index; /* If strength is reported via CIND */
+       int signal_min; /* min strength reported via CIND */
+       int signal_max; /* max strength reported via CIND */
+       int signal_invalid; /* invalid strength reported via CIND */
+       int tech;
+       struct ofono_network_time time;
+       guint nitz_timeout;
+       unsigned int vendor;
+};
+
+struct tech_query {
+       int status;
+       int lac;
+       int ci;
+       struct ofono_netreg *netreg;
+};
+
+static void extract_mcc_mnc(const char *str, char *mcc, char *mnc)
+{
+       /* Three digit country code */
+       strncpy(mcc, str, OFONO_MAX_MCC_LENGTH);
+       mcc[OFONO_MAX_MCC_LENGTH] = '\0';
+
+       /* Usually a 2 but sometimes 3 digit network code */
+       strncpy(mnc, str + OFONO_MAX_MCC_LENGTH, OFONO_MAX_MNC_LENGTH);
+       mnc[OFONO_MAX_MNC_LENGTH] = '\0';
+}
+
+static int zte_parse_tech(GAtResult *result)
+{
+       GAtResultIter iter;
+       const char *network, *domain;
+       int tech;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+ZPAS:"))
+               return -1;
+
+       if (!g_at_result_iter_next_string(&iter, &network))
+               return -1;
+
+       if (!g_at_result_iter_next_string(&iter, &domain))
+               return -1;
+
+       if (g_str_equal(network, "GSM") == TRUE ||
+                       g_str_equal(network, "GPRS") == TRUE)
+               tech = ACCESS_TECHNOLOGY_GSM;
+       else if (g_str_equal(network, "EDGE") == TRUE)
+               tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
+       else if (g_str_equal(network, "UMTS") == TRUE)
+               tech = ACCESS_TECHNOLOGY_UTRAN;
+       else if (g_str_equal(network, "HSDPA") == TRUE)
+               tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
+       else
+               tech = -1;
+
+       DBG("network %s domain %s tech %d", network, domain, tech);
+
+       return tech;
+}
+
+static int option_parse_tech(GAtResult *result)
+{
+       GAtResultIter iter;
+       int s, octi, ouwcti;
+       int tech;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "_OCTI:"))
+               return -1;
+
+       if (!g_at_result_iter_next_number(&iter, &s))
+               return -1;
+
+       if (!g_at_result_iter_next_number(&iter, &octi))
+               return -1;
+
+       if (!g_at_result_iter_next(&iter, "_OUWCTI:"))
+               return -1;
+
+       if (!g_at_result_iter_next_number(&iter, &s))
+               return -1;
+
+       if (!g_at_result_iter_next_number(&iter, &ouwcti))
+               return -1;
+
+       switch (octi) {
+       case 1: /* GSM */
+               tech = ACCESS_TECHNOLOGY_GSM;
+               break;
+       case 2: /* GPRS */
+               tech = ACCESS_TECHNOLOGY_GSM;
+               break;
+       case 3: /* EDGE */
+               tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
+               break;
+       default:
+               tech = -1;
+               break;
+       }
+
+       switch (ouwcti) {
+       case 1: /* UMTS */
+               tech = ACCESS_TECHNOLOGY_UTRAN;
+               break;
+       case 2: /* HSDPA */
+               tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
+               break;
+       case 3: /* HSUPA */
+               tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA;
+               break;
+       case 4: /* HSPA */
+               tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
+               break;
+       }
+
+       DBG("octi %d ouwcti %d tech %d", octi, ouwcti, tech);
+
+       return tech;
+}
+
+static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_status_cb_t cb = cbd->cb;
+       int status, lac, ci, tech;
+       struct ofono_error error;
+       struct netreg_data *nd = cbd->user;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, -1, -1, -1, cbd->data);
+               return;
+       }
+
+       if (at_util_parse_reg(result, "+CREG:", NULL, &status,
+                               &lac, &ci, &tech, nd->vendor) == FALSE) {
+               CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data);
+               return;
+       }
+
+       if ((status == 1 || status == 5) && (tech == -1))
+               tech = nd->tech;
+
+       cb(&error, status, lac, ci, tech, cbd->data);
+}
+
+static void zte_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_netreg *netreg = cbd->data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (ok)
+               nd->tech = zte_parse_tech(result);
+       else
+               nd->tech = -1;
+}
+
+static void option_tech_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_netreg *netreg = cbd->data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (ok)
+               nd->tech = option_parse_tech(result);
+       else
+               nd->tech = -1;
+}
+
+static void at_registration_status(struct ofono_netreg *netreg,
+                                       ofono_netreg_status_cb_t cb,
+                                       void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = nd;
+
+       switch (nd->vendor) {
+       case OFONO_VENDOR_MBM:
+               /*
+                * Send *ERINFO to find out the current tech, it will be
+                * intercepted in mbm_erinfo_notify
+                */
+               g_at_chat_send(nd->chat, "AT*ERINFO?", none_prefix,
+                               NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_GOBI:
+               /*
+                * Send *CNTI=0 to find out the current tech, it will be
+                * intercepted in gobi_cnti_notify
+                */
+               g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
+                               NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_NOVATEL:
+               /*
+                * Send $CNTI=0 to find out the current tech, it will be
+                * intercepted in nw_cnti_notify
+                */
+               g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix,
+                               NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_ZTE:
+               /*
+                * Send +ZPAS? to find out the current tech, zte_tech_cb
+                * will call, fire CREG? to do the rest.
+                */
+               if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix,
+                                       zte_tech_cb, cbd, NULL) == 0)
+                       nd->tech = -1;
+               break;
+       case OFONO_VENDOR_OPTION_HSO:
+               /*
+                * Send AT_OCTI?;_OUWCTI? to find out the current tech,
+                * option_tech_cb will call, fire CREG? to do the rest.
+                */
+               if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
+                                       option_tech_prefix,
+                                       option_tech_cb, cbd, NULL) == 0)
+                       nd->tech = -1;
+               break;
+       }
+
+       if (g_at_chat_send(nd->chat, "AT+CREG?", creg_prefix,
+                               at_creg_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
+}
+
+static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
+       ofono_netreg_operator_cb_t cb = cbd->cb;
+       struct ofono_network_operator op;
+       GAtResultIter iter;
+       int format, tech;
+       const char *name;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+COPS:"))
+               goto error;
+
+       g_at_result_iter_skip_next(&iter);
+
+       ok = g_at_result_iter_next_number(&iter, &format);
+
+       if (ok == FALSE || format != 0)
+               goto error;
+
+       if (g_at_result_iter_next_string(&iter, &name) == FALSE)
+               goto error;
+
+       /* Default to GSM */
+       if (g_at_result_iter_next_number(&iter, &tech) == FALSE)
+               tech = ACCESS_TECHNOLOGY_GSM;
+
+       strncpy(op.name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
+       op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+
+       strncpy(op.mcc, nd->mcc, OFONO_MAX_MCC_LENGTH);
+       op.mcc[OFONO_MAX_MCC_LENGTH] = '\0';
+
+       strncpy(op.mnc, nd->mnc, OFONO_MAX_MNC_LENGTH);
+       op.mnc[OFONO_MAX_MNC_LENGTH] = '\0';
+
+       /* Set to current */
+       op.status = 2;
+       op.tech = tech;
+
+       DBG("cops_cb: %s, %s %s %d", name, nd->mcc, nd->mnc, tech);
+
+       cb(&error, &op, cbd->data);
+       g_free(cbd);
+
+       return;
+
+error:
+       cb(&error, NULL, cbd->data);
+
+       g_free(cbd);
+}
+
+static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
+       ofono_netreg_operator_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       const char *str;
+       int format;
+       int len;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+COPS:"))
+               goto error;
+
+       g_at_result_iter_skip_next(&iter);
+
+       ok = g_at_result_iter_next_number(&iter, &format);
+
+       if (ok == FALSE || format != 2)
+               goto error;
+
+       if (g_at_result_iter_next_string(&iter, &str) == FALSE)
+               goto error;
+
+       len = strspn(str, "0123456789");
+
+       if (len != 5 && len != 6)
+               goto error;
+
+       extract_mcc_mnc(str, nd->mcc, nd->mnc);
+
+       DBG("Cops numeric got mcc: %s, mnc: %s", nd->mcc, nd->mnc);
+
+       ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix,
+                                       NULL, NULL, NULL);
+
+       if (ok)
+               ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
+                                       cops_cb, cbd, NULL);
+
+       if (ok)
+               return;
+
+error:
+       cb(&error, NULL, cbd->data);
+       g_free(cbd);
+}
+
+static void at_current_operator(struct ofono_netreg *netreg,
+                               ofono_netreg_operator_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       gboolean ok;
+
+       cbd->user = netreg;
+
+       /* Nokia modems have a broken return value for the string
+        * returned for the numeric value. It misses a " at the end.
+        * Trying to read this will stall the parser. So skip it. */
+       if (nd->vendor == OFONO_VENDOR_NOKIA) {
+               ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix,
+                                                       NULL, NULL, NULL);
+
+               if (ok)
+                       ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
+                                                       cops_cb, cbd, NULL);
+       } else {
+               ok = g_at_chat_send(nd->chat, "AT+COPS=3,2", none_prefix,
+                                                       NULL, NULL, NULL);
+
+               if (ok)
+                       ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
+                                               cops_numeric_cb, cbd, NULL);
+       }
+
+       if (ok)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_operator_list_cb_t cb = cbd->cb;
+       struct ofono_network_operator *list;
+       GAtResultIter iter;
+       int num = 0;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, 0, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+COPS:")) {
+               while (g_at_result_iter_skip_next(&iter))
+                       num += 1;
+       }
+
+       DBG("Got %d elements", num);
+
+       list = g_try_new0(struct ofono_network_operator, num);
+       if (list == NULL) {
+               CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
+               return;
+       }
+
+       num = 0;
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+COPS:")) {
+               int status, tech, plmn;
+               const char *l, *s, *n;
+               gboolean have_long = FALSE;
+
+               while (1) {
+                       if (!g_at_result_iter_open_list(&iter))
+                               break;
+
+                       if (!g_at_result_iter_next_number(&iter, &status))
+                               break;
+
+                       list[num].status = status;
+
+                       if (!g_at_result_iter_next_string(&iter, &l))
+                               break;
+
+                       if (strlen(l) > 0) {
+                               have_long = TRUE;
+                               strncpy(list[num].name, l,
+                                       OFONO_MAX_OPERATOR_NAME_LENGTH);
+                       }
+
+                       if (!g_at_result_iter_next_string(&iter, &s))
+                               break;
+
+                       if (strlen(s) > 0 && !have_long)
+                               strncpy(list[num].name, s,
+                                       OFONO_MAX_OPERATOR_NAME_LENGTH);
+
+                       list[num].name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+
+                       if (!g_at_result_iter_next_string(&iter, &n))
+                               break;
+
+                       extract_mcc_mnc(n, list[num].mcc, list[num].mnc);
+
+                       if (!g_at_result_iter_next_number(&iter, &tech))
+                               tech = ACCESS_TECHNOLOGY_GSM;
+
+                       list[num].tech = tech;
+
+                       if (!g_at_result_iter_next_number(&iter, &plmn))
+                               plmn = 0;
+
+                       if (!g_at_result_iter_close_list(&iter))
+                               break;
+
+                       num += 1;
+               }
+       }
+
+       DBG("Got %d operators", num);
+
+{
+       int i = 0;
+
+       for (; i < num; i++) {
+               DBG("Operator: %s, %s, %s, status: %d, %d",
+                       list[i].name, list[i].mcc, list[i].mnc,
+                       list[i].status, list[i].tech);
+       }
+}
+
+       cb(&error, num, list, cbd->data);
+
+       g_free(list);
+}
+
+static void at_list_operators(struct ofono_netreg *netreg,
+                               ofono_netreg_operator_list_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(nd->chat, "AT+COPS=?", cops_prefix,
+                               cops_list_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
+}
+
+static void register_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_register_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_register_auto(struct ofono_netreg *netreg,
+                               ofono_netreg_register_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(nd->chat, "AT+COPS=0", none_prefix,
+                               register_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_register_manual(struct ofono_netreg *netreg,
+                               const char *mcc, const char *mnc,
+                               ofono_netreg_register_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[128];
+
+       snprintf(buf, sizeof(buf), "AT+COPS=1,2,\"%s%s\"", mcc, mnc);
+
+       if (g_at_chat_send(nd->chat, buf, none_prefix,
+                               register_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void csq_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int strength;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSQ:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               return;
+
+       ofono_netreg_strength_notify(netreg,
+                               at_util_convert_signal_strength(strength));
+}
+
+static void calypso_csq_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int strength;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "%CSQ:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               return;
+
+       ofono_netreg_strength_notify(netreg,
+                               at_util_convert_signal_strength(strength));
+}
+
+static void option_osigq_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int strength;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "_OSIGQ:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               return;
+
+       ofono_netreg_strength_notify(netreg,
+                               at_util_convert_signal_strength(strength));
+}
+
+static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data)
+{
+       //struct ofono_netreg *netreg = user_data;
+       const char *label;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XHOMEZR:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &label))
+               return;
+
+       ofono_info("Home zone: %s", label);
+}
+
+static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int strength, ind;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XCIEV:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &ind))
+               return;
+
+       if (ind == 0)
+               strength = -1;
+       else if (ind == 7)
+               strength = 100;
+       else
+               strength = (ind * 15);
+
+       ofono_netreg_strength_notify(netreg, strength);
+}
+
+static void ciev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       int strength, ind;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIEV:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &ind))
+               return;
+
+       if (ind != nd->signal_index)
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               return;
+
+       if (strength == nd->signal_invalid)
+               strength = -1;
+       else
+               strength = (strength * 100) / (nd->signal_max - nd->signal_min);
+
+       ofono_netreg_strength_notify(netreg, strength);
+}
+
+static void ctzv_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       const char *tz;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CTZV:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
+               return;
+
+       DBG("tz %s", tz);
+
+       nd->time.utcoff = atoi(tz) * 15 * 60;
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+}
+
+static gboolean notify_time(gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       nd->nitz_timeout = 0;
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+
+       return FALSE;
+}
+
+static void ifx_ctzv_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       int year, mon, mday, hour, min, sec;
+       const char *tz, *time;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CTZV:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &time))
+               return;
+
+       DBG("tz %s time %s", tz, time);
+
+       if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
+                                               &hour, &min, &sec) != 6)
+               return;
+
+       nd->time.sec = sec;
+       nd->time.min = min;
+       nd->time.hour = hour;
+       nd->time.mday = mday;
+       nd->time.mon = mon;
+       nd->time.year = 2000 + year;
+
+       if (nd->nitz_timeout > 0)
+               g_source_remove(nd->nitz_timeout);
+
+       nd->nitz_timeout = g_timeout_add_seconds(1, notify_time, user_data);
+}
+
+static void ifx_ctzdst_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       int dst;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CTZDST:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &dst))
+               return;
+
+       DBG("dst %d", dst);
+
+       nd->time.dst = dst;
+
+       if (nd->nitz_timeout > 0) {
+               g_source_remove(nd->nitz_timeout);
+               nd->nitz_timeout = 0;
+       }
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+}
+
+static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_strength_cb_t cb = cbd->cb;
+       struct netreg_data *nd = cbd->user;
+       int index;
+       int strength;
+       GAtResultIter iter;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIND:")) {
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       for (index = 1; index < nd->signal_index; index++)
+               g_at_result_iter_skip_next(&iter);
+
+       g_at_result_iter_next_number(&iter, &strength);
+
+       if (strength == nd->signal_invalid)
+               strength = -1;
+       else
+               strength = (strength * 100) / (nd->signal_max - nd->signal_min);
+
+       cb(&error, strength, cbd->data);
+}
+
+static void huawei_rssi_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       GAtResultIter iter;
+       int strength;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^RSSI:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               return;
+
+       ofono_netreg_strength_notify(netreg,
+                               at_util_convert_signal_strength(strength));
+}
+
+static void huawei_mode_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GAtResultIter iter;
+       int mode, submode;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^MODE:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &mode))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &submode))
+               return;
+
+       switch (mode) {
+       case 3:
+               nd->tech = ACCESS_TECHNOLOGY_GSM;
+               break;
+       case 5:
+               nd->tech = ACCESS_TECHNOLOGY_UTRAN;
+               break;
+       }
+}
+
+static void huawei_nwtime_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       int year, mon, mday, hour, min, sec;
+       char tz[4];
+       const char *date, *time, *dst;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^NWTIME:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &date))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &time))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &dst))
+               return;
+
+       DBG("date %s time %s dst %s", date, time, dst);
+
+       if (sscanf(date, "%u/%u/%u", &year, &mon, &mday) != 3)
+               return;
+
+       if (sscanf(time, "%u:%u:%u%s", &hour, &min, &sec, tz) != 4)
+               return;
+
+       nd->time.utcoff = atoi(tz) * 15 * 60;
+       nd->time.dst = atoi(dst);
+
+       nd->time.sec = sec;
+       nd->time.min = min;
+       nd->time.hour = hour;
+       nd->time.mday = mday;
+       nd->time.mon = mon;
+       nd->time.year = 2000 + year;
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+}
+
+static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_strength_cb_t cb = cbd->cb;
+       int strength;
+       GAtResultIter iter;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSQ:")) {
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_next_number(&iter, &strength);
+
+       DBG("csq_cb: %d", strength);
+
+       if (strength == 99)
+               strength = -1;
+       else
+               strength = (strength * 100) / 31;
+
+       cb(&error, strength, cbd->data);
+}
+
+static void at_signal_strength(struct ofono_netreg *netreg,
+                               ofono_netreg_strength_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = nd;
+
+       /*
+        * If we defaulted to using CIND, then keep using it,
+        * otherwise fall back to CSQ
+        */
+       if (nd->signal_index > 0) {
+               if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix,
+                                       cind_cb, cbd, g_free) > 0)
+                       return;
+       } else {
+               if (g_at_chat_send(nd->chat, "AT+CSQ", csq_prefix,
+                               csq_cb, cbd, g_free) > 0)
+                       return;
+       }
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void mbm_etzv_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       int year, mon, mday, hour, min, sec;
+       const char *tz, *time, *timestamp;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*ETZV:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_string(&iter, &tz) == FALSE)
+               return;
+
+       if (g_at_result_iter_next_string(&iter, &time) == FALSE)
+               time = NULL;
+
+       if (g_at_result_iter_next_string(&iter, &timestamp) == FALSE)
+               timestamp = NULL;
+
+       DBG("tz %s time %s timestamp %s", tz, time, timestamp);
+
+       if (time == NULL) {
+               year = -1;
+               mon = -1;
+               mday = -1;
+               hour = -1;
+               min = -1;
+               sec = -1;
+       } else {
+               if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
+                                               &hour, &min, &sec) != 6)
+               return;
+       }
+
+       nd->time.utcoff = atoi(tz) * 15 * 60;
+
+       nd->time.sec = sec;
+       nd->time.min = min;
+       nd->time.hour = hour;
+       nd->time.mday = mday;
+       nd->time.mon = mon;
+       nd->time.year = year;
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+}
+
+static void mbm_erinfo_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GAtResultIter iter;
+       int mode, gsm, umts;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*ERINFO:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &gsm) == FALSE)
+               return;
+
+       /*
+        * According to MBM the ERINFO unsolicited response does not contain
+        * the mode parameter, however at least the MD300 does report it.  So
+        * we handle both 2 and 3 argument versions
+        */
+       if (g_at_result_iter_next_number(&iter, &umts) == FALSE) {
+               gsm = mode;
+               umts = gsm;
+       }
+
+       ofono_info("network capability: GSM %d UMTS %d", gsm, umts);
+
+       /* Convert to tech values from 27.007 */
+       switch (gsm) {
+       case 1: /* GSM */
+               nd->tech = ACCESS_TECHNOLOGY_GSM;
+               break;
+       case 2: /* EDGE */
+               nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
+               break;
+       default:
+               nd->tech = -1;
+       }
+
+       switch (umts) {
+       case 1: /* UMTS */
+               nd->tech = ACCESS_TECHNOLOGY_UTRAN;
+               break;
+       case 2: /* UMTS + HSDPA */
+               nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
+               break;
+       }
+}
+
+static int cnti_to_tech(const char *cnti)
+{
+       if (g_str_equal(cnti, "GSM") == TRUE ||
+                       g_str_equal(cnti, "GPRS") == TRUE)
+               return ACCESS_TECHNOLOGY_GSM;
+       else if (g_str_equal(cnti, "EDGE") == TRUE)
+               return ACCESS_TECHNOLOGY_GSM_EGPRS;
+       else if (g_str_equal(cnti, "UMTS") == TRUE)
+               return ACCESS_TECHNOLOGY_UTRAN;
+       else if (g_str_equal(cnti, "HSDPA") == TRUE)
+               return ACCESS_TECHNOLOGY_UTRAN_HSDPA;
+       else if (g_str_equal(cnti, "HSUPA") == TRUE)
+               return ACCESS_TECHNOLOGY_UTRAN_HSUPA;
+
+       return -1;
+}
+
+static void gobi_cnti_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GAtResultIter iter;
+       const char *tech;
+       int option;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*CNTI:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &option) == FALSE)
+               return;
+
+       if (option != 0)
+               return;
+
+       if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
+               return;
+
+       nd->tech = cnti_to_tech(tech);
+}
+
+static void nw_cnti_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GAtResultIter iter;
+       const char *tech;
+       int option;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "$CNTI:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &option) == FALSE)
+               return;
+
+       if (option != 0)
+               return;
+
+       if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE)
+               return;
+
+       nd->tech = cnti_to_tech(tech);
+}
+
+static void cnti_query_tech_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct tech_query *tq = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(tq->netreg);
+
+       ofono_netreg_status_notify(tq->netreg,
+                       tq->status, tq->lac, tq->ci, nd->tech);
+}
+
+static void zte_query_tech_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct tech_query *tq = user_data;
+       int tech;
+
+       if (ok)
+               tech = zte_parse_tech(result);
+       else
+               tech = -1;
+
+       ofono_netreg_status_notify(tq->netreg,
+                       tq->status, tq->lac, tq->ci, tech);
+}
+
+static void option_query_tech_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct tech_query *tq = user_data;
+       int tech;
+
+       if (ok)
+               tech = option_parse_tech(result);
+       else
+               tech = -1;
+
+       ofono_netreg_status_notify(tq->netreg,
+                       tq->status, tq->lac, tq->ci, tech);
+}
+
+static void creg_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int status, lac, ci, tech;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct tech_query *tq;
+
+       if (at_util_parse_reg_unsolicited(result, "+CREG:", &status,
+                               &lac, &ci, &tech, nd->vendor) == FALSE)
+               return;
+
+       if (status != 1 && status != 5)
+               goto notify;
+
+       tq = g_try_new0(struct tech_query, 1);
+       if (tq == NULL)
+               goto notify;
+
+       tq->status = status;
+       tq->lac = lac;
+       tq->ci = ci;
+       tq->netreg = netreg;
+
+       switch (nd->vendor) {
+       case OFONO_VENDOR_GOBI:
+               if (g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
+                                       cnti_query_tech_cb, tq, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_NOVATEL:
+               if (g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix,
+                                       cnti_query_tech_cb, tq, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_ZTE:
+               if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix,
+                                       zte_query_tech_cb, tq, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_OPTION_HSO:
+               if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?",
+                                       option_tech_prefix,
+                                       option_query_tech_cb, tq, g_free) > 0)
+                       return;
+               break;
+       }
+
+       g_free(tq);
+
+       if ((status == 1 || status == 5) && tech == -1)
+               tech = nd->tech;
+
+notify:
+       ofono_netreg_status_notify(netreg, status, lac, ci, tech);
+}
+
+static void cind_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GAtResultIter iter;
+       const char *str;
+       char *signal_identifier = "signal";
+       int index;
+       int min = 0;
+       int max = 0;
+       int tmp_min, tmp_max, invalid;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+       if (!g_at_result_iter_next(&iter, "+CIND:"))
+               goto error;
+
+       index = 1;
+
+       /*
+        * Telit encapsulates the CIND=? tokens with braces
+        * so we need to skip them
+        */
+       if (nd->vendor == OFONO_VENDOR_TELIT) {
+               g_at_result_iter_open_list(&iter);
+               signal_identifier = "rssi";
+       }
+
+       while (g_at_result_iter_open_list(&iter)) {
+               /* Reset invalid default value for every token */
+               invalid = 99;
+
+               if (!g_at_result_iter_next_string(&iter, &str))
+                       goto error;
+
+               if (!g_at_result_iter_open_list(&iter))
+                       goto error;
+
+               while (g_at_result_iter_next_range(&iter, &tmp_min, &tmp_max)) {
+                       if (tmp_min != tmp_max) {
+                               min = tmp_min;
+                               max = tmp_max;
+                       } else
+                               invalid = tmp_min;
+               }
+
+               if (!g_at_result_iter_close_list(&iter))
+                       goto error;
+
+               if (!g_at_result_iter_close_list(&iter))
+                       goto error;
+
+               if (g_str_equal(signal_identifier, str) == TRUE) {
+                       nd->signal_index = index;
+                       nd->signal_min = min;
+                       nd->signal_max = max;
+                       nd->signal_invalid = invalid;
+               }
+
+               index += 1;
+       }
+
+       if (nd->vendor == OFONO_VENDOR_TELIT)
+               g_at_result_iter_close_list(&iter);
+
+       if (nd->signal_index == 0)
+               goto error;
+
+       g_at_chat_send(nd->chat, "AT+CMER=3,0,0,1", NULL,
+                       NULL, NULL, NULL);
+       g_at_chat_register(nd->chat, "+CIEV:",
+                               ciev_notify, FALSE, netreg, NULL);
+       g_at_chat_register(nd->chat, "+CREG:",
+                               creg_notify, FALSE, netreg, NULL);
+
+       ofono_netreg_register(netreg);
+       return;
+
+error:
+       ofono_error("This driver is not setup with Signal Strength reporting"
+                       " via CIND indications, please write proper netreg"
+                       " handling for this device");
+
+       ofono_netreg_remove(netreg);
+}
+
+static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (!ok) {
+               ofono_error("Unable to initialize Network Registration");
+               ofono_netreg_remove(netreg);
+               return;
+       }
+
+       switch (nd->vendor) {
+       case OFONO_VENDOR_PHONESIM:
+               g_at_chat_register(nd->chat, "+CSQ:",
+                                       csq_notify, FALSE, netreg, NULL);
+               break;
+       case OFONO_VENDOR_CALYPSO:
+               g_at_chat_send(nd->chat, "AT%CSQ=1", none_prefix,
+                               NULL, NULL, NULL);
+               g_at_chat_register(nd->chat, "%CSQ:", calypso_csq_notify,
+                                       FALSE, netreg, NULL);
+               break;
+       case OFONO_VENDOR_OPTION_HSO:
+               g_at_chat_send(nd->chat, "AT_OSSYS=1", none_prefix,
+                               NULL, NULL, NULL);
+               g_at_chat_send(nd->chat, "AT_OSQI=1", none_prefix,
+                               NULL, NULL, NULL);
+               g_at_chat_register(nd->chat, "_OSIGQ:", option_osigq_notify,
+                                       FALSE, netreg, NULL);
+
+               g_at_chat_send(nd->chat, "AT_OSSYS?", none_prefix,
+                               NULL, NULL, NULL);
+               g_at_chat_send(nd->chat, "AT_OSQI?", none_prefix,
+                               NULL, NULL, NULL);
+
+               /* Register for network time update reports */
+               g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
+                                               NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_MBM:
+               /* Enable network registration updates */
+               g_at_chat_send(nd->chat, "AT*E2REG=1", none_prefix,
+                                               NULL, NULL, NULL);
+               g_at_chat_send(nd->chat, "AT*EREG=2", none_prefix,
+                                               NULL, NULL, NULL);
+               g_at_chat_send(nd->chat, "AT*EPSB=1", none_prefix,
+                                               NULL, NULL, NULL);
+
+               /* Register for network technology updates */
+               g_at_chat_send(nd->chat, "AT*ERINFO=1", none_prefix,
+                                               NULL, NULL, NULL);
+               g_at_chat_register(nd->chat, "*ERINFO:", mbm_erinfo_notify,
+                                               FALSE, netreg, NULL);
+
+               /* Register for network time update reports */
+               g_at_chat_register(nd->chat, "*ETZV:", mbm_etzv_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->chat, "AT*ETZR=2", none_prefix,
+                                               NULL, NULL, NULL);
+
+               g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
+                                       cind_support_cb, netreg, NULL);
+               return;
+       case OFONO_VENDOR_GOBI:
+               /*
+                * Gobi devices don't support unsolicited notifications
+                * of technology changes, but register a handle for
+                * CNTI so we get notified by any query.
+                */
+               g_at_chat_register(nd->chat, "*CNTI:", gobi_cnti_notify,
+                                       FALSE, netreg, NULL);
+               break;
+       case OFONO_VENDOR_NOVATEL:
+               /*
+                * Novatel doesn't support unsolicited notifications
+                * of technology changes, but register a handle for
+                * CNTI so we get notified by any query.
+                */
+               g_at_chat_register(nd->chat, "$CNTI:", nw_cnti_notify,
+                                       FALSE, netreg, NULL);
+               break;
+       case OFONO_VENDOR_HUAWEI:
+               /* Register for RSSI reports */
+               g_at_chat_register(nd->chat, "^RSSI:", huawei_rssi_notify,
+                                               FALSE, netreg, NULL);
+
+               /* Register for system mode reports */
+               g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify,
+                                               FALSE, netreg, NULL);
+
+               /* Register for network time reports */
+               g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify,
+                                               FALSE, netreg, NULL);
+               break;
+       case OFONO_VENDOR_IFX:
+               /* Register for specific signal strength reports */
+               g_at_chat_register(nd->chat, "+XCIEV:", ifx_xciev_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix,
+                                               NULL, NULL, NULL);
+
+               /* Register for home zone reports */
+               g_at_chat_register(nd->chat, "+XHOMEZR:", ifx_xhomezr_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->chat, "AT+XHOMEZR=1", none_prefix,
+                                               NULL, NULL, NULL);
+
+               /* Register for network time update reports */
+               g_at_chat_register(nd->chat, "+CTZV:", ifx_ctzv_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_register(nd->chat, "+CTZDST:", ifx_ctzdst_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
+                                               NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_ZTE:
+               /* Register for network time update reports */
+               g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
+                                               NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_NOKIA:
+       case OFONO_VENDOR_SAMSUNG:
+       case OFONO_VENDOR_SIMCOM:
+               /* Signal strength reporting via CIND is not supported */
+               break;
+       default:
+               g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
+                               cind_support_cb, netreg, NULL);
+               return;
+       }
+
+       g_at_chat_register(nd->chat, "+CREG:",
+                               creg_notify, FALSE, netreg, NULL);
+       ofono_netreg_register(netreg);
+}
+
+static void at_creg_test_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       gint range[2];
+       GAtResultIter iter;
+       int creg1 = 0;
+       int creg2 = 0;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CREG:"))
+               goto error;
+
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) {
+               if (1 >= range[0] && 1 <= range[1])
+                       creg1 = 1;
+               if (2 >= range[0] && 2 <= range[1])
+                       creg2 = 1;
+       }
+
+       g_at_result_iter_close_list(&iter);
+
+       if (creg2) {
+               g_at_chat_send(nd->chat, "AT+CREG=2", none_prefix,
+                               at_creg_set_cb, netreg, NULL);
+               return;
+       }
+
+       if (creg1) {
+               g_at_chat_send(nd->chat, "AT+CREG=1", none_prefix,
+                               at_creg_set_cb, netreg, NULL);
+               return;
+       }
+
+error:
+       ofono_error("Unable to initialize Network Registration");
+       ofono_netreg_remove(netreg);
+}
+
+static int at_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct netreg_data *nd;
+
+       nd = g_new0(struct netreg_data, 1);
+
+       nd->chat = g_at_chat_clone(chat);
+       nd->vendor = vendor;
+       nd->tech = -1;
+       nd->time.sec = -1;
+       nd->time.min = -1;
+       nd->time.hour = -1;
+       nd->time.mday = -1;
+       nd->time.mon = -1;
+       nd->time.year = -1;
+       nd->time.dst = 0;
+       nd->time.utcoff = 0;
+       ofono_netreg_set_data(netreg, nd);
+
+       g_at_chat_send(nd->chat, "AT+CREG=?", creg_prefix,
+                       at_creg_test_cb, netreg, NULL);
+
+       return 0;
+}
+
+static void at_netreg_remove(struct ofono_netreg *netreg)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (nd->nitz_timeout)
+               g_source_remove(nd->nitz_timeout);
+
+       ofono_netreg_set_data(netreg, NULL);
+
+       g_at_chat_unref(nd->chat);
+       g_free(nd);
+}
+
+static struct ofono_netreg_driver driver = {
+       .name                           = "atmodem",
+       .probe                          = at_netreg_probe,
+       .remove                         = at_netreg_remove,
+       .registration_status            = at_registration_status,
+       .current_operator               = at_current_operator,
+       .list_operators                 = at_list_operators,
+       .register_auto                  = at_register_auto,
+       .register_manual                = at_register_manual,
+       .strength                       = at_signal_strength,
+};
+
+void at_netreg_init(void)
+{
+       ofono_netreg_driver_register(&driver);
+}
+
+void at_netreg_exit(void)
+{
+       ofono_netreg_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/phonebook.c b/drivers/atmodem/phonebook.c
new file mode 100644 (file)
index 0000000..a43b8cc
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/phonebook.h>
+#include "util.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+#include "vendor.h"
+
+#define INDEX_INVALID -1
+
+#define CHARSET_UTF8 1
+#define CHARSET_UCS2 2
+#define CHARSET_IRA  4
+#define CHARSET_SUPPORT (CHARSET_UTF8 | CHARSET_UCS2)
+
+static const char *none_prefix[] = { NULL };
+static const char *cpbr_prefix[] = { "+CPBR:", NULL };
+static const char *cscs_prefix[] = { "+CSCS:", NULL };
+static const char *cpbs_prefix[] = { "+CPBS:", NULL };
+
+struct pb_data {
+       int index_min, index_max;
+       char *old_charset;
+       int supported;
+       GAtChat *chat;
+       unsigned int vendor;
+       guint poll_source;
+       guint poll_count;
+       guint ready_id;
+};
+
+static void warn_bad(void)
+{
+       ofono_warn("Name field conversion to UTF8 failed, this can indicate a"
+                       " problem with modem integration, as this field"
+                       " is required by 27.007.");
+}
+
+static gboolean parse_text(GAtResultIter *iter, char **str, int encoding)
+{
+       const char *string;
+       const guint8 *hex;
+       int len;
+       char *utf8;
+       /* charset_current is CHARSET_UCS2, CHARSET_IRA or CHARSET_UTF8 */
+       if (encoding == CHARSET_UCS2) {
+               /*
+                * Some devices omit the quotes, so use next_hexstring,
+                * which handles quoted or unquoted hex strings
+                */
+               if (g_at_result_iter_next_hexstring(iter, &hex, &len) == FALSE)
+                       return FALSE;
+
+               utf8 = g_convert((const gchar*) hex, len,
+                                       "UTF-8//TRANSLIT", "UCS-2BE",
+                                       NULL, NULL, NULL);
+
+               if (utf8) {
+                       *str = utf8;
+                       return TRUE;
+               }
+
+               return FALSE;
+       }
+
+       /*
+        * In the case of IRA charset, assume these are Latin1
+        * characters, same as in UTF8
+        */
+       if (g_at_result_iter_next_string(iter, &string)) {
+               *str = g_strdup(string);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static const char *best_charset(int supported)
+{
+       const char *charset = "Invalid";
+
+       if (supported & CHARSET_IRA)
+               charset = "IRA";
+
+       if (supported & CHARSET_UCS2)
+               charset = "UCS2";
+
+       if (supported & CHARSET_UTF8)
+               charset = "UTF-8";
+
+       return charset;
+}
+
+static void at_cpbr_notify(GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       GAtResultIter iter;
+       int current;
+
+       if (pbd->supported & CHARSET_IRA)
+               current = CHARSET_IRA;
+
+       if (pbd->supported & CHARSET_UCS2)
+               current = CHARSET_UCS2;
+
+       if (pbd->supported & CHARSET_UTF8)
+               current = CHARSET_UTF8;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+CPBR:")) {
+               int index;
+               const char *number;
+               int type;
+               char *text;
+               int hidden = -1;
+               char *group = NULL;
+               const char *adnumber = NULL;
+               int adtype = -1;
+               char *secondtext = NULL;
+               char *email = NULL;
+               char *sip_uri = NULL;
+               char *tel_uri = NULL;
+
+               if (!g_at_result_iter_next_number(&iter, &index))
+                       continue;
+
+               if (!g_at_result_iter_next_string(&iter, &number))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &type))
+                       continue;
+
+               if (!parse_text(&iter, &text, current)) {
+                       warn_bad();
+                       continue;
+               }
+
+               g_at_result_iter_next_number_default(&iter, 0, &hidden);
+               parse_text(&iter, &group, current);
+               g_at_result_iter_next_string(&iter, &adnumber);
+               g_at_result_iter_next_number_default(&iter, 0, &adtype);
+               parse_text(&iter, &secondtext, current);
+               parse_text(&iter, &email, current);
+               parse_text(&iter, &sip_uri, current);
+               parse_text(&iter, &tel_uri, current);
+
+               ofono_phonebook_entry(pb, index, number, type,
+                       text, hidden, group, adnumber,
+                       adtype, secondtext, email,
+                       sip_uri, tel_uri);
+
+               g_free(text);
+               g_free(group);
+               g_free(secondtext);
+               g_free(email);
+               g_free(sip_uri);
+               g_free(tel_uri);
+       }
+}
+
+static void export_failed(struct cb_data *cbd)
+{
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       ofono_phonebook_cb_t cb = cbd->cb;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+
+       if (pbd->old_charset) {
+               g_free(pbd->old_charset);
+               pbd->old_charset = NULL;
+       }
+}
+
+static void at_read_entries_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       ofono_phonebook_cb_t cb = cbd->cb;
+       const char *charset;
+       struct ofono_error error;
+       char buf[32];
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+       g_free(cbd);
+
+       charset = best_charset(pbd->supported);
+
+       if (strcmp(pbd->old_charset, charset)) {
+               snprintf(buf, sizeof(buf), "AT+CSCS=\"%s\"", pbd->old_charset);
+               g_at_chat_send(pbd->chat, buf, none_prefix, NULL, NULL, NULL);
+       }
+
+       g_free(pbd->old_charset);
+       pbd->old_charset = NULL;
+}
+
+static void at_read_entries(struct cb_data *cbd)
+{
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       char buf[32];
+
+       snprintf(buf, sizeof(buf), "AT+CPBR=%d,%d",
+                       pbd->index_min, pbd->index_max);
+       if (g_at_chat_send_listing(pbd->chat, buf, cpbr_prefix,
+                                       at_cpbr_notify, at_read_entries_cb,
+                                       cbd, NULL) > 0)
+               return;
+
+       /* If we get here, then most likely connection to the modem dropped
+        * and we can't really restore the charset anyway
+        */
+       export_failed(cbd);
+}
+
+static void at_set_charset_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+
+       if (!ok) {
+               export_failed(cbd);
+               return;
+       }
+
+       at_read_entries(cbd);
+}
+
+static void at_read_charset_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       GAtResultIter iter;
+       const char *charset;
+       char buf[32];
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSCS:"))
+               goto error;
+
+       g_at_result_iter_next_string(&iter, &charset);
+
+       pbd->old_charset = g_strdup(charset);
+
+       charset = best_charset(pbd->supported);
+
+       if (!strcmp(pbd->old_charset, charset)) {
+               at_read_entries(cbd);
+               return;
+       }
+
+       snprintf(buf, sizeof(buf), "AT+CSCS=\"%s\"", charset);
+       if (g_at_chat_send(pbd->chat, buf, none_prefix,
+                               at_set_charset_cb, cbd, NULL) > 0)
+               return;
+
+error:
+       export_failed(cbd);
+}
+
+static void at_list_indices_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       GAtResultIter iter;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+       if (!g_at_result_iter_next(&iter, "+CPBR:"))
+               goto error;
+
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       /* Retrieve index_min and index_max from indices
+        * which seems like "(1-150),32,16"
+        */
+       if (!g_at_result_iter_next_range(&iter, &pbd->index_min,
+                                               &pbd->index_max))
+               goto error;
+
+       if (!g_at_result_iter_close_list(&iter))
+               goto error;
+
+       if (g_at_chat_send(pbd->chat, "AT+CSCS?", cscs_prefix,
+                               at_read_charset_cb, cbd, NULL) > 0)
+               return;
+
+error:
+       export_failed(cbd);
+}
+
+static void at_select_storage_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+       if (!ok)
+               goto error;
+
+       if (g_at_chat_send(pbd->chat, "AT+CPBR=?", cpbr_prefix,
+                               at_list_indices_cb, cbd, NULL) > 0)
+               return;
+
+error:
+       export_failed(cbd);
+}
+
+static void at_export_entries(struct ofono_phonebook *pb, const char *storage,
+                               ofono_phonebook_cb_t cb, void *data)
+{
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[32];
+
+       cbd->user = pb;
+
+       snprintf(buf, sizeof(buf), "AT+CPBS=\"%s\"", storage);
+       if (g_at_chat_send(pbd->chat, buf, none_prefix,
+                               at_select_storage_cb, cbd, NULL) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void phonebook_not_supported(struct ofono_phonebook *pb)
+{
+       ofono_error("Phonebook not supported by this modem.  If this is in "
+                       "error please submit patches to support this hardware");
+
+       ofono_phonebook_remove(pb);
+}
+
+static void at_list_storages_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data);
+
+static gboolean cpbs_support_check(gpointer user_data)
+{
+       struct ofono_phonebook *pb = user_data;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+       pbd->poll_source = 0;
+
+       if (g_at_chat_send(pbd->chat, "AT+CPBS=?", cpbs_prefix,
+                               at_list_storages_cb, pb, NULL) > 0)
+               return FALSE;
+
+       phonebook_not_supported(pb);
+
+       return FALSE;
+}
+
+static void ifx_pbready_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_phonebook *pb = user_data;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+       g_at_chat_unregister(pbd->chat, pbd->ready_id);
+       pbd->ready_id = 0;
+
+       cpbs_support_check(pb);
+}
+
+static void at_list_storages_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_phonebook *pb = user_data;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       struct ofono_error error;
+       gboolean sm_supported = FALSE;
+       gboolean me_supported = FALSE;
+       gboolean in_list = FALSE;
+       GAtResultIter iter;
+       const char *storage;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       switch (error.type) {
+       case OFONO_ERROR_TYPE_NO_ERROR:
+               break;
+       case OFONO_ERROR_TYPE_CME:
+               /* Check for SIM busy - try again later */
+               if (error.error == 14) {
+                       if (pbd->poll_count++ < 12) {
+                               pbd->poll_source = g_timeout_add_seconds(5,
+                                               cpbs_support_check, pb);
+                               return;
+                       }
+               }
+               /* fall through */
+       default:
+               goto error;
+       }
+
+       g_at_result_iter_init(&iter, result);
+       if (!g_at_result_iter_next(&iter, "+CPBS:"))
+               goto error;
+
+       /* Some modems don't report CPBS in a proper list */
+       if (g_at_result_iter_open_list(&iter))
+               in_list = TRUE;
+
+       while (g_at_result_iter_next_string(&iter, &storage)) {
+               if (!strcmp(storage, "ME"))
+                       me_supported = TRUE;
+               else if (!strcmp(storage, "SM"))
+                       sm_supported = TRUE;
+       }
+
+       if (in_list && !g_at_result_iter_close_list(&iter))
+               goto vendor;
+
+       if (!me_supported && !sm_supported)
+               goto vendor;
+
+       ofono_phonebook_register(pb);
+       return;
+
+vendor:
+       switch (pbd->vendor) {
+       case OFONO_VENDOR_IFX:
+               pbd->ready_id = g_at_chat_register(pbd->chat, "+PBREADY",
+                                       ifx_pbready_notify, FALSE, pb, NULL);
+               return;
+       }
+
+error:
+       phonebook_not_supported(pb);
+}
+
+static void at_list_charsets_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_phonebook *pb = user_data;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       gboolean in_list = FALSE;
+       GAtResultIter iter;
+       const char *charset;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+       if (!g_at_result_iter_next(&iter, "+CSCS:"))
+               goto error;
+
+       /* Some modems don't report CSCS in a proper list */
+       if (g_at_result_iter_open_list(&iter))
+               in_list = TRUE;
+
+       while (g_at_result_iter_next_string(&iter, &charset)) {
+               if (!strcmp(charset, "UTF-8"))
+                       pbd->supported |= CHARSET_UTF8;
+               else if (!strcmp(charset, "UCS2"))
+                       pbd->supported |= CHARSET_UCS2;
+               else if (!strcmp(charset, "IRA"))
+                       pbd->supported |= CHARSET_IRA;
+       }
+
+       if (in_list && !g_at_result_iter_close_list(&iter))
+               goto error;
+
+       if (!(pbd->supported & CHARSET_SUPPORT)) {
+               /* Some modems, like the Google G1, do not support UCS2 or UTF8
+                * Such modems are effectively junk, but we can still get some
+                * useful information out of them by using IRA charset, which
+                * is essentially Latin1.  Still, all bets are off if a SIM
+                * with UCS2 encoded entries is present.
+                */
+               if (pbd->supported & CHARSET_IRA) {
+                       ofono_error("This modem does not support UCS2 or UTF8 "
+                                       "character sets.  This means no i18n "
+                                       "phonebook is possible on this modem,"
+                                       " if this is in error, submit patches "
+                                       "to properly support this hardware");
+               } else {
+                       goto error;
+               }
+       }
+
+       pbd->poll_count = 0;
+
+       cpbs_support_check(pb);
+       return;
+
+error:
+       phonebook_not_supported(pb);
+}
+
+static void at_list_charsets(struct ofono_phonebook *pb)
+{
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+       if (g_at_chat_send(pbd->chat, "AT+CSCS=?", cscs_prefix,
+                               at_list_charsets_cb, pb, NULL) > 0)
+               return;
+
+       phonebook_not_supported(pb);
+}
+
+static int at_phonebook_probe(struct ofono_phonebook *pb, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct pb_data *pbd;
+
+       pbd = g_try_new0(struct pb_data, 1);
+       if (pbd == NULL)
+               return -ENOMEM;
+
+       pbd->chat = g_at_chat_clone(chat);
+       pbd->vendor = vendor;
+
+       ofono_phonebook_set_data(pb, pbd);
+
+       at_list_charsets(pb);
+
+       return 0;
+}
+
+static void at_phonebook_remove(struct ofono_phonebook *pb)
+{
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+       if (pbd->poll_source > 0)
+               g_source_remove(pbd->poll_source);
+
+       if (pbd->old_charset)
+               g_free(pbd->old_charset);
+
+       ofono_phonebook_set_data(pb, NULL);
+
+       g_at_chat_unref(pbd->chat);
+       g_free(pbd);
+}
+
+static struct ofono_phonebook_driver driver = {
+       .name           = "atmodem",
+       .probe          = at_phonebook_probe,
+       .remove         = at_phonebook_remove,
+       .export_entries = at_export_entries
+};
+
+void at_phonebook_init(void)
+{
+       ofono_phonebook_driver_register(&driver);
+}
+
+void at_phonebook_exit(void)
+{
+       ofono_phonebook_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/sim-auth.c b/drivers/atmodem/sim-auth.c
new file mode 100644 (file)
index 0000000..9ce810f
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <glib.h>
+
+#include <ofono/modem.h>
+#include <ofono/sim-auth.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "simutil.h"
+#include "vendor.h"
+
+#include "atmodem.h"
+
+struct sim_auth_data {
+       GAtChat *chat;
+       unsigned int vendor;
+};
+
+static const char *cuad_prefix[] = { "+CUAD:", NULL };
+
+static void at_discover_apps_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sim_list_apps_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       const unsigned char *dataobj;
+       gint linelen;
+       unsigned char *buffer;
+       int len;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, 0, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       len = 0;
+       while (g_at_result_iter_next(&iter, "+CUAD:")) {
+               if (!g_at_result_iter_next_hexstring(&iter, NULL, &linelen))
+                       goto error;
+
+               len += linelen;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       buffer = g_malloc(len);
+       len = 0;
+
+       while (g_at_result_iter_next(&iter, "+CUAD:")) {
+               g_at_result_iter_next_hexstring(&iter, &dataobj, &linelen);
+               memcpy(buffer + len, dataobj, linelen);
+               len += linelen;
+       }
+
+       cb(&error, buffer, len, cbd->data);
+
+       g_free(buffer);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
+}
+
+static void at_discover_apps(struct ofono_sim_auth *sa,
+                               ofono_sim_list_apps_cb_t cb,
+                               void *data)
+{
+       struct sim_auth_data *sad = ofono_sim_auth_get_data(sa);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(sad->chat, "AT+CUAD", cuad_prefix,
+                                       at_discover_apps_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static gboolean at_sim_auth_register(gpointer user)
+{
+       struct ofono_sim_auth *sa = user;
+
+       ofono_sim_auth_register(sa);
+
+       return FALSE;
+}
+
+static int at_sim_auth_probe(struct ofono_sim_auth *sa, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct sim_auth_data *sad;
+
+       sad = g_new0(struct sim_auth_data, 1);
+       sad->chat = g_at_chat_clone(chat);
+       sad->vendor = vendor;
+
+       ofono_sim_auth_set_data(sa, sad);
+       g_idle_add(at_sim_auth_register, sa);
+
+       return 0;
+}
+
+static void at_sim_auth_remove(struct ofono_sim_auth *sa)
+{
+       struct sim_auth_data *sad = ofono_sim_auth_get_data(sa);
+
+       ofono_sim_auth_set_data(sa, NULL);
+
+       g_at_chat_unref(sad->chat);
+       g_free(sad);
+}
+
+static struct ofono_sim_auth_driver driver = {
+       .name           = "atmodem",
+       .probe          = at_sim_auth_probe,
+       .remove         = at_sim_auth_remove,
+       .list_apps      = at_discover_apps,
+};
+
+void at_sim_auth_init(void)
+{
+       ofono_sim_auth_driver_register(&driver);
+}
+
+void at_sim_auth_exit(void)
+{
+       ofono_sim_auth_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/sim.c b/drivers/atmodem/sim.c
new file mode 100644 (file)
index 0000000..8edd582
--- /dev/null
@@ -0,0 +1,1296 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sim.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "simutil.h"
+#include "vendor.h"
+
+#include "atmodem.h"
+
+#define EF_STATUS_INVALIDATED 0
+#define EF_STATUS_VALID 1
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+struct sim_data {
+       GAtChat *chat;
+       unsigned int vendor;
+       guint ready_id;
+};
+
+static const char *crsm_prefix[] = { "+CRSM:", NULL };
+static const char *cpin_prefix[] = { "+CPIN:", NULL };
+static const char *clck_prefix[] = { "+CLCK:", NULL };
+static const char *huawei_cpin_prefix[] = { "^CPIN:", NULL };
+static const char *xpincnt_prefix[] = { "+XPINCNT:", NULL };
+static const char *zpinpuk_prefix[] = { "+ZPINPUK:", NULL };
+static const char *oercn_prefix[] = { "_OERCN:", NULL };
+static const char *cpinr_prefixes[] = { "+CPINR:", "+CPINRE:", NULL };
+static const char *epin_prefix[] = { "*EPIN:", NULL };
+static const char *none_prefix[] = { NULL };
+
+static void at_crsm_info_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sim_file_info_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       const guint8 *response;
+       gint sw1, sw2, len;
+       int flen, rlen;
+       int str;
+       unsigned char access[3];
+       unsigned char file_status;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CRSM:"))
+               goto error;
+
+       g_at_result_iter_next_number(&iter, &sw1);
+       g_at_result_iter_next_number(&iter, &sw2);
+
+       if (!g_at_result_iter_next_hexstring(&iter, &response, &len) ||
+                       (sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92) ||
+                       (sw1 == 0x90 && sw2 != 0x00)) {
+               memset(&error, 0, sizeof(error));
+
+               error.type = OFONO_ERROR_TYPE_SIM;
+               error.error = (sw1 << 8) | sw2;
+
+               cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data);
+               return;
+       }
+
+       DBG("crsm_info_cb: %02x, %02x, %i", sw1, sw2, len);
+
+       if (response[0] == 0x62) {
+               ok = sim_parse_3g_get_response(response, len, &flen, &rlen,
+                                               &str, access, NULL);
+
+               file_status = EF_STATUS_VALID;
+       }
+       else
+               ok = sim_parse_2g_get_response(response, len, &flen, &rlen,
+                                               &str, access, &file_status);
+
+       if (!ok)
+               goto error;
+
+       cb(&error, flen, str, rlen, access, file_status, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
+                               EF_STATUS_INVALIDATED, cbd->data);
+}
+
+static void at_sim_read_info(struct ofono_sim *sim, int fileid,
+                                       ofono_sim_file_info_cb_t cb,
+                                       void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd;
+       char buf[64];
+
+       if (sd->vendor == OFONO_VENDOR_OPTION_HSO) {
+               unsigned char access[3] = { 0x00, 0x00, 0x00 };
+
+               if (fileid == SIM_EFAD_FILEID) {
+                       CALLBACK_WITH_SUCCESS(cb, 4, 0, 0, access,
+                                               EF_STATUS_VALID, data);
+                       return;
+               }
+       }
+
+       cbd = cb_data_new(cb, data);
+
+       snprintf(buf, sizeof(buf), "AT+CRSM=192,%i", fileid);
+
+       switch (sd->vendor) {
+       case OFONO_VENDOR_ZTE:
+       case OFONO_VENDOR_HUAWEI:
+       case OFONO_VENDOR_SIERRA:
+       case OFONO_VENDOR_SPEEDUP:
+       case OFONO_VENDOR_QUALCOMM_MSM:
+               strcat(buf, ",0,0,255"); /* Maximum possible length */
+               break;
+       }
+
+       if (g_at_chat_send(sd->chat, buf, crsm_prefix,
+                               at_crsm_info_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
+                               EF_STATUS_INVALIDATED, data);
+}
+
+static void at_crsm_read_cb(gboolean ok, GAtResult *result,
+               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sim_read_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       const guint8 *response;
+       gint sw1, sw2, len;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, 0, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CRSM:")) {
+               CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_next_number(&iter, &sw1);
+       g_at_result_iter_next_number(&iter, &sw2);
+
+       if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
+                       (sw1 == 0x90 && sw2 != 0x00)) {
+               memset(&error, 0, sizeof(error));
+
+               error.type = OFONO_ERROR_TYPE_SIM;
+               error.error = (sw1 << 8) | sw2;
+
+               cb(&error, NULL, 0, cbd->data);
+               return;
+       }
+
+       if (!g_at_result_iter_next_hexstring(&iter, &response, &len)) {
+               CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
+               return;
+       }
+
+       DBG("crsm_read_cb: %02x, %02x, %d", sw1, sw2, len);
+
+       cb(&error, response, len, cbd->data);
+}
+
+static void at_sim_read_binary(struct ofono_sim *sim, int fileid,
+                                       int start, int length,
+                                       ofono_sim_read_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CRSM=176,%i,%i,%i,%i", fileid,
+                       start >> 8, start & 0xff, length);
+
+       if (g_at_chat_send(sd->chat, buf, crsm_prefix,
+                               at_crsm_read_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void at_sim_read_record(struct ofono_sim *sim, int fileid,
+                                       int record, int length,
+                                       ofono_sim_read_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CRSM=178,%i,%i,4,%i", fileid,
+                       record, length);
+
+       if (g_at_chat_send(sd->chat, buf, crsm_prefix,
+                               at_crsm_read_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void at_crsm_update_cb(gboolean ok, GAtResult *result,
+               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sim_write_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       gint sw1, sw2;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CRSM:")) {
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_next_number(&iter, &sw1);
+       g_at_result_iter_next_number(&iter, &sw2);
+
+       if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
+                       (sw1 == 0x90 && sw2 != 0x00)) {
+               memset(&error, 0, sizeof(error));
+
+               error.type = OFONO_ERROR_TYPE_SIM;
+               error.error = (sw1 << 8) | sw2;
+       }
+
+       DBG("crsm_update_cb: %02x, %02x", sw1, sw2);
+
+       cb(&error, cbd->data);
+}
+
+static void at_sim_update_binary(struct ofono_sim *sim, int fileid,
+                                       int start, int length,
+                                       const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, 36 + length * 2);
+       int len, ret;
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT+CRSM=214,%i,%i,%i,%i,", fileid,
+                       start >> 8, start & 0xff, length);
+
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *value++);
+
+       ret = g_at_chat_send(sd->chat, buf, crsm_prefix,
+                               at_crsm_update_cb, cbd, g_free);
+
+       g_free(buf);
+
+       if (ret > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_sim_update_record(struct ofono_sim *sim, int fileid,
+                                       int record, int length,
+                                       const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf;
+       int len, ret;
+       int size = 36 + length * 2;
+
+       if (sd->vendor == OFONO_VENDOR_MBM)
+               size += 2; /*Add quotes*/
+
+       buf = g_try_new(char, size);
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT+CRSM=220,%i,%i,4,%i,", fileid,
+                       record, length);
+
+       if (sd->vendor == OFONO_VENDOR_MBM)
+               len += sprintf(buf + len, "\"");
+
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *value++);
+
+       if (sd->vendor == OFONO_VENDOR_MBM)
+               sprintf(buf + len, "\"");
+
+       ret = g_at_chat_send(sd->chat, buf, crsm_prefix,
+                               at_crsm_update_cb, cbd, g_free);
+
+       g_free(buf);
+
+       if (ret > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_sim_update_cyclic(struct ofono_sim *sim, int fileid,
+                                       int length, const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf;
+       int len, ret;
+       int size = 36 + length * 2;
+
+       if (sd->vendor == OFONO_VENDOR_MBM)
+               size += 2; /* Add quotes */
+
+       buf = g_try_new(char, size);
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT+CRSM=220,%i,0,3,%i,", fileid, length);
+
+       if (sd->vendor == OFONO_VENDOR_MBM)
+               len += sprintf(buf + len, "\"");
+
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *value++);
+
+       if (sd->vendor == OFONO_VENDOR_MBM)
+               sprintf(buf + len, "\"");
+
+       ret = g_at_chat_send(sd->chat, buf, crsm_prefix,
+                               at_crsm_update_cb, cbd, g_free);
+
+       g_free(buf);
+
+       if (ret > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_cimi_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sim_imsi_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       const char *imsi;
+       int i;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       for (i = 0; i < g_at_result_num_response_lines(result); i++)
+               g_at_result_iter_next(&iter, NULL);
+
+       imsi = g_at_result_iter_raw_line(&iter);
+
+       DBG("cimi_cb: %s", imsi);
+
+       cb(&error, imsi, cbd->data);
+}
+
+static void at_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb,
+                       void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(sd->chat, "AT+CIMI", NULL,
+                               at_cimi_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static struct {
+       enum ofono_sim_password_type type;
+       const char *name;
+} const at_sim_name[] = {
+       { OFONO_SIM_PASSWORD_NONE,              "READY"         },
+       { OFONO_SIM_PASSWORD_SIM_PIN,           "SIM PIN"       },
+       { OFONO_SIM_PASSWORD_SIM_PUK,           "SIM PUK"       },
+       { OFONO_SIM_PASSWORD_PHSIM_PIN,         "PH-SIM PIN"    },
+       { OFONO_SIM_PASSWORD_PHFSIM_PIN,        "PH-FSIM PIN"   },
+       { OFONO_SIM_PASSWORD_PHFSIM_PUK,        "PH-FSIM PUK"   },
+       { OFONO_SIM_PASSWORD_SIM_PIN2,          "SIM PIN2"      },
+       { OFONO_SIM_PASSWORD_SIM_PUK2,          "SIM PUK2"      },
+       { OFONO_SIM_PASSWORD_PHNET_PIN,         "PH-NET PIN"    },
+       { OFONO_SIM_PASSWORD_PHNET_PUK,         "PH-NET PUK"    },
+       { OFONO_SIM_PASSWORD_PHNETSUB_PIN,      "PH-NETSUB PIN" },
+       { OFONO_SIM_PASSWORD_PHNETSUB_PUK,      "PH-NETSUB PUK" },
+       { OFONO_SIM_PASSWORD_PHSP_PIN,          "PH-SP PIN"     },
+       { OFONO_SIM_PASSWORD_PHSP_PUK,          "PH-SP PUK"     },
+       { OFONO_SIM_PASSWORD_PHCORP_PIN,        "PH-CORP PIN"   },
+       { OFONO_SIM_PASSWORD_PHCORP_PUK,        "PH-CORP PUK"   },
+};
+
+#define BUILD_PIN_RETRIES_ARRAY(passwd_types, passwd_types_cnt, retry) \
+       for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)                \
+               retry[i] = -1;                                          \
+                                                                       \
+       for (i = 0; i < passwd_types_cnt; i++) {                        \
+               int val;                                                \
+                                                                       \
+               if (!g_at_result_iter_next_number(&iter, &val))         \
+                       goto error;                                     \
+                                                                       \
+               retry[passwd_types[i]] = val;                           \
+                                                                       \
+               DBG("retry counter id=%d, val=%d", passwd_types[i],     \
+                                       retry[passwd_types[i]]);        \
+       }                                                               \
+
+static void huawei_cpin_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       const char *final = g_at_result_final_response(result);
+       GAtResultIter iter;
+       struct ofono_error error;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       size_t i;
+       static enum ofono_sim_password_type password_types[] = {
+               OFONO_SIM_PASSWORD_SIM_PUK,
+               OFONO_SIM_PASSWORD_SIM_PIN,
+               OFONO_SIM_PASSWORD_SIM_PUK2,
+               OFONO_SIM_PASSWORD_SIM_PIN2,
+       };
+
+       decode_at_error(&error, final);
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^CPIN:"))
+               goto error;
+
+       /* Skip status since we are not interested in this */
+       if (!g_at_result_iter_skip_next(&iter))
+               goto error;
+
+       /* Skip "overall counter" since we'll grab each one individually */
+       if (!g_at_result_iter_skip_next(&iter))
+               goto error;
+
+       BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
+                               retries);
+
+       cb(&error, retries, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void zpinpuk_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       const char *final = g_at_result_final_response(result);
+       GAtResultIter iter;
+       struct ofono_error error;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       size_t i;
+       static enum ofono_sim_password_type password_types[] = {
+               OFONO_SIM_PASSWORD_SIM_PIN,
+               OFONO_SIM_PASSWORD_SIM_PUK,
+       };
+
+
+       decode_at_error(&error, final);
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+ZPINPUK:"))
+               goto error;
+
+       BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
+                               retries);
+
+       cb(&error, retries, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void xpincnt_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       const char *final = g_at_result_final_response(result);
+       GAtResultIter iter;
+       struct ofono_error error;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       size_t i;
+       static enum ofono_sim_password_type password_types[] = {
+               OFONO_SIM_PASSWORD_SIM_PIN,
+               OFONO_SIM_PASSWORD_SIM_PIN2,
+               OFONO_SIM_PASSWORD_SIM_PUK,
+               OFONO_SIM_PASSWORD_SIM_PUK2,
+       };
+
+       decode_at_error(&error, final);
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XPINCNT:"))
+               goto error;
+
+       BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
+                               retries);
+
+       cb(&error, retries, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void oercn_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       const char *final = g_at_result_final_response(result);
+       GAtResultIter iter;
+       struct ofono_error error;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       size_t i;
+       static enum ofono_sim_password_type password_types[] = {
+               OFONO_SIM_PASSWORD_SIM_PIN,
+               OFONO_SIM_PASSWORD_SIM_PUK,
+       };
+
+       decode_at_error(&error, final);
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "_OERCN:"))
+               goto error;
+
+       BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
+                               retries);
+
+       cb(&error, retries, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void cpnnum_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       const char *final = g_at_result_final_response(result);
+       GAtResultIter iter;
+       struct ofono_error error;
+       const char *line;
+       int num;
+       char **entries;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       size_t i;
+
+       decode_at_error(&error, final);
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       for (num = 0; num < g_at_result_num_response_lines(result); num++)
+               g_at_result_iter_next(&iter, NULL);
+
+       line = g_at_result_iter_raw_line(&iter);
+
+       DBG("%s", line);
+
+       for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
+               retries[i] = -1;
+
+       entries = g_strsplit(line, "; ", -1);
+
+       for (num = 0; entries[num]; num++) {
+               int retry;
+
+               if (strlen(entries[num]) < 5)
+                       continue;
+
+               retry = strtol(entries[num] + 5, NULL, 10);
+               if (retry == 0 && errno == EINVAL)
+                       continue;
+
+               if (g_str_has_prefix(entries[num], "PIN1=") == TRUE)
+                       retries[OFONO_SIM_PASSWORD_SIM_PIN] = retry;
+               else if (g_str_has_prefix(entries[num], "PUK1=") == TRUE)
+                       retries[OFONO_SIM_PASSWORD_SIM_PUK] = retry;
+               else if (g_str_has_prefix(entries[num], "PIN2=") == TRUE)
+                       retries[OFONO_SIM_PASSWORD_SIM_PIN2] = retry;
+               else if (g_str_has_prefix(entries[num], "PUK2=") == TRUE)
+                       retries[OFONO_SIM_PASSWORD_SIM_PUK2] = retry;
+       }
+
+       g_strfreev(entries);
+
+       cb(&error, retries, cbd->data);
+}
+
+static void at_epin_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       const char *final = g_at_result_final_response(result);
+       GAtResultIter iter;
+       struct ofono_error error;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       size_t i;
+       static enum ofono_sim_password_type password_types[] = {
+               OFONO_SIM_PASSWORD_SIM_PIN,
+               OFONO_SIM_PASSWORD_SIM_PUK,
+               OFONO_SIM_PASSWORD_SIM_PIN2,
+               OFONO_SIM_PASSWORD_SIM_PUK2,
+       };
+
+       decode_at_error(&error, final);
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "*EPIN:"))
+               goto error;
+
+       BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
+                               retries);
+
+       cb(&error, retries, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void at_cpinr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       struct ofono_error error;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       size_t len = sizeof(at_sim_name) / sizeof(*at_sim_name);
+       size_t i;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
+               retries[i] = -1;
+
+       g_at_result_iter_init(&iter, result);
+
+       /* Ignore +CPINRE results... */
+       while (g_at_result_iter_next(&iter, "+CPINR:")) {
+               const char *name;
+               int val;
+
+               if (!g_at_result_iter_next_unquoted_string(&iter, &name))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &val))
+                       continue;
+
+               for (i = 1; i < len; i++) {
+                       if (!strcmp(name, at_sim_name[i].name)) {
+                               retries[i] = val;
+                               break;
+                       }
+               }
+       }
+
+       cb(&error, retries, cbd->data);
+}
+
+static void at_pin_retries_query(struct ofono_sim *sim,
+                                       ofono_sim_pin_retries_cb_t cb,
+                                       void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       DBG("");
+
+       switch (sd->vendor) {
+       case OFONO_VENDOR_IFX:
+               if (g_at_chat_send(sd->chat, "AT+XPINCNT", xpincnt_prefix,
+                                       xpincnt_cb, cbd, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_SPEEDUP:
+               if (g_at_chat_send(sd->chat, "AT+CPNNUM", NULL,
+                                       cpnnum_cb, cbd, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_OPTION_HSO:
+               if (g_at_chat_send(sd->chat, "AT_OERCN?", oercn_prefix,
+                                       oercn_cb, cbd, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_HUAWEI:
+               if (g_at_chat_send(sd->chat, "AT^CPIN?", huawei_cpin_prefix,
+                                       huawei_cpin_cb, cbd, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_ZTE:
+               if (g_at_chat_send(sd->chat, "AT+ZPINPUK=?", zpinpuk_prefix,
+                                       zpinpuk_cb, cbd, g_free) > 0)
+                       return;
+               break;
+       case OFONO_VENDOR_MBM:
+               if (g_at_chat_send(sd->chat, "AT*EPIN?", epin_prefix,
+                                       at_epin_cb, cbd, g_free) > 0)
+                       return;
+               break;
+       default:
+               if (g_at_chat_send(sd->chat, "AT+CPINR", cpinr_prefixes,
+                                       at_cpinr_cb, cbd, g_free) > 0)
+                       return;
+               break;
+       }
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void at_cpin_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct sim_data *sd = ofono_sim_get_data(cbd->user);
+       GAtResultIter iter;
+       ofono_sim_passwd_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       const char *pin_required;
+       int pin_type = OFONO_SIM_PASSWORD_INVALID;
+       int i;
+       int len = sizeof(at_sim_name) / sizeof(*at_sim_name);
+       const char *final = g_at_result_final_response(result);
+
+       if (sd->vendor == OFONO_VENDOR_WAVECOM && ok && strlen(final) > 7)
+               decode_at_error(&error, "OK");
+       else
+               decode_at_error(&error, final);
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       if (sd->vendor == OFONO_VENDOR_WAVECOM) {
+               /* +CPIN: <pin> */
+               pin_required = final + 7;
+       } else {
+               g_at_result_iter_init(&iter, result);
+
+               if (!g_at_result_iter_next(&iter, "+CPIN:")) {
+                       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+                       return;
+               }
+
+               g_at_result_iter_next_unquoted_string(&iter, &pin_required);
+       }
+
+       for (i = 0; i < len; i++) {
+               if (strcmp(pin_required, at_sim_name[i].name))
+                       continue;
+
+               pin_type = at_sim_name[i].type;
+               break;
+       }
+
+       if (pin_type == OFONO_SIM_PASSWORD_INVALID) {
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       DBG("crsm_pin_cb: %s", pin_required);
+
+       cb(&error, pin_type, cbd->data);
+}
+
+static void at_pin_query(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb,
+                       void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = sim;
+
+       if (g_at_chat_send(sd->chat, "AT+CPIN?", cpin_prefix,
+                               at_cpin_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void at_xsim_notify(GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct sim_data *sd = cbd->user;
+       ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+       struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
+       GAtResultIter iter;
+       int state;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XSIM:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &state))
+               return;
+
+       switch (state) {
+       case 3: /* PIN verified – Ready */
+       case 7: /* ready for attach (+COPS) */
+               break;
+       default:
+               return;
+       }
+
+       cb(&error, cbd->data);
+
+       g_at_chat_unregister(sd->chat, sd->ready_id);
+       sd->ready_id = 0;
+}
+
+static void at_epev_notify(GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct sim_data *sd = cbd->user;
+       ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+       struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
+
+       cb(&error, cbd->data);
+
+       g_at_chat_unregister(sd->chat, sd->ready_id);
+       sd->ready_id = 0;
+}
+
+static void at_pin_send_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct sim_data *sd = cbd->user;
+       ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto done;
+
+       switch (sd->vendor) {
+       case OFONO_VENDOR_IFX:
+               /*
+                * On the IFX modem, AT+CPIN? can return READY too
+                * early and so use +XSIM notification to detect
+                * the ready state of the SIM.
+                */
+               sd->ready_id = g_at_chat_register(sd->chat, "+XSIM",
+                                                       at_xsim_notify,
+                                                       FALSE, cbd, g_free);
+               return;
+       case OFONO_VENDOR_MBM:
+               /*
+                * On the MBM modem, AT+CPIN? keeps returning SIM PIN
+                * for a moment after successful AT+CPIN="..", but then
+                * sends *EPEV when that changes.
+                */
+               sd->ready_id = g_at_chat_register(sd->chat, "*EPEV",
+                                                       at_epev_notify,
+                                                       FALSE, cbd, g_free);
+               return;
+       }
+
+done:
+       cb(&error, cbd->data);
+
+       g_free(cbd);
+}
+
+static void at_pin_send(struct ofono_sim *sim, const char *passwd,
+                       ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+       int ret;
+
+       cbd->user = sd;
+
+       snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\"", passwd);
+
+       ret = g_at_chat_send(sd->chat, buf, none_prefix,
+                               at_pin_send_cb, cbd, NULL);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (ret > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_pin_send_puk(struct ofono_sim *sim, const char *puk,
+                               const char *passwd,
+                               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+       int ret;
+
+       cbd->user = sd;
+
+       snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\",\"%s\"", puk, passwd);
+
+       ret = g_at_chat_send(sd->chat, buf, none_prefix,
+                               at_pin_send_cb, cbd, NULL);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (ret > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_lock_unlock_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static const char *const at_clck_cpwd_fac[] = {
+       [OFONO_SIM_PASSWORD_SIM_PIN] = "SC",
+       [OFONO_SIM_PASSWORD_SIM_PIN2] = "P2",
+       [OFONO_SIM_PASSWORD_PHSIM_PIN] = "PS",
+       [OFONO_SIM_PASSWORD_PHFSIM_PIN] = "PF",
+       [OFONO_SIM_PASSWORD_PHNET_PIN] = "PN",
+       [OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "PU",
+       [OFONO_SIM_PASSWORD_PHSP_PIN] = "PP",
+       [OFONO_SIM_PASSWORD_PHCORP_PIN] = "PC",
+};
+
+static void at_pin_enable(struct ofono_sim *sim,
+                               enum ofono_sim_password_type passwd_type,
+                               int enable, const char *passwd,
+                               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+       int ret;
+       unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
+
+       if (passwd_type >= len || at_clck_cpwd_fac[passwd_type] == NULL)
+               goto error;
+
+       snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",%i,\"%s\"",
+                       at_clck_cpwd_fac[passwd_type], enable ? 1 : 0, passwd);
+
+       ret = g_at_chat_send(sd->chat, buf, none_prefix,
+                               at_lock_unlock_cb, cbd, g_free);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (ret > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_change_passwd(struct ofono_sim *sim,
+                       enum ofono_sim_password_type passwd_type,
+                       const char *old_passwd, const char *new_passwd,
+                       ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+       int ret;
+       unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
+
+       if (passwd_type >= len ||
+                       at_clck_cpwd_fac[passwd_type] == NULL)
+               goto error;
+
+       snprintf(buf, sizeof(buf), "AT+CPWD=\"%s\",\"%s\",\"%s\"",
+                       at_clck_cpwd_fac[passwd_type], old_passwd, new_passwd);
+
+       ret = g_at_chat_send(sd->chat, buf, none_prefix,
+                               at_lock_unlock_cb, cbd, g_free);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (ret > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_lock_status_cb(gboolean ok, GAtResult *result,
+               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sim_locked_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       int locked;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLCK:")) {
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_next_number(&iter, &locked);
+
+       DBG("lock_status_cb: %i", locked);
+
+       cb(&error, locked, cbd->data);
+}
+
+static void at_pin_query_enabled(struct ofono_sim *sim,
+                               enum ofono_sim_password_type passwd_type,
+                               ofono_sim_locked_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+       unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
+
+       if (passwd_type >= len || at_clck_cpwd_fac[passwd_type] == NULL)
+               goto error;
+
+       snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2",
+                       at_clck_cpwd_fac[passwd_type]);
+
+       if (g_at_chat_send(sd->chat, buf, clck_prefix,
+                               at_lock_status_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static gboolean at_sim_register(gpointer user)
+{
+       struct ofono_sim *sim = user;
+
+       ofono_sim_register(sim);
+
+       return FALSE;
+}
+
+static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct sim_data *sd;
+
+       sd = g_new0(struct sim_data, 1);
+       sd->chat = g_at_chat_clone(chat);
+       sd->vendor = vendor;
+
+       switch (sd->vendor) {
+       case OFONO_VENDOR_WAVECOM:
+               g_at_chat_add_terminator(sd->chat, "+CPIN:", 6, TRUE);
+               break;
+       case OFONO_VENDOR_MBM:
+               g_at_chat_send(sd->chat, "AT*EPEE=1", NULL, NULL, NULL, NULL);
+               break;
+       default:
+               break;
+       }
+
+       ofono_sim_set_data(sim, sd);
+       g_idle_add(at_sim_register, sim);
+
+       return 0;
+}
+
+static void at_sim_remove(struct ofono_sim *sim)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       ofono_sim_set_data(sim, NULL);
+
+       g_at_chat_unref(sd->chat);
+       g_free(sd);
+}
+
+static struct ofono_sim_driver driver = {
+       .name                   = "atmodem",
+       .probe                  = at_sim_probe,
+       .remove                 = at_sim_remove,
+       .read_file_info         = at_sim_read_info,
+       .read_file_transparent  = at_sim_read_binary,
+       .read_file_linear       = at_sim_read_record,
+       .read_file_cyclic       = at_sim_read_record,
+       .write_file_transparent = at_sim_update_binary,
+       .write_file_linear      = at_sim_update_record,
+       .write_file_cyclic      = at_sim_update_cyclic,
+       .read_imsi              = at_read_imsi,
+       .query_passwd_state     = at_pin_query,
+       .query_pin_retries      = at_pin_retries_query,
+       .send_passwd            = at_pin_send,
+       .reset_passwd           = at_pin_send_puk,
+       .lock                   = at_pin_enable,
+       .change_passwd          = at_change_passwd,
+       .query_locked           = at_pin_query_enabled,
+};
+
+static struct ofono_sim_driver driver_noef = {
+       .name                   = "atmodem-noef",
+       .probe                  = at_sim_probe,
+       .remove                 = at_sim_remove,
+       .read_imsi              = at_read_imsi,
+       .query_passwd_state     = at_pin_query,
+       .query_pin_retries      = at_pin_retries_query,
+       .send_passwd            = at_pin_send,
+       .reset_passwd           = at_pin_send_puk,
+       .lock                   = at_pin_enable,
+       .change_passwd          = at_change_passwd,
+       .query_locked           = at_pin_query_enabled,
+};
+
+void at_sim_init(void)
+{
+       ofono_sim_driver_register(&driver);
+       ofono_sim_driver_register(&driver_noef);
+}
+
+void at_sim_exit(void)
+{
+       ofono_sim_driver_unregister(&driver);
+       ofono_sim_driver_unregister(&driver_noef);
+}
diff --git a/drivers/atmodem/sms.c b/drivers/atmodem/sms.c
new file mode 100644 (file)
index 0000000..c6fb8c4
--- /dev/null
@@ -0,0 +1,1266 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sms.h>
+#include "smsutil.h"
+#include "util.h"
+#include "vendor.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *csca_prefix[] = { "+CSCA:", NULL };
+static const char *cgsms_prefix[] = { "+CGSMS:", NULL };
+static const char *csms_prefix[] = { "+CSMS:", NULL };
+static const char *cmgf_prefix[] = { "+CMGF:", NULL };
+static const char *cpms_prefix[] = { "+CPMS:", NULL };
+static const char *cnmi_prefix[] = { "+CNMI:", NULL };
+static const char *cmgs_prefix[] = { "+CMGS:", NULL };
+static const char *cmgl_prefix[] = { "+CMGL:", NULL };
+static const char *none_prefix[] = { NULL };
+
+static gboolean set_cmgf(gpointer user_data);
+static gboolean set_cpms(gpointer user_data);
+static void at_cmgl_set_cpms(struct ofono_sms *sms, int store);
+
+#define MAX_CMGF_RETRIES 10
+#define MAX_CPMS_RETRIES 10
+
+static const char *storages[] = {
+       "SM",
+       "ME",
+       "MT",
+       "SR",
+       "BM",
+};
+
+struct sms_data {
+       int store;
+       int incoming;
+       int retries;
+       gboolean expect_sr;
+       gboolean cnma_enabled;
+       char *cnma_ack_pdu;
+       int cnma_ack_pdu_len;
+       guint timeout_source;
+       GAtChat *chat;
+       unsigned int vendor;
+};
+
+struct cpms_request {
+       struct ofono_sms *sms;
+       int store;
+       int index;
+       gboolean expect_sr;
+};
+
+static void at_csca_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sms_sca_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_csca_set(struct ofono_sms *sms,
+                       const struct ofono_phone_number *sca,
+                       ofono_sms_sca_set_cb_t cb, void *user_data)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CSCA=\"%s\",%d", sca->number, sca->type);
+
+       if (g_at_chat_send(data->chat, buf, csca_prefix,
+                               at_csca_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void at_csca_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sms_sca_query_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       struct ofono_phone_number sca;
+       const char *number;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSCA:"))
+               goto err;
+
+       if (!g_at_result_iter_next_string(&iter, &number))
+               goto err;
+
+       if (number[0] == '+') {
+               number = number + 1;
+               sca.type = 145;
+       } else {
+               sca.type = 129;
+       }
+
+       strncpy(sca.number, number, OFONO_MAX_PHONE_NUMBER_LENGTH);
+       sca.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+
+       g_at_result_iter_next_number(&iter, &sca.type);
+
+       DBG("csca_query_cb: %s, %d", sca.number, sca.type);
+
+       cb(&error, &sca, cbd->data);
+
+       return;
+
+err:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void at_csca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb,
+                                       void *user_data)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+
+       if (g_at_chat_send(data->chat, "AT+CSCA?", csca_prefix,
+                               at_csca_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, user_data);
+}
+
+static void at_cmgs_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       GAtResultIter iter;
+       ofono_sms_submit_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       int mr;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CMGS:"))
+               goto err;
+
+       if (!g_at_result_iter_next_number(&iter, &mr))
+               goto err;
+
+       DBG("Got MR: %d", mr);
+
+       cb(&error, mr, cbd->data);
+       return;
+
+err:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void at_cmgs(struct ofono_sms *sms, unsigned char *pdu, int pdu_len,
+                       int tpdu_len, int mms, ofono_sms_submit_cb_t cb,
+                       void *user_data)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char buf[512];
+       int len;
+
+       if (mms) {
+               snprintf(buf, sizeof(buf), "AT+CMMS=%d", mms);
+               g_at_chat_send(data->chat, buf, none_prefix,
+                               NULL, NULL, NULL);
+       }
+
+       len = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", tpdu_len);
+       encode_hex_own_buf(pdu, pdu_len, 0, buf+len);
+
+       if (g_at_chat_send(data->chat, buf, cmgs_prefix,
+                               at_cmgs_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, user_data);
+}
+
+static void at_cgsms_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sms_sca_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void at_cgsms_set(struct ofono_sms *sms, int bearer,
+                       ofono_sms_bearer_set_cb_t cb, void *user_data)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+CGSMS=%d", bearer);
+
+       if (g_at_chat_send(data->chat, buf, none_prefix,
+                               at_cgsms_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void at_cgsms_query_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_sms_bearer_query_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int bearer;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CGSMS:"))
+               goto err;
+
+       g_at_result_iter_next_number(&iter, &bearer);
+
+       cb(&error, bearer, cbd->data);
+
+       return;
+
+err:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void at_cgsms_query(struct ofono_sms *sms,
+                               ofono_sms_bearer_query_cb_t cb, void *user_data)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+
+       if (g_at_chat_send(data->chat, "AT+CGSMS?", cgsms_prefix,
+                               at_cgsms_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, user_data);
+}
+
+static void at_cnma_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       if (!ok)
+               ofono_error("CNMA acknowledgement failed: "
+                               "Further SMS reception is not guaranteed");
+}
+
+static gboolean at_parse_pdu_common(GAtResult *result, const char *prefix,
+                                       const char **pdu, int *pdulen)
+{
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, prefix))
+               return FALSE;
+
+       if (!strcmp(prefix, "+CMT:") && !g_at_result_iter_skip_next(&iter))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, pdulen))
+               return FALSE;
+
+       *pdu = g_at_result_pdu(result);
+
+       return TRUE;
+}
+
+static inline void at_ack_delivery(struct ofono_sms *sms)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+       char buf[256];
+
+       DBG("");
+
+       /* We must acknowledge the PDU using CNMA */
+       if (data->cnma_ack_pdu)
+               snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s",
+                               data->cnma_ack_pdu_len, data->cnma_ack_pdu);
+       else /* Should be a safe fallback */
+               snprintf(buf, sizeof(buf), "AT+CNMA=0");
+
+       g_at_chat_send(data->chat, buf, none_prefix, at_cnma_cb, NULL, NULL);
+}
+
+static void at_cds_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       long pdu_len;
+       int tpdu_len;
+       const char *hexpdu;
+       unsigned char pdu[176];
+
+       if (!at_parse_pdu_common(result, "+CDS:", &hexpdu, &tpdu_len)) {
+               ofono_error("Unable to parse CDS notification");
+               return;
+       }
+
+       if (strlen(hexpdu) > sizeof(pdu) * 2) {
+               ofono_error("Bad PDU length in CDS notification");
+               return;
+       }
+
+       DBG("Got new Status-Report PDU via CDS: %s, %d", hexpdu, tpdu_len);
+
+       /* Decode pdu and notify about new SMS status report */
+       decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
+       ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len);
+
+       if (data->cnma_enabled)
+               at_ack_delivery(sms);
+}
+
+static void at_cmt_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       const char *hexpdu;
+       long pdu_len;
+       int tpdu_len;
+       unsigned char pdu[176];
+
+       if (!at_parse_pdu_common(result, "+CMT:", &hexpdu, &tpdu_len)) {
+               ofono_error("Unable to parse CMT notification");
+               return;
+       }
+
+       if (strlen(hexpdu) > sizeof(pdu) * 2) {
+               ofono_error("Bad PDU length in CMT notification");
+               return;
+       }
+
+       DBG("Got new SMS Deliver PDU via CMT: %s, %d", hexpdu, tpdu_len);
+
+       decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
+       ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
+
+       at_ack_delivery(sms);
+}
+
+static void at_cmgr_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       GAtResultIter iter;
+       const char *hexpdu;
+       unsigned char pdu[176];
+       long pdu_len;
+       int tpdu_len;
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CMGR:"))
+               goto err;
+
+       if (!g_at_result_iter_skip_next(&iter))
+               goto err;
+
+       if (!g_at_result_iter_skip_next(&iter))
+               goto err;
+
+       if (!g_at_result_iter_next_number(&iter, &tpdu_len))
+               goto err;
+
+       hexpdu = g_at_result_pdu(result);
+
+       if (strlen(hexpdu) > sizeof(pdu) * 2)
+               goto err;
+
+       DBG("Got PDU: %s, with len: %d", hexpdu, tpdu_len);
+
+       decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
+
+       if (data->expect_sr)
+               ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len);
+       else
+               ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
+       return;
+
+err:
+       ofono_error("Unable to parse CMGR response");
+}
+
+static void at_cmgr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       if (!ok)
+               ofono_error("Received a CMTI indication but CMGR failed!");
+}
+
+static void at_cmgd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       if (!ok)
+               ofono_error("Unable to delete received SMS");
+}
+
+static void at_cmgr_cpms_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cpms_request *req = user_data;
+       struct ofono_sms *sms = req->sms;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       char buf[128];
+
+       if (!ok) {
+               ofono_error("Received CMTI/CDSI, but CPMS request failed");
+               return;
+       }
+
+       data->store = req->store;
+       data->expect_sr = req->expect_sr;
+
+       snprintf(buf, sizeof(buf), "AT+CMGR=%d", req->index);
+       g_at_chat_send(data->chat, buf, none_prefix, at_cmgr_cb, NULL, NULL);
+
+       /* We don't buffer SMS on the SIM/ME, send along a CMGD as well */
+       snprintf(buf, sizeof(buf), "AT+CMGD=%d", req->index);
+       g_at_chat_send(data->chat, buf, none_prefix, at_cmgd_cb, NULL, NULL);
+}
+
+static void at_send_cmgr_cpms(struct ofono_sms *sms, int store, int index,
+                               gboolean expect_sr)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       if (store == data->store) {
+               struct cpms_request req;
+
+               req.sms = sms;
+               req.store = store;
+               req.index = index;
+               req.expect_sr = expect_sr;
+
+               at_cmgr_cpms_cb(TRUE, NULL, &req);
+       } else {
+               char buf[128];
+               const char *incoming = storages[data->incoming];
+               struct cpms_request *req = g_new(struct cpms_request, 1);
+
+               req->sms = sms;
+               req->store = store;
+               req->index = index;
+               req->expect_sr = expect_sr;
+
+               snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"",
+                               storages[store], storages[store], incoming);
+
+               g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgr_cpms_cb,
+                               req, g_free);
+       }
+}
+
+static void at_cmti_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       enum at_util_sms_store store;
+       int index;
+
+       if (at_util_parse_sms_index_delivery(result, "+CMTI:",
+                                               &store, &index) == FALSE)
+               goto error;
+
+       if (store != AT_UTIL_SMS_STORE_SM && store != AT_UTIL_SMS_STORE_ME)
+               goto error;
+
+       DBG("Got a CMTI indication at %s, index: %d", storages[store], index);
+       at_send_cmgr_cpms(sms, store, index, FALSE);
+       return;
+
+error:
+       ofono_error("Unable to parse CMTI notification");
+}
+
+static void at_cdsi_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       enum at_util_sms_store store;
+       int index;
+
+       if (at_util_parse_sms_index_delivery(result, "+CDSI:",
+                                               &store, &index) == FALSE)
+               goto error;
+
+       /* Some modems actually store status reports in SM, and not SR */
+       if (store != AT_UTIL_SMS_STORE_SR && store != AT_UTIL_SMS_STORE_SM &&
+                       store != AT_UTIL_SMS_STORE_ME)
+               goto error;
+
+       DBG("Got a CDSI indication at %s, index: %d", storages[store], index);
+       at_send_cmgr_cpms(sms, store, index, TRUE);
+       return;
+
+error:
+       ofono_error("Unable to parse CDSI notification");
+}
+
+static void at_cmgl_done(struct ofono_sms *sms)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       DBG("");
+
+       if (data->incoming == AT_UTIL_SMS_STORE_MT &&
+                       data->store == AT_UTIL_SMS_STORE_ME) {
+               at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_SM);
+               return;
+       }
+
+       g_at_chat_register(data->chat, "+CMTI:", at_cmti_notify, FALSE,
+                               sms, NULL);
+       g_at_chat_register(data->chat, "+CMT:", at_cmt_notify, TRUE,
+                               sms, NULL);
+       g_at_chat_register(data->chat, "+CDS:", at_cds_notify, TRUE,
+                               sms, NULL);
+       g_at_chat_register(data->chat, "+CDSI:", at_cdsi_notify, FALSE,
+                               sms, NULL);
+
+       /* We treat CMGR just like a notification */
+       g_at_chat_register(data->chat, "+CMGR:", at_cmgr_notify, TRUE,
+                               sms, NULL);
+}
+
+static void at_cmgl_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       GAtResultIter iter;
+       const char *hexpdu;
+       unsigned char pdu[176];
+       long pdu_len;
+       int tpdu_len;
+       int index;
+       int status;
+       char buf[16];
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+CMGL:")) {
+               if (!g_at_result_iter_next_number(&iter, &index))
+                       goto err;
+
+               if (!g_at_result_iter_next_number(&iter, &status))
+                       goto err;
+
+               if (!g_at_result_iter_skip_next(&iter))
+                       goto err;
+
+               if (!g_at_result_iter_next_number(&iter, &tpdu_len))
+                       goto err;
+
+               /* Only MT messages */
+               if (status != 0 && status != 1)
+                       continue;
+
+               hexpdu = g_at_result_pdu(result);
+
+               DBG("Found an old SMS PDU: %s, with len: %d",
+                               hexpdu, tpdu_len);
+
+               if (strlen(hexpdu) > sizeof(pdu) * 2)
+                       continue;
+
+               decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
+               ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
+
+               /* We don't buffer SMS on the SIM/ME, send along a CMGD */
+               snprintf(buf, sizeof(buf), "AT+CMGD=%d", index);
+               g_at_chat_send(data->chat, buf, none_prefix,
+                               at_cmgd_cb, NULL, NULL);
+       }
+       return;
+
+err:
+       ofono_error("Unable to parse CMGL response");
+}
+
+static void at_cmgl_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+
+       if (!ok)
+               DBG("Initial listing SMS storage failed!");
+
+       at_cmgl_done(sms);
+}
+
+static void at_cmgl_cpms_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cpms_request *req = user_data;
+       struct ofono_sms *sms = req->sms;
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       if (!ok) {
+               ofono_error("Initial CPMS request failed");
+               at_cmgl_done(sms);
+               return;
+       }
+
+       data->store = req->store;
+
+       g_at_chat_send_pdu_listing(data->chat, "AT+CMGL=4", cmgl_prefix,
+                                       at_cmgl_notify, at_cmgl_cb, sms, NULL);
+}
+
+static void at_cmgl_set_cpms(struct ofono_sms *sms, int store)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       if (store == data->store) {
+               struct cpms_request req;
+
+               req.sms = sms;
+               req.store = store;
+
+               at_cmgl_cpms_cb(TRUE, NULL, &req);
+       } else {
+               char buf[128];
+               const char *readwrite = storages[store];
+               const char *incoming = storages[data->incoming];
+               struct cpms_request *req = g_new(struct cpms_request, 1);
+
+               req->sms = sms;
+               req->store = store;
+
+               snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"",
+                               readwrite, readwrite, incoming);
+
+               g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgl_cpms_cb,
+                               req, g_free);
+       }
+}
+
+static void at_sms_initialized(struct ofono_sms *sms)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       /* Inspect and free the incoming SMS storage */
+       if (data->incoming == AT_UTIL_SMS_STORE_MT)
+               at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_ME);
+       else
+               at_cmgl_set_cpms(sms, data->incoming);
+
+       ofono_sms_register(sms);
+}
+
+static void at_sms_not_supported(struct ofono_sms *sms)
+{
+       ofono_error("SMS not supported by this modem.  If this is in error"
+                       " please submit patches to support this hardware");
+
+       ofono_sms_remove(sms);
+}
+
+static void at_cnmi_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+
+       if (!ok)
+               return at_sms_not_supported(sms);
+
+       at_sms_initialized(sms);
+}
+
+static inline char wanted_cnmi(int supported, const char *pref)
+{
+       while (*pref) {
+               if (supported & (1 << (*pref - '0')))
+                       return *pref;
+
+               pref++;
+       }
+
+       return '\0';
+}
+
+static inline gboolean append_cnmi_element(char *buf, int *len, int cap,
+                                               const char *wanted,
+                                               gboolean last)
+{
+       char setting = wanted_cnmi(cap, wanted);
+
+       if (!setting)
+               return FALSE;
+
+       buf[*len] = setting;
+
+       if (last)
+               buf[*len + 1] = '\0';
+       else
+               buf[*len + 1] = ',';
+
+       *len += 2;
+
+       return TRUE;
+}
+
+static gboolean build_cnmi_string(char *buf, int *cnmi_opts,
+                                       struct sms_data *data)
+{
+       const char *mode;
+       int len = sprintf(buf, "AT+CNMI=");
+
+       DBG("");
+
+       switch (data->vendor) {
+       case OFONO_VENDOR_GOBI:
+       case OFONO_VENDOR_QUALCOMM_MSM:
+       case OFONO_VENDOR_NOVATEL:
+       case OFONO_VENDOR_HUAWEI:
+       case OFONO_VENDOR_ZTE:
+               /* MSM devices advertise support for mode 2, but return an
+                * error if we attempt to actually use it. */
+               mode = "1";
+               break;
+       default:
+               /* Sounds like 2 is the sanest mode */
+               mode = "2310";
+               break;
+       }
+
+       if (!append_cnmi_element(buf, &len, cnmi_opts[0], mode, FALSE))
+               return FALSE;
+
+       /* Prefer to deliver SMS via +CMT if CNMA is supported */
+       if (!append_cnmi_element(buf, &len, cnmi_opts[1],
+                                       data->cnma_enabled ? "21" : "1", FALSE))
+               return FALSE;
+
+       /* Always deliver CB via +CBM, otherwise don't deliver at all */
+       if (!append_cnmi_element(buf, &len, cnmi_opts[2], "20", FALSE))
+               return FALSE;
+
+       /*
+        * Some manufacturers seem to have trouble with delivery via +CDS.
+        * They report the status report properly, however refuse to +CNMA
+        * ack it with error "CNMA not expected."  However, not acking it
+        * sends the device into la-la land.
+        */
+       switch (data->vendor) {
+       case OFONO_VENDOR_NOVATEL:
+               mode = "20";
+               break;
+       default:
+               mode = "120";
+               break;
+       }
+
+       /*
+        * Try to deliver Status-Reports via +CDS, then CDSI or don't
+        * deliver at all
+        * */
+       if (!append_cnmi_element(buf, &len, cnmi_opts[3], mode, FALSE))
+               return FALSE;
+
+       /* Don't care about buffering, 0 seems safer */
+       if (!append_cnmi_element(buf, &len, cnmi_opts[4], "01", TRUE))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void construct_ack_pdu(struct sms_data *d)
+{
+       struct sms ackpdu;
+       unsigned char pdu[164];
+       int len;
+       int tpdu_len;
+
+       DBG("");
+
+       memset(&ackpdu, 0, sizeof(ackpdu));
+
+       ackpdu.type = SMS_TYPE_DELIVER_REPORT_ACK;
+
+       if (!sms_encode(&ackpdu, &len, &tpdu_len, pdu))
+               goto err;
+
+       /* Constructing an <ackpdu> according to 27.005 Section 4.6 */
+       if (len != tpdu_len)
+               goto err;
+
+       d->cnma_ack_pdu = encode_hex(pdu, tpdu_len, 0);
+       if (d->cnma_ack_pdu == NULL)
+               goto err;
+
+       d->cnma_ack_pdu_len = tpdu_len;
+       return;
+
+err:
+       ofono_error("Unable to construct Deliver ACK PDU");
+}
+
+static void at_cnmi_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       GAtResultIter iter;
+       int cnmi_opts[5]; /* See 27.005 Section 3.4.1 */
+       int opt;
+       int mode;
+       gboolean supported = FALSE;
+       char buf[128];
+
+       if (!ok)
+               goto out;
+
+       memset(cnmi_opts, 0, sizeof(cnmi_opts));
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CNMI:"))
+               goto out;
+
+       for (opt = 0; opt < 5; opt++) {
+               int min, max;
+
+               if (!g_at_result_iter_open_list(&iter))
+                       goto out;
+
+               while (g_at_result_iter_next_range(&iter, &min, &max)) {
+                       for (mode = min; mode <= max; mode++)
+                               cnmi_opts[opt] |= 1 << mode;
+               }
+
+               if (!g_at_result_iter_close_list(&iter))
+                       goto out;
+       }
+
+       if (build_cnmi_string(buf, cnmi_opts, data))
+               supported = TRUE;
+
+       /* support for ack pdu is not working */
+       switch (data->vendor) {
+       case OFONO_VENDOR_IFX:
+       case OFONO_VENDOR_GOBI:
+       case OFONO_VENDOR_ZTE:
+       case OFONO_VENDOR_HUAWEI:
+       case OFONO_VENDOR_NOVATEL:
+       case OFONO_VENDOR_OPTION_HSO:
+               goto out;
+       default:
+               break;
+       }
+
+       if (data->cnma_enabled)
+               construct_ack_pdu(data);
+
+out:
+       if (!supported)
+               return at_sms_not_supported(sms);
+
+       g_at_chat_send(data->chat, buf, cnmi_prefix,
+                       at_cnmi_set_cb, sms, NULL);
+}
+
+static void at_query_cnmi(struct ofono_sms *sms)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       g_at_chat_send(data->chat, "AT+CNMI=?", cnmi_prefix,
+                       at_cnmi_query_cb, sms, NULL);
+}
+
+static void at_cpms_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       if (ok)
+               return at_query_cnmi(sms);
+
+       data->retries += 1;
+
+       if (data->retries == MAX_CPMS_RETRIES) {
+               ofono_error("Unable to set preferred storage");
+               return at_sms_not_supported(sms);
+       }
+
+       data->timeout_source = g_timeout_add_seconds(1, set_cpms, sms);
+}
+
+static gboolean set_cpms(gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       const char *store = storages[data->store];
+       const char *incoming = storages[data->incoming];
+       char buf[128];
+
+       snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"",
+                       store, store, incoming);
+
+       g_at_chat_send(data->chat, buf, cpms_prefix,
+                       at_cpms_set_cb, sms, NULL);
+
+       data->timeout_source = 0;
+
+       return FALSE;
+}
+
+static void at_cmgf_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       if (ok) {
+               data->retries = 0;
+               set_cpms(sms);
+               return;
+       }
+
+       data->retries += 1;
+
+       if (data->retries == MAX_CMGF_RETRIES) {
+               DBG("Unable to enter PDU mode");
+               return at_sms_not_supported(sms);
+       }
+
+       data->timeout_source = g_timeout_add_seconds(1, set_cmgf, sms);
+}
+
+static gboolean set_cmgf(gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       g_at_chat_send(data->chat, "AT+CMGF=0", cmgf_prefix,
+                       at_cmgf_set_cb, sms, NULL);
+
+       data->timeout_source = 0;
+
+       return FALSE;
+}
+
+static void at_cpms_query_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       gboolean supported = FALSE;
+
+       if (ok) {
+               int mem = 0;
+               GAtResultIter iter;
+               const char *store;
+               gboolean me_supported[3];
+               gboolean sm_supported[3];
+               gboolean mt_supported[3];
+
+               memset(me_supported, 0, sizeof(me_supported));
+               memset(sm_supported, 0, sizeof(sm_supported));
+               memset(mt_supported, 0, sizeof(mt_supported));
+
+               g_at_result_iter_init(&iter, result);
+
+               if (!g_at_result_iter_next(&iter, "+CPMS:"))
+                       goto out;
+
+               for (mem = 0; mem < 3; mem++) {
+                       if (!g_at_result_iter_open_list(&iter))
+                               goto out;
+
+                       while (g_at_result_iter_next_string(&iter, &store)) {
+                               if (!strcmp(store, "ME"))
+                                       me_supported[mem] = TRUE;
+                               else if (!strcmp(store, "SM"))
+                                       sm_supported[mem] = TRUE;
+                               else if (!strcmp(store, "MT"))
+                                       mt_supported[mem] = TRUE;
+                       }
+
+                       if (!g_at_result_iter_close_list(&iter))
+                               goto out;
+               }
+
+               if (!sm_supported[2] && !me_supported[2] && !mt_supported[2])
+                       goto out;
+
+               if (sm_supported[0] && sm_supported[1]) {
+                       supported = TRUE;
+                       data->store = AT_UTIL_SMS_STORE_SM;
+               }
+
+               if (me_supported[0] && me_supported[1]) {
+                       supported = TRUE;
+                       data->store = AT_UTIL_SMS_STORE_ME;
+               }
+
+               /* This seems to be a special case, where the modem will
+                * pick & route the SMS to any of the storages supported by
+                * mem1
+                */
+               if (mt_supported[2] && (sm_supported[0] || me_supported[0]))
+                       data->incoming = AT_UTIL_SMS_STORE_MT;
+
+               if (sm_supported[2])
+                       data->incoming = AT_UTIL_SMS_STORE_SM;
+
+               if (me_supported[2])
+                       data->incoming = AT_UTIL_SMS_STORE_ME;
+       }
+out:
+       if (!supported)
+               return at_sms_not_supported(sms);
+
+       set_cmgf(sms);
+}
+
+static void at_cmgf_query_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       gboolean supported = FALSE;
+
+       if (ok) {
+               GAtResultIter iter;
+               int mode;
+
+               g_at_result_iter_init(&iter, result);
+
+               if (!g_at_result_iter_next(&iter, "+CMGF:"))
+                       goto out;
+
+               if (!g_at_result_iter_open_list(&iter))
+                       goto out;
+
+               /* Look for mode 0 (PDU mode) */
+               while (g_at_result_iter_next_number(&iter, &mode))
+                       if (mode == 0)
+                               supported = TRUE;
+       }
+
+out:
+       if (!supported)
+               return at_sms_not_supported(sms);
+
+       g_at_chat_send(data->chat, "AT+CPMS=?", cpms_prefix,
+                       at_cpms_query_cb, sms, NULL);
+}
+
+static void at_csms_status_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       gboolean supported = FALSE;
+
+       if (ok) {
+               GAtResultIter iter;
+               int service;
+               int mt;
+               int mo;
+
+               g_at_result_iter_init(&iter, result);
+
+               if (!g_at_result_iter_next(&iter, "+CSMS:"))
+                       goto out;
+
+
+               switch (data->vendor) {
+               case OFONO_VENDOR_HUAWEI:
+               case OFONO_VENDOR_NOVATEL:
+                       g_at_result_iter_skip_next(&iter);
+                       service = 0;
+                       break;
+               default:
+                       if (!g_at_result_iter_next_number(&iter, &service))
+                               goto out;
+                       break;
+               }
+
+               if (!g_at_result_iter_next_number(&iter, &mt))
+                       goto out;
+
+               if (!g_at_result_iter_next_number(&iter, &mo))
+                       goto out;
+
+               if (service == 1)
+                       data->cnma_enabled = TRUE;
+
+               if (mt == 1 && mo == 1)
+                       supported = TRUE;
+       }
+
+out:
+       if (!supported)
+               return at_sms_not_supported(sms);
+
+       /* Now query supported text format */
+       g_at_chat_send(data->chat, "AT+CMGF=?", cmgf_prefix,
+                       at_cmgf_query_cb, sms, NULL);
+}
+
+static void at_csms_set_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       g_at_chat_send(data->chat, "AT+CSMS?", csms_prefix,
+                       at_csms_status_cb, sms, NULL);
+}
+
+static void at_csms_query_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       struct sms_data *data = ofono_sms_get_data(sms);
+       gboolean cnma_supported = FALSE;
+       GAtResultIter iter;
+       int status_min, status_max;
+       char buf[128];
+
+       if (!ok)
+               return at_sms_not_supported(sms);
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSMS:"))
+               goto out;
+
+       if (!g_at_result_iter_open_list(&iter))
+               goto out;
+
+       while (g_at_result_iter_next_range(&iter, &status_min, &status_max))
+               if (status_min <= 1 && 1 <= status_max)
+                       cnma_supported = TRUE;
+
+       DBG("CSMS query parsed successfully");
+
+out:
+       snprintf(buf, sizeof(buf), "AT+CSMS=%d", cnma_supported ? 1 : 0);
+       g_at_chat_send(data->chat, buf, csms_prefix,
+                       at_csms_set_cb, sms, NULL);
+}
+
+static int at_sms_probe(struct ofono_sms *sms, unsigned int vendor,
+                               void *user)
+{
+       GAtChat *chat = user;
+       struct sms_data *data;
+
+       data = g_new0(struct sms_data, 1);
+       data->chat = g_at_chat_clone(chat);
+       data->vendor = vendor;
+
+       ofono_sms_set_data(sms, data);
+
+       g_at_chat_send(data->chat, "AT+CSMS=?", csms_prefix,
+                       at_csms_query_cb, sms, NULL);
+
+       return 0;
+}
+
+static void at_sms_remove(struct ofono_sms *sms)
+{
+       struct sms_data *data = ofono_sms_get_data(sms);
+
+       g_free(data->cnma_ack_pdu);
+
+       if (data->timeout_source > 0)
+               g_source_remove(data->timeout_source);
+
+       g_at_chat_unref(data->chat);
+       g_free(data);
+
+       ofono_sms_set_data(sms, NULL);
+}
+
+static struct ofono_sms_driver driver = {
+       .name           = "atmodem",
+       .probe          = at_sms_probe,
+       .remove         = at_sms_remove,
+       .sca_query      = at_csca_query,
+       .sca_set        = at_csca_set,
+       .submit         = at_cmgs,
+       .bearer_query   = at_cgsms_query,
+       .bearer_set     = at_cgsms_set,
+};
+
+void at_sms_init(void)
+{
+       ofono_sms_driver_register(&driver);
+}
+
+void at_sms_exit(void)
+{
+       ofono_sms_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/stk.c b/drivers/atmodem/stk.c
new file mode 100644 (file)
index 0000000..f0bd3a0
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/stk.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+#include "stk.h"
+#include "vendor.h"
+
+struct stk_data {
+       GAtChat *chat;
+       unsigned int vendor;
+};
+
+static const char *none_prefix[] = { NULL };
+static const char *cusate_prefix[] = { "+CUSATER:", NULL };
+
+static void at_cusate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_envelope_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       const guint8 *response = NULL;
+       gint len = 0;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok == FALSE)
+               goto done;
+
+       /*
+        * According to 27.007, Section 12.2.5 the envelope response is
+        * returned in +CUSATER intermediate response
+        */
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CUSATER:"))
+               goto done;
+
+       if (!g_at_result_iter_next_hexstring(&iter, &response, &len))
+               goto done;
+
+done:
+       cb(&error, response, len, cbd->data);
+}
+
+static void at_stk_envelope(struct ofono_stk *stk, int length,
+                               const unsigned char *command,
+                               ofono_stk_envelope_cb_t cb, void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = alloca(64 + length * 2);
+       int len;
+
+       len = sprintf(buf, "AT+CUSATE=");
+
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *command++);
+
+       if (g_at_chat_send(sd->chat, buf, cusate_prefix,
+                               at_cusate_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void at_cusatt_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_generic_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void at_stk_terminal_response(struct ofono_stk *stk, int length,
+                                       const unsigned char *value,
+                                       ofono_stk_generic_cb_t cb,
+                                       void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = alloca(64 + length * 2);
+       int len;
+
+       len = sprintf(buf, "AT+CUSATT=");
+
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *value++);
+
+       if (g_at_chat_send(sd->chat, buf, none_prefix,
+                               at_cusatt_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void phonesim_cusatp_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *response;
+       gint len;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CUSATP:"))
+               return;
+
+       if (!g_at_result_iter_next_hexstring(&iter, &response, &len))
+               return;
+
+       ofono_stk_proactive_command_notify(stk, len, response);
+}
+
+static void phonesim_hcmd_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *response;
+       gint len;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "*HCMD:"))
+               return;
+
+       if (!g_at_result_iter_next_hexstring(&iter, &response, &len))
+               return;
+
+       ofono_stk_proactive_command_handled_notify(stk, len, response);
+}
+
+static void phonesim_cusatend_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       ofono_stk_proactive_session_end_notify(stk);
+}
+
+static gboolean at_stk_register(gpointer user)
+{
+       struct ofono_stk *stk = user;
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       g_at_chat_register(sd->chat, "+CUSATP:", phonesim_cusatp_notify,
+                                               FALSE, stk, NULL);
+
+       g_at_chat_register(sd->chat, "+CUSATEND", phonesim_cusatend_notify,
+                                               FALSE, stk, NULL);
+
+       if (sd->vendor == OFONO_VENDOR_PHONESIM)
+               g_at_chat_register(sd->chat, "*HCMD:", phonesim_hcmd_notify,
+                                               FALSE, stk, NULL);
+
+       ofono_stk_register(stk);
+
+       return FALSE;
+}
+
+static int at_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct stk_data *sd;
+
+       sd = g_new0(struct stk_data, 1);
+       sd->chat = g_at_chat_clone(chat);
+       sd->vendor = vendor;
+
+       ofono_stk_set_data(stk, sd);
+       g_idle_add(at_stk_register, stk);
+
+       return 0;
+}
+
+static void at_stk_remove(struct ofono_stk *stk)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       ofono_stk_set_data(stk, NULL);
+
+       g_at_chat_unref(sd->chat);
+       g_free(sd);
+}
+
+static struct ofono_stk_driver driver = {
+       .name                   = "atmodem",
+       .probe                  = at_stk_probe,
+       .remove                 = at_stk_remove,
+       .envelope               = at_stk_envelope,
+       .terminal_response      = at_stk_terminal_response,
+};
+
+void at_stk_init(void)
+{
+       ofono_stk_driver_register(&driver);
+}
+
+void at_stk_exit(void)
+{
+       ofono_stk_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/stk.h b/drivers/atmodem/stk.h
new file mode 100644 (file)
index 0000000..ab651b2
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void at_sim_fetch_command(struct ofono_stk *stk, int length);
diff --git a/drivers/atmodem/ussd.c b/drivers/atmodem/ussd.c
new file mode 100644 (file)
index 0000000..443251a
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/ussd.h>
+#include "util.h"
+#include "smsutil.h"
+#include "vendor.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *cusd_prefix[] = { "+CUSD:", NULL };
+static const char *none_prefix[] = { NULL };
+static const char *cscs_prefix[] = { "+CSCS:", NULL };
+
+struct ussd_data {
+       GAtChat *chat;
+       unsigned int vendor;
+       enum at_util_charset charset;
+};
+
+static void read_charset_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ussd_data *data = user_data;
+
+       if (!ok)
+               return;
+
+       at_util_parse_cscs_query(result, &data->charset);
+}
+
+static const unsigned char *ucs2_gsm_to_packed(const char *content,
+                                               long *msg_len,
+                                               unsigned char *msg)
+{
+       unsigned char *decoded;
+       long len;
+       unsigned char *gsm;
+       long written;
+       unsigned char *packed;
+       unsigned char buf[182 * 2]; /* 182 USSD chars * 2 (UCS2) */
+
+       if (strlen(content) > sizeof(buf) * 2) /* Hex, 2 chars / byte */
+               return NULL;
+
+       decoded = decode_hex_own_buf(content, -1, &len, 0, buf);
+
+       if (decoded == NULL)
+               return NULL;
+
+       gsm = convert_ucs2_to_gsm(decoded, len, NULL, &written, 0);
+
+       if (gsm == NULL)
+               return NULL;
+
+       if (written > 182) {
+               g_free(gsm);
+               return NULL;
+       }
+
+       packed = pack_7bit_own_buf(gsm, written, 0, TRUE, msg_len, 0, msg);
+       g_free(gsm);
+
+       return packed;
+}
+
+static void cusd_parse(GAtResult *result, struct ofono_ussd *ussd)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+       GAtResultIter iter;
+       int status;
+       const char *content;
+       int dcs;
+       enum sms_charset charset;
+       unsigned char msg[160];
+       const unsigned char *msg_ptr = NULL;
+       long msg_len;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CUSD:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &status))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &content))
+               goto out;
+
+       if (!g_at_result_iter_next_number(&iter, &dcs))
+               dcs = 0;
+
+       if (!cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) {
+               ofono_error("Unsupported USSD data coding scheme (%02x)", dcs);
+               status = 4; /* Not supported */
+               goto out;
+       }
+
+       DBG("response charset %d modem charset %d", charset, data->charset);
+
+       switch (charset) {
+       case SMS_CHARSET_7BIT:
+               switch (data->charset) {
+               case AT_UTIL_CHARSET_GSM:
+                       msg_ptr = pack_7bit_own_buf((const guint8 *) content,
+                                                       -1, 0, TRUE, &msg_len,
+                                                       0, msg);
+                       break;
+
+               case AT_UTIL_CHARSET_UTF8:
+                       if (ussd_encode(content, &msg_len, msg) == TRUE)
+                               msg_ptr = msg;
+
+                       break;
+
+               case AT_UTIL_CHARSET_UCS2:
+                       msg_ptr = ucs2_gsm_to_packed(content, &msg_len, msg);
+                       break;
+
+               default:
+                       msg_ptr = NULL;
+               }
+               break;
+
+       case SMS_CHARSET_8BIT:
+       case SMS_CHARSET_UCS2:
+               msg_ptr = decode_hex_own_buf(content, -1, &msg_len, 0, msg);
+               break;
+       }
+
+       DBG("msg ptr %p msg len %ld", msg_ptr, msg_len);
+
+out:
+       ofono_ussd_notify(ussd, status, dcs, msg_ptr, msg_ptr ? msg_len : 0);
+}
+
+static void cusd_request_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_ussd_cb_t cb = cbd->cb;
+       struct ofono_ussd *ussd = cbd->user;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+
+       cusd_parse(result, ussd);
+}
+
+static void at_ussd_request(struct ofono_ussd *ussd, int dcs,
+                               const unsigned char *pdu, int len,
+                               ofono_ussd_cb_t cb, void *user_data)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char buf[512];
+       enum sms_charset charset;
+
+       cbd->user = ussd;
+
+       if (!cbs_dcs_decode(dcs, NULL, NULL, &charset,
+                                       NULL, NULL, NULL))
+               goto error;
+
+       if (charset == SMS_CHARSET_7BIT) {
+               unsigned char unpacked_buf[182];
+               long written;
+
+               unpack_7bit_own_buf(pdu, len, 0, TRUE, sizeof(unpacked_buf),
+                                       &written, 0, unpacked_buf);
+
+               if (written < 1)
+                       goto error;
+
+               snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%.*s\",%d",
+                               (int) written, unpacked_buf, dcs);
+       } else {
+               char coded_buf[321];
+               char *converted = encode_hex_own_buf(pdu, len, 0, coded_buf);
+
+               if (converted == NULL)
+                       goto error;
+
+               snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%s\",%d",
+                               converted, dcs);
+       }
+
+       if (g_at_chat_send(data->chat, buf, cusd_prefix,
+                               cusd_request_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void cusd_cancel_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_ussd_cb_t cb = cbd->cb;
+       struct ussd_data *data = cbd->user;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       switch (data->vendor) {
+       case OFONO_VENDOR_GOBI:
+       case OFONO_VENDOR_QUALCOMM_MSM:
+               /* All errors and notifications arrive unexpected and
+                * thus just reset the state here. This is safer than
+                * getting stuck in a dead-lock. */
+               error.type = OFONO_ERROR_TYPE_NO_ERROR;
+               error.error = 0;
+               break;
+       default:
+               break;
+       }
+
+       cb(&error, cbd->data);
+}
+
+static void at_ussd_cancel(struct ofono_ussd *ussd,
+                               ofono_ussd_cb_t cb, void *user_data)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+
+       cbd->user = data;
+
+       if (g_at_chat_send(data->chat, "AT+CUSD=2", none_prefix,
+                               cusd_cancel_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void cusd_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_ussd *ussd = user_data;
+
+       cusd_parse(result, ussd);
+}
+
+static void at_ussd_register(gboolean ok, GAtResult *result, gpointer user)
+{
+       struct ofono_ussd *ussd = user;
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+
+       if (!ok) {
+               ofono_error("Could not enable CUSD notifications");
+               return;
+       }
+
+       g_at_chat_register(data->chat, "+CUSD:", cusd_notify,
+                                               FALSE, ussd, NULL);
+
+       ofono_ussd_register(ussd);
+}
+
+static int at_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor,
+                               void *user)
+{
+       GAtChat *chat = user;
+       struct ussd_data *data;
+
+       data = g_new0(struct ussd_data, 1);
+       data->chat = g_at_chat_clone(chat);
+       data->vendor = vendor;
+
+       ofono_ussd_set_data(ussd, data);
+
+       g_at_chat_send(data->chat, "AT+CSCS?", cscs_prefix,
+                       read_charset_cb, data, NULL);
+
+       g_at_chat_send(data->chat, "AT+CUSD=1", NULL,
+                       at_ussd_register, ussd, NULL);
+
+       return 0;
+}
+
+static void at_ussd_remove(struct ofono_ussd *ussd)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+
+       ofono_ussd_set_data(ussd, NULL);
+
+       g_at_chat_unref(data->chat);
+       g_free(data);
+}
+
+static struct ofono_ussd_driver driver = {
+       .name           = "atmodem",
+       .probe          = at_ussd_probe,
+       .remove         = at_ussd_remove,
+       .request        = at_ussd_request,
+       .cancel         = at_ussd_cancel
+};
+
+void at_ussd_init(void)
+{
+       ofono_ussd_driver_register(&driver);
+}
+
+void at_ussd_exit(void)
+{
+       ofono_ussd_driver_unregister(&driver);
+}
diff --git a/drivers/atmodem/vendor.h b/drivers/atmodem/vendor.h
new file mode 100644 (file)
index 0000000..44b037f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum ofono_vendor {
+       OFONO_VENDOR_GENERIC = 0,
+       OFONO_VENDOR_CALYPSO,
+       OFONO_VENDOR_IFX,
+       OFONO_VENDOR_STE,
+       OFONO_VENDOR_MBM,
+       OFONO_VENDOR_GOBI,
+       OFONO_VENDOR_QUALCOMM_MSM,
+       OFONO_VENDOR_OPTION_HSO,
+       OFONO_VENDOR_ZTE,
+       OFONO_VENDOR_HUAWEI,
+       OFONO_VENDOR_SIERRA,
+       OFONO_VENDOR_NOVATEL,
+       OFONO_VENDOR_WAVECOM,
+       OFONO_VENDOR_NOKIA,
+       OFONO_VENDOR_PHONESIM,
+       OFONO_VENDOR_TELIT,
+       OFONO_VENDOR_SPEEDUP,
+       OFONO_VENDOR_SAMSUNG,
+       OFONO_VENDOR_SIMCOM,
+};
diff --git a/drivers/atmodem/voicecall.c b/drivers/atmodem/voicecall.c
new file mode 100644 (file)
index 0000000..d07052c
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+#include "vendor.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "common.h"
+
+#include "atmodem.h"
+
+/* Amount of ms we wait between CLCC calls */
+#define POLL_CLCC_INTERVAL 500
+
+ /* Amount of time we give for CLIP to arrive before we commence CLCC poll */
+#define CLIP_INTERVAL 200
+
+ /* When +VTD returns 0, an unspecified manufacturer-specific delay is used */
+#define TONE_DURATION 1000
+
+static const char *clcc_prefix[] = { "+CLCC:", NULL };
+static const char *none_prefix[] = { NULL };
+
+/* According to 27.007 COLP is an intermediate status for ATD */
+static const char *atd_prefix[] = { "+COLP:", NULL };
+
+#define FLAG_NEED_CLIP 1
+#define FLAG_NEED_CNAP 2
+#define FLAG_NEED_CDIP 4
+
+struct voicecall_data {
+       GSList *calls;
+       unsigned int local_release;
+       unsigned int clcc_source;
+       GAtChat *chat;
+       unsigned int vendor;
+       unsigned int tone_duration;
+       guint vts_source;
+       unsigned int vts_delay;
+       unsigned char flags;
+};
+
+struct release_id_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int id;
+};
+
+struct change_state_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int affected_types;
+};
+
+static gboolean poll_clcc(gpointer user_data);
+
+static int class_to_call_type(int cls)
+{
+       switch (cls) {
+       case 1:
+               return 0;
+       case 4:
+               return 2;
+       case 8:
+               return 9;
+       default:
+               return 1;
+       }
+}
+
+static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
+                                       int direction, int status,
+                                       const char *num, int num_type, int clip)
+{
+       struct voicecall_data *d = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* Generate a call structure for the waiting call */
+       call = g_try_new(struct ofono_call, 1);
+       if (call == NULL)
+               return NULL;
+
+       ofono_call_init(call);
+
+       call->id = ofono_voicecall_get_next_callid(vc);
+       call->type = type;
+       call->direction = direction;
+       call->status = status;
+
+       if (clip != 2) {
+               strncpy(call->phone_number.number, num,
+                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = num_type;
+       }
+
+       call->clip_validity = clip;
+       call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE;
+
+       d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
+
+       return call;
+}
+
+static void clcc_poll_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *calls;
+       GSList *n, *o;
+       struct ofono_call *nc, *oc;
+       gboolean poll_again = FALSE;
+
+       if (!ok) {
+               ofono_error("We are polling CLCC and received an error");
+               ofono_error("All bets are off for call management");
+               return;
+       }
+
+       calls = at_util_parse_clcc(result);
+
+       n = calls;
+       o = vd->calls;
+
+       while (n || o) {
+               nc = n ? n->data : NULL;
+               oc = o ? o->data : NULL;
+
+               switch (vd->vendor) {
+               case OFONO_VENDOR_QUALCOMM_MSM:
+                       poll_again = TRUE;
+                       break;
+               default:
+                       if (nc && nc->status >= CALL_STATUS_DIALING &&
+                                       nc->status <= CALL_STATUS_WAITING)
+                               poll_again = TRUE;
+                       break;
+               }
+
+               if (oc && (nc == NULL || (nc->id > oc->id))) {
+                       enum ofono_disconnect_reason reason;
+
+                       if (vd->local_release & (1 << oc->id))
+                               reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+                       else
+                               reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+
+                       if (!oc->type)
+                               ofono_voicecall_disconnected(vc, oc->id,
+                                                               reason, NULL);
+
+                       o = o->next;
+               } else if (nc && (oc == NULL || (nc->id < oc->id))) {
+                       /* new call, signal it */
+                       if (nc->type == 0)
+                               ofono_voicecall_notify(vc, nc);
+
+                       n = n->next;
+               } else {
+                       /*
+                        * Always use the clip_validity from old call
+                        * the only place this is truly told to us is
+                        * in the CLIP notify, the rest are fudged
+                        * anyway.  Useful when RING, CLIP is used,
+                        * and we're forced to use CLCC and clip_validity
+                        * is 1
+                        */
+                       if (oc->clip_validity == 1)
+                               nc->clip_validity = oc->clip_validity;
+
+                       /*
+                        * CNAP doesn't arrive as part of CLCC, always
+                        * re-use from the old call
+                        */
+                       strncpy(nc->name, oc->name,
+                                       OFONO_MAX_CALLER_NAME_LENGTH);
+                       nc->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0';
+                       nc->cnap_validity = oc->cnap_validity;
+
+                       /*
+                        * CDIP doesn't arrive as part of CLCC, always
+                        * re-use from the old call
+                        */
+                       memcpy(&nc->called_number, &oc->called_number,
+                                       sizeof(oc->called_number));
+
+                       /*
+                        * If the CLIP is not provided and the CLIP never
+                        * arrives, or RING is used, then signal the call
+                        * here
+                        */
+                       if (nc->status == CALL_STATUS_INCOMING &&
+                                       (vd->flags & FLAG_NEED_CLIP)) {
+                               if (nc->type == 0)
+                                       ofono_voicecall_notify(vc, nc);
+
+                               vd->flags &= ~FLAG_NEED_CLIP;
+                       } else if (memcmp(nc, oc, sizeof(*nc)) && nc->type == 0)
+                               ofono_voicecall_notify(vc, nc);
+
+                       n = n->next;
+                       o = o->next;
+               }
+       }
+
+       g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+       g_slist_free(vd->calls);
+
+       vd->calls = calls;
+
+       vd->local_release = 0;
+
+       if (poll_again && !vd->clcc_source)
+               vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
+                                               poll_clcc, vc);
+}
+
+static gboolean poll_clcc(gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                               clcc_poll_cb, vc, NULL);
+
+       vd->clcc_source = 0;
+
+       return FALSE;
+}
+
+static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct change_state_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok && req->affected_types) {
+               GSList *l;
+               struct ofono_call *call;
+
+               for (l = vd->calls; l; l = l->next) {
+                       call = l->data;
+
+                       if (req->affected_types & (1 << call->status))
+                               vd->local_release |= (1 << call->id);
+               }
+       }
+
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                       clcc_poll_cb, req->vc, NULL);
+
+       /* We have to callback after we schedule a poll if required */
+       req->cb(&error, req->data);
+}
+
+static void release_id_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct release_id_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok)
+               vd->local_release = 1 << req->id;
+
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                       clcc_poll_cb, req->vc, NULL);
+
+       /* We have to callback after we schedule a poll if required */
+       req->cb(&error, req->data);
+}
+
+static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_voicecall *vc = cbd->user;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       ofono_voicecall_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       const char *num;
+       int type = 128;
+       int validity = 2;
+       struct ofono_error error;
+       struct ofono_call *call;
+       GSList *l;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto out;
+
+       /* On a success, make sure to put all active calls on hold */
+       for (l = vd->calls; l; l = l->next) {
+               call = l->data;
+
+               if (call->status != CALL_STATUS_ACTIVE)
+                       continue;
+
+               call->status = CALL_STATUS_HELD;
+               ofono_voicecall_notify(vc, call);
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+COLP:")) {
+               g_at_result_iter_next_string(&iter, &num);
+               g_at_result_iter_next_number(&iter, &type);
+
+               if (strlen(num) > 0)
+                       validity = 0;
+               else
+                       validity = 2;
+
+               DBG("colp_notify: %s %d %d", num, type, validity);
+       }
+
+       /* Generate a voice call that was just dialed, we guess the ID */
+       call = create_call(vc, 0, 0, CALL_STATUS_DIALING, num, type, validity);
+       if (call == NULL) {
+               ofono_error("Unable to malloc, call tracking will fail!");
+               return;
+       }
+
+       /* oFono core will generate a call with the dialed number
+        * inside its dial callback.  Unless we got COLP information
+        * we do not need to communicate that a call is being
+        * dialed
+        */
+       if (validity != 2)
+               ofono_voicecall_notify(vc, call);
+
+       if (!vd->clcc_source)
+               vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
+                                               poll_clcc, vc);
+
+out:
+       cb(&error, cbd->data);
+}
+
+static void at_dial(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+                       void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[256];
+
+       cbd->user = vc;
+
+       if (ph->type == 145)
+               snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
+       else
+               snprintf(buf, sizeof(buf), "ATD%s", ph->number);
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_INVOCATION:
+               strcat(buf, "I");
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               strcat(buf, "i");
+               break;
+       default:
+               break;
+       }
+
+       strcat(buf, ";");
+
+       if (g_at_chat_send(vd->chat, buf, atd_prefix,
+                               atd_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_template(const char *cmd, struct ofono_voicecall *vc,
+                       GAtResultFunc result_cb, unsigned int affected_types,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->affected_types = affected_types;
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               result_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_answer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       at_template("ATA", vc, generic_cb, 0, cb, data);
+}
+
+static void at_hangup(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Hangup active call */
+       at_template("AT+CHUP", vc, generic_cb, 0x3f, cb, data);
+}
+
+static void clcc_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *l;
+
+       if (!ok)
+               return;
+
+       vd->calls = at_util_parse_clcc(result);
+
+       for (l = vd->calls; l; l = l->next)
+               ofono_voicecall_notify(vc, l->data);
+}
+
+static void at_hold_all_active(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       at_template("AT+CHLD=2", vc, generic_cb, 0, cb, data);
+}
+
+static void at_release_all_held(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int held_status = 1 << CALL_STATUS_HELD;
+       at_template("AT+CHLD=0", vc, generic_cb, held_status, cb, data);
+}
+
+static void at_set_udub(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int incoming_or_waiting =
+               (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
+
+       at_template("AT+CHLD=0", vc, generic_cb, incoming_or_waiting,
+                       cb, data);
+}
+
+static void at_release_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       at_template("AT+CHLD=1", vc, generic_cb, 0x1, cb, data);
+}
+
+static void at_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct release_id_req *req = g_try_new0(struct release_id_req, 1);
+       char buf[32];
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->id = id;
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id);
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               release_id_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_private_chat(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id);
+       at_template(buf, vc, generic_cb, 0, cb, data);
+}
+
+static void at_create_multiparty(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       at_template("AT+CHLD=3", vc, generic_cb, 0, cb, data);
+}
+
+static void at_transfer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Held & Active */
+       unsigned int transfer = 0x1 | 0x2;
+
+       /* Transfer can puts held & active calls together and disconnects
+        * from both.  However, some networks support transferring of
+        * dialing/ringing calls as well.
+        */
+       transfer |= 0x4 | 0x8;
+
+       at_template("AT+CHLD=4", vc, generic_cb, transfer, cb, data);
+}
+
+static void at_deflect(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[128];
+       unsigned int incoming_or_waiting =
+               (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
+
+       snprintf(buf, sizeof(buf), "AT+CTFR=%s,%d", ph->number, ph->type);
+       at_template(buf, vc, generic_cb, incoming_or_waiting, cb, data);
+}
+
+static gboolean vts_timeout_cb(gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct voicecall_data *vd = cbd->user;
+       ofono_voicecall_cb_t cb = cbd->cb;
+
+       vd->vts_source = 0;
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       g_free(cbd);
+
+       return FALSE;
+}
+
+static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct voicecall_data *vd = cbd->user;
+       ofono_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, cbd->data);
+
+               g_free(cbd);
+               return;
+       }
+
+       vd->vts_source = g_timeout_add(vd->vts_delay, vts_timeout_cb, cbd);
+}
+
+static void at_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       int len = strlen(dtmf);
+       int s;
+       int i;
+       char *buf;
+
+       cbd->user = vd;
+
+       /* strlen("+VTS=T;") = 7 + initial AT + null */
+       buf = g_try_new(char, len * 9 + 3);
+       if (buf == NULL)
+               goto error;
+
+       s = sprintf(buf, "AT+VTS=%c", dtmf[0]);
+
+       for (i = 1; i < len; i++)
+               s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
+
+       vd->vts_delay = vd->tone_duration * len;
+
+       s = g_at_chat_send(vd->chat, buf, none_prefix,
+                               vts_cb, cbd, NULL);
+
+       g_free(buf);
+
+       if (s > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* See comment in CRING */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       /* RING can repeat, ignore if we already have an incoming call */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status))
+               return;
+
+       /* Generate an incoming call of unknown type */
+       call = create_call(vc, 9, 1, CALL_STATUS_INCOMING, NULL, 128, 2);
+       if (call == NULL) {
+               ofono_error("Couldn't create call, call management is fubar!");
+               return;
+       }
+
+       /* We don't know the call type, we must run clcc */
+       vd->clcc_source = g_timeout_add(CLIP_INTERVAL, poll_clcc, vc);
+       vd->flags = FLAG_NEED_CLIP | FLAG_NEED_CNAP | FLAG_NEED_CDIP;
+}
+
+static void cring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *line;
+       int type;
+
+       /* Handle the following situation:
+        * Active Call + Waiting Call.  Active Call is Released.  The Waiting
+        * call becomes Incoming and RING/CRING indications are signaled.
+        * Sometimes these arrive before we managed to poll CLCC to find about
+        * the stage change.  If this happens, simply ignore the RING/CRING
+        * when a waiting call exists (cannot have waiting + incoming in GSM)
+        */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       /* CRING can repeat, ignore if we already have an incoming call */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status))
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CRING:"))
+               return;
+
+       line = g_at_result_iter_raw_line(&iter);
+       if (line == NULL)
+               return;
+
+       /* Ignore everything that is not voice for now */
+       if (!strcasecmp(line, "VOICE"))
+               type = 0;
+       else
+               type = 9;
+
+       /* Generate an incoming call */
+       create_call(vc, type, 1, CALL_STATUS_INCOMING, NULL, 128, 2);
+
+       /* We have a call, and call type but don't know the number and
+        * must wait for the CLIP to arrive before announcing the call.
+        * So we wait, and schedule the clcc call.  If the CLIP arrives
+        * earlier, we announce the call there
+        */
+       vd->clcc_source = g_timeout_add(CLIP_INTERVAL, poll_clcc, vc);
+       vd->flags = FLAG_NEED_CLIP | FLAG_NEED_CNAP | FLAG_NEED_CDIP;
+
+       DBG("");
+}
+
+static void clip_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int type, validity;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CLIP for unknown call");
+               return;
+       }
+
+       /* We have already saw a CLIP for this call, no need to parse again */
+       if ((vd->flags & FLAG_NEED_CLIP) == 0)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLIP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               return;
+
+       if (strlen(num) > 0)
+               validity = CLIP_VALIDITY_VALID;
+       else
+               validity = CLIP_VALIDITY_NOT_AVAILABLE;
+
+       /* Skip subaddr, satype and alpha */
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d %d", num, type, validity);
+
+       call = l->data;
+
+       strncpy(call->phone_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->phone_number.type = type;
+       call->clip_validity = validity;
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+
+       vd->flags &= ~FLAG_NEED_CLIP;
+}
+
+static void cdip_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int type;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CDIP for unknown call");
+               return;
+       }
+
+       /* We have already saw a CDIP for this call, no need to parse again */
+       if ((vd->flags & FLAG_NEED_CDIP) == 0)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CDIP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               return;
+
+       DBG("%s %d", num, type);
+
+       call = l->data;
+
+       strncpy(call->called_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->called_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->called_number.type = type;
+
+       /* Only signal the call here if we already signaled it to the core */
+       if (call->type == 0 && (vd->flags & FLAG_NEED_CLIP) == 0)
+               ofono_voicecall_notify(vc, call);
+
+       vd->flags &= ~FLAG_NEED_CDIP;
+}
+
+static void cnap_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *name;
+       int validity;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CNAP for unknown call");
+               return;
+       }
+
+       /* We have already saw a CLIP for this call, no need to parse again */
+       if ((vd->flags & FLAG_NEED_CNAP) == 0)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CNAP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &name))
+               return;
+
+       if (strlen(name) > 0)
+               validity = CNAP_VALIDITY_VALID;
+       else
+               validity = CNAP_VALIDITY_NOT_AVAILABLE;
+
+       /* If we have CNI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d", name, validity);
+
+       call = l->data;
+
+       strncpy(call->name, name,
+               OFONO_MAX_CALLER_NAME_LENGTH);
+       call->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0';
+       call->cnap_validity = validity;
+
+       /* Only signal the call here if we already signaled it to the core */
+       if (call->type == 0 && (vd->flags & FLAG_NEED_CLIP) == 0)
+               ofono_voicecall_notify(vc, call);
+
+       vd->flags &= ~FLAG_NEED_CNAP;
+}
+
+static void ccwa_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int num_type, validity, cls;
+       struct ofono_call *call;
+
+       /* Some modems resend CCWA, ignore it the second time around */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CCWA:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &num_type))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &cls))
+               return;
+
+       /* Skip alpha field */
+       g_at_result_iter_skip_next(&iter);
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d %d %d", num, num_type, cls, validity);
+
+       call = create_call(vc, class_to_call_type(cls), 1, CALL_STATUS_WAITING,
+                               num, num_type, validity);
+       if (call == NULL) {
+               ofono_error("Unable to malloc. Call management is fubar");
+               return;
+       }
+
+       if (call->type == 0) /* Only notify voice calls */
+               ofono_voicecall_notify(vc, call);
+
+       if (vd->clcc_source == 0)
+               vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
+                                               poll_clcc, vc);
+}
+
+static void no_carrier_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                       clcc_poll_cb, vc, NULL);
+}
+
+static void no_answer_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                       clcc_poll_cb, vc, NULL);
+}
+
+static void busy_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       /* Call was rejected, most likely due to network congestion
+        * or UDUB on the other side
+        * TODO: Handle UDUB or other conditions somehow
+        */
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                       clcc_poll_cb, vc, NULL);
+}
+
+static void cssi_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       GAtResultIter iter;
+       int code, index;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSSI:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &code))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &index))
+               index = 0;
+
+       ofono_voicecall_ssn_mo_notify(vc, 0, code, index);
+}
+
+static void cssu_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       GAtResultIter iter;
+       int code;
+       int index;
+       const char *num;
+       struct ofono_phone_number ph;
+
+       ph.number[0] = '\0';
+       ph.type = 129;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSSU:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &code))
+               return;
+
+       if (!g_at_result_iter_next_number_default(&iter, -1, &index))
+               goto out;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               goto out;
+
+       strncpy(ph.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH);
+
+       if (!g_at_result_iter_next_number(&iter, &ph.type))
+               return;
+
+out:
+       ofono_voicecall_ssn_mt_notify(vc, 0, code, index, &ph);
+}
+
+static void vtd_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       int duration;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+       g_at_result_iter_next(&iter, "+VTD:");
+
+       if (!g_at_result_iter_next_number(&iter, &duration))
+               return;
+
+       if (duration)
+               vd->tone_duration = duration * 100;
+}
+
+static void at_voicecall_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       DBG("voicecall_init: registering to notifications");
+
+       g_at_chat_register(vd->chat, "RING", ring_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CDIP:", cdip_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CNAP:", cnap_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL);
+
+       /* Modems with 'better' call progress indicators should
+        * probably not even bother registering to these
+        */
+       g_at_chat_register(vd->chat, "NO CARRIER",
+                               no_carrier_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "NO ANSWER",
+                               no_answer_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "BUSY", busy_notify, FALSE, vc, NULL);
+
+       g_at_chat_register(vd->chat, "+CSSI:", cssi_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CSSU:", cssu_notify, FALSE, vc, NULL);
+
+       ofono_voicecall_register(vc);
+
+       /* Populate the call list */
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_cb, vc, NULL);
+}
+
+static int at_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_try_new0(struct voicecall_data, 1);
+       if (vd == NULL)
+               return -ENOMEM;
+
+       vd->chat = g_at_chat_clone(chat);
+       vd->vendor = vendor;
+       vd->tone_duration = TONE_DURATION;
+
+       ofono_voicecall_set_data(vc, vd);
+
+       g_at_chat_send(vd->chat, "AT+CRC=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CDIP=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CNAP=1", NULL, NULL, NULL, NULL);
+
+       switch (vd->vendor) {
+       case OFONO_VENDOR_QUALCOMM_MSM:
+               g_at_chat_send(vd->chat, "AT+COLP=0", NULL, NULL, NULL, NULL);
+               break;
+       default:
+               g_at_chat_send(vd->chat, "AT+COLP=1", NULL, NULL, NULL, NULL);
+               break;
+       }
+
+       g_at_chat_send(vd->chat, "AT+CSSN=1,1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+VTD?", NULL,
+                               vtd_query_cb, vc, NULL);
+       g_at_chat_send(vd->chat, "AT+CCWA=1", NULL,
+                               at_voicecall_initialized, vc, NULL);
+
+       return 0;
+}
+
+static void at_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       if (vd->clcc_source)
+               g_source_remove(vd->clcc_source);
+
+       if (vd->vts_source)
+               g_source_remove(vd->vts_source);
+
+       g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+       g_slist_free(vd->calls);
+
+       ofono_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "atmodem",
+       .probe                  = at_voicecall_probe,
+       .remove                 = at_voicecall_remove,
+       .dial                   = at_dial,
+       .answer                 = at_answer,
+       .hangup_all             = at_hangup,
+       .hold_all_active        = at_hold_all_active,
+       .release_all_held       = at_release_all_held,
+       .set_udub               = at_set_udub,
+       .release_all_active     = at_release_all_active,
+       .release_specific       = at_release_specific,
+       .private_chat           = at_private_chat,
+       .create_multiparty      = at_create_multiparty,
+       .transfer               = at_transfer,
+       .deflect                = at_deflect,
+       .swap_without_accept    = NULL,
+       .send_tones             = at_send_dtmf
+};
+
+void at_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void at_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/calypsomodem/calypsomodem.c b/drivers/calypsomodem/calypsomodem.c
new file mode 100644 (file)
index 0000000..cf1105c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "calypsomodem.h"
+
+static int calypsomodem_init(void)
+{
+       calypso_voicecall_init();
+       calypso_stk_init();
+
+       return 0;
+}
+
+static void calypsomodem_exit(void)
+{
+       calypso_stk_exit();
+       calypso_voicecall_exit();
+}
+
+OFONO_PLUGIN_DEFINE(calypsomodem, "Calypso modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       calypsomodem_init, calypsomodem_exit)
diff --git a/drivers/calypsomodem/calypsomodem.h b/drivers/calypsomodem/calypsomodem.h
new file mode 100644 (file)
index 0000000..52a1083
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void calypso_voicecall_init(void);
+extern void calypso_voicecall_exit(void);
+
+extern void calypso_stk_init(void);
+extern void calypso_stk_exit(void);
diff --git a/drivers/calypsomodem/stk.c b/drivers/calypsomodem/stk.c
new file mode 100644 (file)
index 0000000..9ac9d7c
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/stk.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "calypsomodem.h"
+
+struct stk_data {
+       GAtChat *chat;
+};
+
+static const char *none_prefix[] = { NULL };
+static const char *sate_prefix[] = { "%SATE:", NULL };
+
+static void sate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_envelope_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       struct ofono_error error;
+       const guint8 *pdu = NULL;
+       gint len = 0;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       /*
+        * Ignore errors "SIM memory failure" and "Unknown error", seem
+        * to be generated for no reason.
+        */
+       if (!ok && error.type == OFONO_ERROR_TYPE_CMS && error.error == 320) {
+               ok = TRUE;
+               error.type = OFONO_ERROR_TYPE_NO_ERROR;
+       }
+       if (!ok && error.type == OFONO_ERROR_TYPE_CME && error.error == 100) {
+               ok = TRUE;
+               error.type = OFONO_ERROR_TYPE_NO_ERROR;
+       }
+
+       if (!ok)
+               goto done;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "%SATE:") == FALSE)
+               goto done;
+
+       /* Response data is optional */
+       g_at_result_iter_next_hexstring(&iter, &pdu, &len);
+
+       DBG("len %d", len);
+
+done:
+       cb(&error, pdu, len, cbd->data);
+}
+
+static void calypso_stk_envelope(struct ofono_stk *stk, int length,
+                                       const unsigned char *command,
+                                       ofono_stk_envelope_cb_t cb, void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, 64 + length * 2);
+       int len;
+
+       DBG("");
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT%%SATE=\"");
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *command++);
+       len += sprintf(buf + len, "\"");
+
+       DBG("%s", buf);
+
+       if (g_at_chat_send(sd->chat, buf, sate_prefix,
+                                       sate_cb, cbd, g_free) > 0) {
+               g_free(buf);
+               return;
+       }
+
+error:
+       g_free(buf);
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void satr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_generic_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void calypso_stk_terminal_response(struct ofono_stk *stk, int length,
+                                               const unsigned char *command,
+                                               ofono_stk_generic_cb_t cb,
+                                               void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, 64 + length * 2);
+       int len;
+
+       DBG("");
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT%%SATR=\"");
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *command++);
+       len += sprintf(buf + len, "\"");
+
+       DBG("%s", buf);
+
+       if (g_at_chat_send(sd->chat, buf, none_prefix,
+                                       satr_cb, cbd, g_free) > 0) {
+               g_free(buf);
+               return;
+       }
+
+error:
+       g_free(buf);
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void sati_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *pdu;
+       gint len;
+       gboolean ret;
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "%SATI:"))
+               return;
+
+       ret = g_at_result_iter_next_hexstring(&iter, &pdu, &len);
+       if (!ret || len == 0) {
+               /*
+                * An empty notification is a End Session notification on
+                * the part of the UICC.
+                */
+               ofono_stk_proactive_session_end_notify(stk);
+
+               return;
+       }
+
+       ofono_stk_proactive_command_notify(stk, len, pdu);
+}
+
+static void sata_notify(GAtResult *result, gpointer user_data)
+{
+       DBG("");
+
+       /* TODO: Pending call alert */
+}
+
+static void satn_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *pdu;
+       gint len;
+
+       DBG("");
+
+       /* Proactive command has been handled by the modem. */
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "%SATN:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE)
+               return;
+
+       if (len == 0)
+               return;
+
+       ofono_stk_proactive_command_handled_notify(stk, len, pdu);
+}
+
+static void calypso_stk_register(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       DBG("");
+
+       if (!ok)
+               return;
+
+       g_at_chat_register(sd->chat, "%SATI:", sati_notify, FALSE, stk, NULL);
+       g_at_chat_register(sd->chat, "%SATA:", sata_notify, FALSE, stk, NULL);
+       g_at_chat_register(sd->chat, "%SATN:", satn_notify, FALSE, stk, NULL);
+
+       ofono_stk_register(stk);
+}
+
+static int calypso_stk_probe(struct ofono_stk *stk,
+                               unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct stk_data *sd;
+
+       DBG("");
+
+       sd = g_try_new0(struct stk_data, 1);
+       if (sd == NULL)
+               return -ENOMEM;
+
+       sd->chat = g_at_chat_clone(chat);
+
+       ofono_stk_set_data(stk, sd);
+
+       /*
+        * Provide terminal profile data needed for the download and
+        * enable %SATI / %SATN.  The actual PROFILE DOWNLOAD will
+        * happen during AT+CFUN=1 later.
+        */
+       g_at_chat_send(sd->chat, "AT%SATC=1,\"19E1FFFF0000FF7FFF03FEFF\"",
+                       none_prefix, NULL, stk, NULL);
+
+       /* Enable Call Control / SMS Control */
+       g_at_chat_send(sd->chat, "AT%SATCC=1",
+                       none_prefix, calypso_stk_register, stk, NULL);
+
+       return 0;
+}
+
+static void calypso_stk_remove(struct ofono_stk *stk)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       DBG("");
+
+       ofono_stk_set_data(stk, NULL);
+
+       g_at_chat_unref(sd->chat);
+       g_free(sd);
+}
+
+static struct ofono_stk_driver driver = {
+       .name                   = "calypsomodem",
+       .probe                  = calypso_stk_probe,
+       .remove                 = calypso_stk_remove,
+       .envelope               = calypso_stk_envelope,
+       .terminal_response      = calypso_stk_terminal_response,
+};
+
+void calypso_stk_init(void)
+{
+       ofono_stk_driver_register(&driver);
+}
+
+void calypso_stk_exit(void)
+{
+       ofono_stk_driver_unregister(&driver);
+}
diff --git a/drivers/calypsomodem/voicecall.c b/drivers/calypsomodem/voicecall.c
new file mode 100644 (file)
index 0000000..3c047b7
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "calypsomodem.h"
+
+static const char *none_prefix[] = { NULL };
+
+struct voicecall_data {
+       GAtChat *chat;
+};
+
+static void calypso_generic_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void calypso_template(struct ofono_voicecall *vc, const char *cmd,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               calypso_generic_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void calypso_dial(struct ofono_voicecall *vc,
+                               const struct ofono_phone_number *ph,
+                               enum ofono_clir_option clir,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[256];
+
+       if (ph->type == 145)
+               snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
+       else
+               snprintf(buf, sizeof(buf), "ATD%s", ph->number);
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_INVOCATION:
+               strcat(buf, "I");
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               strcat(buf, "i");
+               break;
+       default:
+               break;
+       }
+
+       strcat(buf, ";");
+
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_answer(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "ATA", cb, data);
+}
+
+static void calypso_ath(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "ATH", cb, data);
+}
+
+static void calypso_chup(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHUP", cb, data);
+}
+
+static void calypso_hold_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=2", cb, data);
+}
+
+static void calypso_release_all_held(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=0", cb, data);
+}
+
+static void calypso_set_udub(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=0", cb, data);
+}
+
+static void calypso_release_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=1", cb, data);
+}
+
+static void calypso_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       /* On calypso, 1X only releases active calls, while 7X releases
+        * active or held calls
+        */
+       snprintf(buf, sizeof(buf), "AT%%CHLD=7%d", id);
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_private_chat(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id);
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_create_multiparty(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=3", cb, data);
+}
+
+static void calypso_transfer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=4", cb, data);
+}
+
+static void calypso_deflect(struct ofono_voicecall *vc,
+                               const struct ofono_phone_number *ph,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[128];
+
+       snprintf(buf, sizeof(buf), "AT+CTFR=%s,%d", ph->number, ph->type);
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       int len = strlen(dtmf);
+       int s;
+       int i;
+       char *buf;
+
+       /* strlen("+VTS=\"T\";") = 9 + initial AT + null */
+       buf = g_try_new(char, len * 9 + 3);
+
+       if (buf == NULL) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       s = sprintf(buf, "AT+VTS=%c", dtmf[0]);
+
+       for (i = 1; i < len; i++)
+               s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
+
+       calypso_template(vc, buf, cb, data);
+       g_free(buf);
+}
+
+static void cpi_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       int id;
+       int msgtype;
+       int direction;
+       int mode;
+       const char *num;
+       int type;
+       int cause;
+       int line = 0;
+       int validity;
+       struct ofono_call call;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "%CPI:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &id))
+               return;
+
+       /* msgtype
+        * 0 - setup
+        * 1 - disconnect
+        * 2 - alert
+        * 3 - call proceed
+        * 4 - sync
+        * 5 - progress
+        * 6 - connected
+        * 7 - release
+        * 8 - reject
+        * 9 - request (MO Setup)
+        * 10 - hold
+        */
+       if (!g_at_result_iter_next_number(&iter, &msgtype))
+               return;
+
+       /* Skip in-band ring tone notification */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       /* Skip traffic channel assignment */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &direction))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &mode))
+               return;
+
+       DBG("id:%d, msgtype:%d, direction:%d, mode:%d",
+               id, msgtype, direction, mode);
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (strlen(num) > 0) {
+               DBG("Len > 0");
+               validity = 0;
+
+               if (!g_at_result_iter_next_number(&iter, &type))
+                       return;
+
+               DBG("type obtained");
+       } else {
+               DBG("skip next");
+               validity = 2;
+               type = 129;
+
+               if (!g_at_result_iter_skip_next(&iter))
+                       return;
+               DBG("skipped");
+       }
+
+       DBG("num:%s, type:%d", num, type);
+
+       /* Skip alpha field */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       g_at_result_iter_next_number(&iter, &cause);
+       g_at_result_iter_next_number(&iter, &line);
+
+       DBG("cause:%d, line:%d", cause, line);
+
+       /* We only care about voice calls here */
+       if (mode != 0)
+               return;
+
+       if (line != 0) {
+               ofono_error("Alternate Line service not yet handled");
+               return;
+       }
+
+       /* Need to send this on the calypso hardware to avoid echo issues */
+       if (msgtype == 3 || msgtype == 4)
+               g_at_chat_send(vd->chat, "AT%N0187", none_prefix,
+                               NULL, NULL, NULL);
+
+       ofono_call_init(&call);
+
+       switch (msgtype) {
+       case 0:
+               /* Set call status to incoming */
+               call.status = 4;
+               break;
+       case 2:
+               /* Set call status to alerting */
+               call.status = 3;
+               break;
+       case 3:
+       case 9:
+               /* Set call status to dialing */
+               call.status = 2;
+               break;
+       case 6:
+               /* Set call status to connected */
+               call.status = 0;
+               break;
+       case 10:
+               /* Set call status to held */
+               call.status = 1;
+               break;
+       case 1:
+       case 8:
+               ofono_voicecall_disconnected(vc, id,
+                                       OFONO_DISCONNECT_REASON_UNKNOWN, NULL);
+               return;
+       default:
+               return;
+       };
+
+       call.id = id;
+       call.type = mode;
+       call.direction = direction;
+       strncpy(call.phone_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call.phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call.phone_number.type = type;
+       call.clip_validity = validity;
+
+       ofono_voicecall_notify(vc, &call);
+}
+
+static void calypso_voicecall_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       DBG("voicecall_init: registering to notifications");
+
+       g_at_chat_register(vd->chat, "%CPI:", cpi_notify, FALSE, vc, NULL);
+
+       ofono_voicecall_register(vc);
+}
+
+static int calypso_voicecall_probe(struct ofono_voicecall *vc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_try_new0(struct voicecall_data, 1);
+       if (vd == NULL)
+               return -ENOMEM;
+
+       vd->chat = g_at_chat_clone(chat);
+
+       ofono_voicecall_set_data(vc, vd);
+
+       g_at_chat_send(vd->chat, "AT%CPI=3", NULL,
+                               calypso_voicecall_initialized, vc, NULL);
+
+       return 0;
+}
+
+static void calypso_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       ofono_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "calypsomodem",
+       .probe                  = calypso_voicecall_probe,
+       .remove                 = calypso_voicecall_remove,
+       .dial                   = calypso_dial,
+       .answer                 = calypso_answer,
+       .hangup_all             = calypso_ath,
+       .hangup_active          = calypso_chup,
+       .hold_all_active        = calypso_hold_all_active,
+       .release_all_held       = calypso_release_all_held,
+       .set_udub               = calypso_set_udub,
+       .release_all_active     = calypso_release_all_active,
+       .release_specific       = calypso_release_specific,
+       .private_chat           = calypso_private_chat,
+       .create_multiparty      = calypso_create_multiparty,
+       .transfer               = calypso_transfer,
+       .deflect                = calypso_deflect,
+       .swap_without_accept    = NULL,
+       .send_tones             = calypso_send_dtmf
+};
+
+void calypso_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void calypso_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/cdmamodem/cdmamodem.c b/drivers/cdmamodem/cdmamodem.c
new file mode 100644 (file)
index 0000000..50908e3
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "cdmamodem.h"
+
+static int cdmamodem_init(void)
+{
+       cdma_voicecall_init();
+       cdma_devinfo_init();
+       cdma_connman_init();
+
+       return 0;
+}
+
+static void cdmamodem_exit(void)
+{
+       cdma_voicecall_exit();
+       cdma_devinfo_exit();
+       cdma_connman_exit();
+}
+
+OFONO_PLUGIN_DEFINE(cdmamodem, "CDMA AT modem driver", VERSION,
+       OFONO_PLUGIN_PRIORITY_DEFAULT, cdmamodem_init, cdmamodem_exit)
diff --git a/drivers/cdmamodem/cdmamodem.h b/drivers/cdmamodem/cdmamodem.h
new file mode 100644 (file)
index 0000000..3554705
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void cdma_voicecall_init(void);
+extern void cdma_voicecall_exit(void);
+extern void cdma_devinfo_init(void);
+extern void cdma_devinfo_exit(void);
+extern void cdma_connman_init(void);
+extern void cdma_connman_exit(void);
diff --git a/drivers/cdmamodem/connman.c b/drivers/cdmamodem/connman.c
new file mode 100644 (file)
index 0000000..8c3265a
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cdma-connman.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gatppp.h"
+
+#include "cdmamodem.h"
+#include "drivers/atmodem/vendor.h"
+
+#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
+
+#define STATIC_IP_NETMASK "255.255.255.255"
+
+static const char *none_prefix[] = { NULL };
+
+enum state {
+       STATE_IDLE,
+       STATE_ENABLING,
+       STATE_DISABLING,
+       STATE_ACTIVE,
+};
+
+struct connman_data {
+       GAtChat *chat;
+       GAtPPP *ppp;
+       unsigned int vendor;
+       enum state state;
+       char username[OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH + 1];
+       char password[OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH + 1];
+       union {
+               ofono_cdma_connman_cb_t down_cb;        /* Down callback */
+               ofono_cdma_connman_up_cb_t up_cb;       /* Up callback */
+       };
+       void *cb_data;                                  /* Callback data */
+};
+
+static void ppp_debug(const char *str, void *data)
+{
+       ofono_info("%s: %s", (const char *) data, str);
+}
+
+static void ppp_connect(const char *interface, const char *local,
+                                       const char *remote,
+                                       const char *dns1, const char *dns2,
+                                       gpointer user_data)
+{
+       struct ofono_cdma_connman *cm = user_data;
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+       const char *dns[3];
+
+       DBG("");
+
+       dns[0] = dns1;
+       dns[1] = dns2;
+       dns[2] = 0;
+
+       ofono_info("IP: %s", local);
+       ofono_info("DNS: %s, %s", dns1, dns2);
+
+       cd->state = STATE_ACTIVE;
+       CALLBACK_WITH_SUCCESS(cd->up_cb, interface, TRUE, local,
+                                       STATIC_IP_NETMASK, NULL,
+                                       dns, cd->cb_data);
+}
+
+static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data)
+{
+       struct ofono_cdma_connman *cm = user_data;
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+
+       DBG("");
+
+       g_at_ppp_unref(cd->ppp);
+       cd->ppp = NULL;
+
+       switch (cd->state) {
+       case STATE_ENABLING:
+               CALLBACK_WITH_FAILURE(cd->up_cb, NULL, FALSE, NULL,
+                                       NULL, NULL, NULL, cd->cb_data);
+               break;
+       case STATE_DISABLING:
+               CALLBACK_WITH_SUCCESS(cd->down_cb, cd->cb_data);
+               break;
+       default:
+               ofono_cdma_connman_deactivated(cm);
+               break;
+       }
+
+       cd->state = STATE_IDLE;
+       g_at_chat_resume(cd->chat);
+}
+
+static gboolean setup_ppp(struct ofono_cdma_connman *cm)
+{
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+       GAtIO *io;
+
+       DBG("");
+
+       io = g_at_chat_get_io(cd->chat);
+
+       g_at_chat_suspend(cd->chat);
+
+       /* open ppp */
+       cd->ppp = g_at_ppp_new();
+
+       if (cd->ppp == NULL) {
+               g_at_chat_resume(cd->chat);
+               return FALSE;
+       }
+
+       if (getenv("OFONO_PPP_DEBUG"))
+               g_at_ppp_set_debug(cd->ppp, ppp_debug, "PPP");
+
+       /* set connect and disconnect callbacks */
+       g_at_ppp_set_connect_function(cd->ppp, ppp_connect, cm);
+       g_at_ppp_set_disconnect_function(cd->ppp, ppp_disconnect, cm);
+
+       g_at_ppp_set_credentials(cd->ppp, cd->username, cd->password);
+
+       /* open the ppp connection */
+       g_at_ppp_open(cd->ppp, io);
+
+       return TRUE;
+}
+
+static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_connman *cm = user_data;
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+
+       DBG("ok %d", ok);
+
+       if (ok == FALSE) {
+               struct ofono_error error;
+
+               ofono_info("Unable to enter data state");
+
+               cd->state = STATE_IDLE;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               cd->up_cb(&error, NULL, 0, NULL, NULL, NULL, NULL,
+                               cd->cb_data);
+               return;
+       }
+
+       setup_ppp(cm);
+}
+
+static void cdma_connman_activate(struct ofono_cdma_connman *cm,
+                                       const char *username,
+                                       const char *password,
+                                       ofono_cdma_connman_up_cb_t cb,
+                                       void *data)
+{
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+
+       DBG("");
+
+       cd->up_cb = cb;
+       cd->cb_data = data;
+       strcpy(cd->username, username);
+       strcpy(cd->password, password);
+
+       cd->state = STATE_ENABLING;
+
+       if (g_at_chat_send(cd->chat, "ATD#777", none_prefix,
+                               atd_cb, cm, NULL) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, data);
+}
+
+static void cdma_connman_deactivate(struct ofono_cdma_connman *cm,
+                                       ofono_cdma_connman_cb_t cb,
+                                       void *data)
+{
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+
+       DBG("");
+
+       cd->state = STATE_DISABLING;
+       cd->down_cb = cb;
+       cd->cb_data = data;
+
+       g_at_ppp_shutdown(cd->ppp);
+}
+
+static void huawei_dsdormant_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_connman *cm = user_data;
+       int dormant;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^DSDORMANT:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &dormant))
+               return;
+
+       switch (dormant) {
+       case 0:
+               ofono_cdma_connman_dormant_notify(cm, FALSE);
+               break;
+       case 1:
+               ofono_cdma_connman_dormant_notify(cm, TRUE);
+               break;
+       default:
+               ofono_error("Invalid DSDORMANT value");
+               break;
+       }
+}
+
+static void at_c0_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_connman *cm = user_data;
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+       GAtChat *chat;
+
+       DBG("ok %d", ok);
+
+       if (ok == FALSE) {
+               ofono_info("Unable to configure circuit 109");
+               ofono_cdma_connman_remove(cm);
+               return;
+       }
+
+       switch (cd->vendor) {
+       case OFONO_VENDOR_HUAWEI:
+               chat = g_at_chat_get_slave(cd->chat);
+               g_at_chat_register(chat, "^DSDORMANT", huawei_dsdormant_notify,
+                                       FALSE, cm, NULL);
+               break;
+       default:
+               break;
+       }
+
+       ofono_cdma_connman_register(cm);
+}
+
+static int cdma_connman_probe(struct ofono_cdma_connman *cm,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct connman_data *cd;
+       struct stat st;
+
+       DBG("");
+
+       if (stat(TUN_SYSFS_DIR, &st) < 0) {
+               ofono_error("Missing support for TUN/TAP devices");
+               return -ENODEV;
+       }
+
+       cd = g_try_new0(struct connman_data, 1);
+       if (cd == NULL)
+               return -ENOMEM;
+
+       cd->chat = g_at_chat_clone(chat);
+       cd->vendor = vendor;
+
+       ofono_cdma_connman_set_data(cm, cd);
+
+       /* Turn off any modem-initiated dormancy timeout */
+       g_at_chat_send(cd->chat, "AT+CTA=0", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(cd->chat, "AT&C0", none_prefix, at_c0_cb, cm, NULL);
+
+       return 0;
+}
+
+static void cdma_connman_remove(struct ofono_cdma_connman *cm)
+{
+       struct connman_data *cd = ofono_cdma_connman_get_data(cm);
+
+       DBG("");
+
+       if (cd->state != STATE_IDLE && cd->ppp) {
+               g_at_ppp_unref(cd->ppp);
+               g_at_chat_resume(cd->chat);
+       }
+
+       ofono_cdma_connman_set_data(cm, NULL);
+
+       g_at_chat_unref(cd->chat);
+       g_free(cd);
+}
+
+static struct ofono_cdma_connman_driver driver = {
+       .name                   = "cdmamodem",
+       .probe                  = cdma_connman_probe,
+       .remove                 = cdma_connman_remove,
+       .activate               = cdma_connman_activate,
+       .deactivate             = cdma_connman_deactivate,
+};
+
+void cdma_connman_init(void)
+{
+       ofono_cdma_connman_driver_register(&driver);
+}
+
+void cdma_connman_exit(void)
+{
+       ofono_cdma_connman_driver_unregister(&driver);
+}
diff --git a/drivers/cdmamodem/devinfo.c b/drivers/cdmamodem/devinfo.c
new file mode 100644 (file)
index 0000000..f8818ae
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "cdmamodem.h"
+
+static const char *gcap_prefix[] = { "+GCAP:", NULL };
+
+static void attr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+         struct cb_data *cbd = user_data;
+         ofono_devinfo_query_cb_t cb = cbd->cb;
+         const char *prefix = cbd->user;
+         struct ofono_error error;
+         const char *attr;
+
+         decode_at_error(&error, g_at_result_final_response(result));
+
+         if (!ok) {
+                 cb(&error, NULL, cbd->data);
+                 return;
+         }
+
+         if (at_util_parse_attr(result, prefix, &attr) == FALSE) {
+                 CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+                 return;
+         }
+
+         cb(&error, attr, cbd->data);
+}
+
+static void cdma_query_manufacturer(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+GMI:";
+
+       if (g_at_chat_send(chat, "AT+GMI", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void cdma_query_model(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+GMM:";
+
+       if (g_at_chat_send(chat, "AT+GMM", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void cdma_query_revision(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+GMR:";
+
+       if (g_at_chat_send(chat, "AT+GMR", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void cdma_query_serial(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb, void *data)
+{
+       struct cb_data *cbd = cb_data_new(cb, data);
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       cbd->user = "+GSN:";
+
+       if (g_at_chat_send(chat, "AT+GSN", NULL, attr_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void capability_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_devinfo *info = user_data;
+
+       ofono_devinfo_register(info);
+}
+
+static int cdma_devinfo_probe(struct ofono_devinfo *info,
+                               unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+
+       ofono_devinfo_set_data(info, g_at_chat_clone(chat));
+
+       g_at_chat_send(chat, "AT+GCAP", gcap_prefix,
+                               capability_cb, info, NULL);
+
+       return 0;
+}
+
+static void cdma_devinfo_remove(struct ofono_devinfo *info)
+{
+       GAtChat *chat = ofono_devinfo_get_data(info);
+
+       g_at_chat_unref(chat);
+       ofono_devinfo_set_data(info, NULL);
+}
+
+static struct ofono_devinfo_driver driver = {
+       .name                   = "cdmamodem",
+       .probe                  = cdma_devinfo_probe,
+       .remove                 = cdma_devinfo_remove,
+       .query_manufacturer     = cdma_query_manufacturer,
+       .query_model            = cdma_query_model,
+       .query_revision         = cdma_query_revision,
+       .query_serial           = cdma_query_serial
+};
+
+void cdma_devinfo_init(void)
+{
+       ofono_devinfo_driver_register(&driver);
+}
+
+void cdma_devinfo_exit(void)
+{
+       ofono_devinfo_driver_unregister(&driver);
+}
diff --git a/drivers/cdmamodem/voicecall.c b/drivers/cdmamodem/voicecall.c
new file mode 100644 (file)
index 0000000..98cf554
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cdma-voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "cdmamodem.h"
+
+static const char *none_prefix[] = { NULL };
+
+struct voicecall_data {
+       GAtChat *chat;
+       unsigned int vendor;
+};
+
+static void cdma_template(const char *cmd, struct ofono_cdma_voicecall *vc,
+                               GAtResultFunc result_cb,
+                               ofono_cdma_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_cdma_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = vc;
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               result_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void cdma_generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_cdma_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void cdma_dial(struct ofono_cdma_voicecall *vc,
+                       const struct ofono_cdma_phone_number *ph,
+                       ofono_cdma_voicecall_cb_t cb, void *data)
+{
+       char buf[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH + 8];
+
+       snprintf(buf, sizeof(buf), "AT+CDV=%s", ph->number);
+       cdma_template(buf, vc, cdma_generic_cb, cb, data);
+}
+
+static void cdma_hangup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+
+       cdma_generic_cb(ok, result, user_data);
+
+       /* TODO: this should come from a modem solicited notification */
+       ofono_cdma_voicecall_disconnected(cbd->user,
+                                       OFONO_DISCONNECT_REASON_LOCAL_HANGUP,
+                                       NULL);
+}
+
+static void cdma_hangup(struct ofono_cdma_voicecall *vc,
+                               ofono_cdma_voicecall_cb_t cb, void *data)
+{
+       /* Hangup active call */
+       cdma_template("AT+CHV", vc, cdma_hangup_cb, cb, data);
+}
+
+static gboolean cdma_voicecall_initialized(gpointer user_data)
+{
+       struct ofono_cdma_voicecall *vc = user_data;
+
+       ofono_cdma_voicecall_register(vc);
+
+       return FALSE;
+}
+
+static int cdma_voicecall_probe(struct ofono_cdma_voicecall *vc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_try_new0(struct voicecall_data, 1);
+       if (vd == NULL)
+               return -ENOMEM;
+
+       vd->chat = g_at_chat_clone(chat);
+       vd->vendor = vendor;
+
+       ofono_cdma_voicecall_set_data(vc, vd);
+       g_idle_add(cdma_voicecall_initialized, vc);
+
+       return 0;
+}
+
+static void cdma_voicecall_remove(struct ofono_cdma_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_cdma_voicecall_get_data(vc);
+
+       ofono_cdma_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_cdma_voicecall_driver driver = {
+       .name                   = "cdmamodem",
+       .probe                  = cdma_voicecall_probe,
+       .remove                 = cdma_voicecall_remove,
+       .dial                   = cdma_dial,
+       .hangup                 = cdma_hangup,
+};
+
+void cdma_voicecall_init(void)
+{
+       ofono_cdma_voicecall_driver_register(&driver);
+}
+
+void cdma_voicecall_exit(void)
+{
+       ofono_cdma_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/dunmodem/dunmodem.c b/drivers/dunmodem/dunmodem.c
new file mode 100644 (file)
index 0000000..35a7dbb
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "dunmodem.h"
+
+static int dunmodem_init(void)
+{
+       dun_netreg_init();
+       dun_gprs_init();
+
+       return 0;
+}
+
+static void dunmodem_exit(void)
+{
+       dun_netreg_exit();
+       dun_gprs_exit();
+}
+
+OFONO_PLUGIN_DEFINE(dunmodem, "Dialup modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       dunmodem_init, dunmodem_exit)
diff --git a/drivers/dunmodem/dunmodem.h b/drivers/dunmodem/dunmodem.h
new file mode 100644 (file)
index 0000000..4fbd4a5
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void dun_netreg_init(void);
+extern void dun_netreg_exit(void);
+
+extern void dun_gprs_init(void);
+extern void dun_gprs_exit(void);
diff --git a/drivers/dunmodem/gprs.c b/drivers/dunmodem/gprs.c
new file mode 100644 (file)
index 0000000..edfe803
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs.h>
+
+#include "dunmodem.h"
+
+static void dun_gprs_set_attached(struct ofono_gprs *gprs, int attached,
+                                               ofono_gprs_cb_t cb, void *data)
+{
+       DBG("");
+
+       CALLBACK_WITH_SUCCESS(cb, data);
+}
+
+static gboolean dun_gprs_finish_registration(gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+
+       ofono_gprs_register(gprs);
+
+       return FALSE;
+}
+
+static int dun_gprs_probe(struct ofono_gprs *gprs,
+                                       unsigned int vendor, void *data)
+{
+       DBG("");
+
+       g_idle_add(dun_gprs_finish_registration, gprs);
+
+       return 0;
+}
+
+static void dun_gprs_remove(struct ofono_gprs *gprs)
+{
+       DBG("");
+}
+
+static void dun_gprs_attached_status(struct ofono_gprs *gprs,
+                                               ofono_gprs_status_cb_t cb,
+                                               void *data)
+{
+       DBG("");
+
+       CALLBACK_WITH_SUCCESS(cb, 1, data);
+}
+
+static struct ofono_gprs_driver driver = {
+       .name                   = "dunmodem",
+       .probe                  = dun_gprs_probe,
+       .remove                 = dun_gprs_remove,
+       .set_attached           = dun_gprs_set_attached,
+       .attached_status        = dun_gprs_attached_status,
+};
+
+void dun_gprs_init(void)
+{
+       ofono_gprs_driver_register(&driver);
+}
+
+void dun_gprs_exit(void)
+{
+       ofono_gprs_driver_unregister(&driver);
+}
diff --git a/drivers/dunmodem/network-registration.c b/drivers/dunmodem/network-registration.c
new file mode 100644 (file)
index 0000000..df24b5c
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gatresult.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/netreg.h>
+
+#include "common.h"
+#include "dunmodem.h"
+
+static const char *cops_prefix[] = { "+COPS:", NULL };
+
+struct netreg_data {
+       GAtChat *chat;
+};
+
+static void dun_registration_status(struct ofono_netreg *netreg,
+                               ofono_netreg_status_cb_t cb, void *data)
+{
+       int status = NETWORK_REGISTRATION_STATUS_REGISTERED;
+
+       DBG("");
+
+       CALLBACK_WITH_SUCCESS(cb, status, -1, -1, -1, data);
+}
+
+static void dun_current_operator(struct ofono_netreg *netreg,
+                               ofono_netreg_operator_cb_t cb, void *data)
+{
+       struct ofono_network_operator op;
+
+       DBG("");
+
+       op.name[0] = '\0';
+       op.mcc[0] = '\0';
+       op.mnc[0] = '\0';
+       op.status = 2;
+       op.tech = -1;
+
+       CALLBACK_WITH_SUCCESS(cb, &op, data);
+}
+
+static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+
+       if (!ok)
+               return;
+
+       ofono_netreg_register(netreg);
+}
+
+static int dun_netreg_probe(struct ofono_netreg *netreg,
+                               unsigned int vendor, void *user_data)
+{
+       GAtChat *chat = user_data;
+       struct netreg_data *nd;
+
+       nd = g_try_new0(struct netreg_data, 1);
+       if (nd == NULL)
+               return -ENOMEM;
+
+       nd->chat = g_at_chat_clone(chat);
+
+       ofono_netreg_set_data(netreg, nd);
+
+       g_at_chat_send(nd->chat, "AT+COPS=0", cops_prefix,
+                                       cops_cb, netreg, NULL);
+
+       return 0;
+}
+
+static void dun_netreg_remove(struct ofono_netreg *netreg)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       ofono_netreg_set_data(netreg, NULL);
+
+       g_free(nd);
+}
+
+static struct ofono_netreg_driver driver = {
+       .name                   = "dunmodem",
+       .probe                  = dun_netreg_probe,
+       .remove                 = dun_netreg_remove,
+       .registration_status    = dun_registration_status,
+       .current_operator       = dun_current_operator,
+};
+
+void dun_netreg_init(void)
+{
+       ofono_netreg_driver_register(&driver);
+}
+
+void dun_netreg_exit(void)
+{
+       ofono_netreg_driver_unregister(&driver);
+}
diff --git a/drivers/hfpmodem/call-volume.c b/drivers/hfpmodem/call-volume.c
new file mode 100644 (file)
index 0000000..0d7431e
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gatresult.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-volume.h>
+
+#include "hfpmodem.h"
+#include "slc.h"
+
+#define HFP_CALL_VOLUME_MAX 15
+
+static const char *vgs_prefix[] = { "+VGS:", NULL };
+static const char *vgm_prefix[] = { "+VGM:", NULL };
+
+struct cv_data {
+       GAtChat *chat;
+       unsigned char sp_volume;
+       unsigned char mic_volume;
+       guint register_source;
+};
+
+static void cv_generic_set_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_call_volume_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void hfp_speaker_volume(struct ofono_call_volume *cv,
+                                       unsigned char percent,
+                                       ofono_call_volume_cb_t cb,
+                                       void *data)
+{
+       struct cv_data *vd = ofono_call_volume_get_data(data);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       vd->sp_volume = percent;
+
+       snprintf(buf, sizeof(buf), "AT+VGS=%d",
+                               (int)(percent*HFP_CALL_VOLUME_MAX/100));
+
+       if (g_at_chat_send(vd->chat, buf, vgs_prefix,
+                               cv_generic_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_microphone_volume(struct ofono_call_volume *cv,
+                                       unsigned char percent,
+                                       ofono_call_volume_cb_t cb,
+                                       void *data)
+{
+       struct cv_data *vd = ofono_call_volume_get_data(data);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       vd->mic_volume = percent;
+
+       snprintf(buf, sizeof(buf), "AT+VGM=%d",
+                               (int)(percent*HFP_CALL_VOLUME_MAX/100));
+
+       if (g_at_chat_send(vd->chat, buf, vgm_prefix,
+                               cv_generic_set_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void vgs_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       struct cv_data *vd = ofono_call_volume_get_data(cv);
+       GAtResultIter iter;
+       gint value;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+VGS:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &value))
+               return;
+
+       vd->sp_volume = (unsigned char)(value*100/HFP_CALL_VOLUME_MAX);
+       ofono_call_volume_set_speaker_volume(cv, vd->sp_volume);
+}
+
+static void vgm_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       struct cv_data *vd = ofono_call_volume_get_data(cv);
+       GAtResultIter iter;
+       gint value;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+VGM:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &value))
+               return;
+
+       vd->mic_volume = (unsigned char)(value*100/HFP_CALL_VOLUME_MAX);
+       ofono_call_volume_set_microphone_volume(cv, vd->mic_volume);
+}
+
+static void sync_speaker_volume_cb(const struct ofono_error *error,
+                                       void *user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       struct cv_data *vd = ofono_call_volume_get_data(cv);
+
+       ofono_call_volume_set_speaker_volume(cv, vd->sp_volume);
+}
+
+static void sync_microphone_volume_cb(const struct ofono_error *error,
+                                       void *user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       struct cv_data *vd = ofono_call_volume_get_data(cv);
+
+       ofono_call_volume_set_microphone_volume(cv, vd->mic_volume);
+}
+
+static gboolean hfp_call_volume_register(gpointer user_data)
+{
+       struct ofono_call_volume *cv = user_data;
+       struct cv_data *vd = ofono_call_volume_get_data(cv);
+
+       DBG("");
+
+       vd->register_source = 0;
+
+       g_at_chat_register(vd->chat, "+VGS:", vgs_notify, FALSE, cv, NULL);
+       g_at_chat_register(vd->chat, "+VGM:", vgm_notify, FALSE, cv, NULL);
+
+       /* set sp and mic volume at 50 percents by default */
+       hfp_speaker_volume(cv, 50, sync_speaker_volume_cb, cv);
+       hfp_microphone_volume(cv, 50, sync_microphone_volume_cb, cv);
+
+       ofono_call_volume_register(cv);
+
+       return FALSE;
+}
+
+static int hfp_call_volume_probe(struct ofono_call_volume *cv,
+                                       unsigned int vendor, void *data)
+{
+       struct hfp_slc_info *info = data;
+       struct cv_data *vd;
+
+       DBG("");
+       vd = g_new0(struct cv_data, 1);
+       vd->chat = g_at_chat_clone(info->chat);
+
+       ofono_call_volume_set_data(cv, vd);
+
+       vd->register_source = g_idle_add(hfp_call_volume_register, cv);
+
+       return 0;
+}
+
+static void hfp_call_volume_remove(struct ofono_call_volume *cv)
+{
+       struct cv_data *vd = ofono_call_volume_get_data(cv);
+
+       if (vd->register_source != 0)
+               g_source_remove(vd->register_source);
+
+       ofono_call_volume_set_data(cv, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_call_volume_driver driver = {
+       .name                   = "hfpmodem",
+       .probe                  = hfp_call_volume_probe,
+       .remove                 = hfp_call_volume_remove,
+       .speaker_volume         = hfp_speaker_volume,
+       .microphone_volume      = hfp_microphone_volume,
+       .mute                   = NULL,
+};
+
+void hfp_call_volume_init(void)
+{
+       ofono_call_volume_driver_register(&driver);
+}
+
+void hfp_call_volume_exit(void)
+{
+       ofono_call_volume_driver_unregister(&driver);
+}
diff --git a/drivers/hfpmodem/devinfo.c b/drivers/hfpmodem/devinfo.c
new file mode 100644 (file)
index 0000000..04929c9
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gatresult.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+
+#include "hfpmodem.h"
+
+struct devinfo_data {
+       char *device_address;
+       guint register_source;
+};
+
+static void hfp_query_serial(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb,
+                               void *data)
+{
+       struct devinfo_data *dev = ofono_devinfo_get_data(info);
+       CALLBACK_WITH_SUCCESS(cb, dev->device_address, data);
+}
+
+static gboolean hfp_devinfo_register(gpointer user_data)
+{
+       struct ofono_devinfo *info = user_data;
+       struct devinfo_data *dd = ofono_devinfo_get_data(info);
+
+       dd->register_source = 0;
+
+       ofono_devinfo_register(info);
+
+       return FALSE;
+}
+
+static int hfp_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
+                               void *user)
+{
+       const char *device_address = user;
+       struct devinfo_data *dd;
+
+       dd = g_new0(struct devinfo_data, 1);
+       dd->device_address = g_strdup(device_address);
+
+       ofono_devinfo_set_data(info, dd);
+
+       dd->register_source = g_idle_add(hfp_devinfo_register, info);
+       return 0;
+}
+
+static void hfp_devinfo_remove(struct ofono_devinfo *info)
+{
+       struct devinfo_data *dd = ofono_devinfo_get_data(info);
+
+       ofono_devinfo_set_data(info, NULL);
+       if (dd == NULL)
+               return;
+
+       if (dd->register_source != 0)
+               g_source_remove(dd->register_source);
+
+       g_free(dd->device_address);
+       g_free(dd);
+}
+
+static struct ofono_devinfo_driver driver = {
+       .name                   = "hfpmodem",
+       .probe                  = hfp_devinfo_probe,
+       .remove                 = hfp_devinfo_remove,
+       .query_serial           = hfp_query_serial
+};
+
+void hfp_devinfo_init(void)
+{
+       ofono_devinfo_driver_register(&driver);
+}
+
+void hfp_devinfo_exit(void)
+{
+       ofono_devinfo_driver_unregister(&driver);
+}
diff --git a/drivers/hfpmodem/handsfree.c b/drivers/hfpmodem/handsfree.c
new file mode 100644 (file)
index 0000000..abe2ad2
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gatresult.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/handsfree.h>
+
+#include "hfpmodem.h"
+#include "slc.h"
+
+static const char *binp_prefix[] = { "+BINP:", NULL };
+static const char *bvra_prefix[] = { "+BVRA:", NULL };
+
+struct hf_data {
+       GAtChat *chat;
+       unsigned int ag_features;
+       guint register_source;
+};
+
+static void hf_generic_set_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_handsfree_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void bsir_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_handsfree *hf = user_data;
+       GAtResultIter iter;
+       int value;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+BSIR:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &value))
+               return;
+
+       ofono_handsfree_set_inband_ringing(hf, (ofono_bool_t) value);
+}
+
+static void bvra_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_handsfree *hf = user_data;
+       GAtResultIter iter;
+       int value;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+BVRA:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &value))
+               return;
+
+       ofono_handsfree_voice_recognition_notify(hf, (ofono_bool_t) value);
+}
+
+static gboolean hfp_handsfree_register(gpointer user_data)
+{
+       struct ofono_handsfree *hf = user_data;
+       struct hf_data *hd = ofono_handsfree_get_data(hf);
+
+       hd->register_source = 0;
+
+       g_at_chat_register(hd->chat, "+BSIR:", bsir_notify, FALSE, hf, NULL);
+       g_at_chat_register(hd->chat, "+BVRA:", bvra_notify, FALSE, hf, NULL);
+
+       if (hd->ag_features & HFP_AG_FEATURE_IN_BAND_RING_TONE)
+               ofono_handsfree_set_inband_ringing(hf, TRUE);
+
+       ofono_handsfree_set_ag_features(hf, hd->ag_features);
+       ofono_handsfree_register(hf);
+
+       return FALSE;
+}
+
+static int hfp_handsfree_probe(struct ofono_handsfree *hf,
+                               unsigned int vendor, void *data)
+{
+       struct hfp_slc_info *info = data;
+       struct hf_data *hd;
+
+       DBG("");
+       hd = g_new0(struct hf_data, 1);
+       hd->chat = g_at_chat_clone(info->chat);
+       hd->ag_features = info->ag_features;
+
+       ofono_handsfree_set_data(hf, hd);
+
+       hd->register_source = g_idle_add(hfp_handsfree_register, hf);
+
+       return 0;
+}
+
+static void hfp_handsfree_remove(struct ofono_handsfree *hf)
+{
+       struct hf_data *hd = ofono_handsfree_get_data(hf);
+
+       if (hd->register_source != 0)
+               g_source_remove(hd->register_source);
+
+       ofono_handsfree_set_data(hf, NULL);
+
+       g_at_chat_unref(hd->chat);
+       g_free(hd);
+}
+
+static void hfp_request_phone_number_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_handsfree_phone_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       struct ofono_error error;
+       const char *num;
+       int type;
+       struct ofono_phone_number phone_number;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+BINP:"))
+               goto fail;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               goto fail;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               goto fail;
+
+       DBG("AT+BINP=1 response: %s %d", num, type);
+
+       strncpy(phone_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       phone_number.type = type;
+
+       cb(&error, &phone_number, cbd->data);
+       return;
+
+fail:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void hfp_request_phone_number(struct ofono_handsfree *hf,
+                                       ofono_handsfree_phone_cb_t cb,
+                                       void *data)
+{
+       struct hf_data *hd = ofono_handsfree_get_data(hf);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(hd->chat, "AT+BINP=1", binp_prefix,
+                               hfp_request_phone_number_cb,
+                               cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void hfp_voice_recognition(struct ofono_handsfree *hf,
+                                       ofono_bool_t enabled,
+                                       ofono_handsfree_cb_t cb, void *data)
+{
+       struct hf_data *hd = ofono_handsfree_get_data(hf);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       snprintf(buf, sizeof(buf), "AT+BVRA=%d",
+                               (int)(enabled));
+
+       if (g_at_chat_send(hd->chat, buf, bvra_prefix,
+                               hf_generic_set_cb,
+                               cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static struct ofono_handsfree_driver driver = {
+       .name                   = "hfpmodem",
+       .probe                  = hfp_handsfree_probe,
+       .remove                 = hfp_handsfree_remove,
+       .request_phone_number   = hfp_request_phone_number,
+       .voice_recognition      = hfp_voice_recognition,
+};
+
+void hfp_handsfree_init(void)
+{
+       ofono_handsfree_driver_register(&driver);
+}
+
+void hfp_handsfree_exit(void)
+{
+       ofono_handsfree_driver_unregister(&driver);
+}
diff --git a/drivers/hfpmodem/hfpmodem.c b/drivers/hfpmodem/hfpmodem.c
new file mode 100644 (file)
index 0000000..1348beb
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+
+#include <glib.h>
+#include <gatchat.h>
+
+#include "hfpmodem.h"
+
+static int hfpmodem_init(void)
+{
+       hfp_voicecall_init();
+       hfp_devinfo_init();
+       hfp_netreg_init();
+       hfp_call_volume_init();
+       hfp_handsfree_init();
+
+       return 0;
+}
+
+static void hfpmodem_exit(void)
+{
+       hfp_voicecall_exit();
+       hfp_devinfo_exit();
+       hfp_netreg_exit();
+       hfp_call_volume_exit();
+       hfp_handsfree_exit();
+}
+
+OFONO_PLUGIN_DEFINE(hfpmodem, "Hands-Free Profile Driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, hfpmodem_init, hfpmodem_exit)
diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h
new file mode 100644 (file)
index 0000000..ef7aea5
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+#include <ofono/dbus.h>
+
+extern void hfp_netreg_init(void);
+extern void hfp_netreg_exit(void);
+
+extern void hfp_call_volume_init(void);
+extern void hfp_call_volume_exit(void);
+
+extern void hfp_voicecall_init(void);
+extern void hfp_voicecall_exit(void);
+
+extern void hfp_handsfree_init(void);
+extern void hfp_handsfree_exit(void);
+
+extern void hfp_devinfo_init(void);
+extern void hfp_devinfo_exit(void);
diff --git a/drivers/hfpmodem/network-registration.c b/drivers/hfpmodem/network-registration.c
new file mode 100644 (file)
index 0000000..4b9f875
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009  ProFUSION embedded systems. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gatresult.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/netreg.h>
+
+#include "common.h"
+
+#include "hfpmodem.h"
+#include "slc.h"
+
+#define HFP_MAX_OPERATOR_NAME_LENGTH 16
+
+static const char *cops_prefix[] = { "+COPS:", NULL };
+static const char *cind_prefix[] = { "+CIND:", NULL };
+
+struct netreg_data {
+       GAtChat *chat;
+       unsigned char cind_pos[HFP_INDICATOR_LAST];
+       int cind_val[HFP_INDICATOR_LAST];
+       guint register_source;
+};
+
+static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_operator_cb_t cb = cbd->cb;
+       struct ofono_network_operator op;
+       GAtResultIter iter;
+       int format;
+       const char *name;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, NULL, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+COPS:"))
+               goto error;
+
+       g_at_result_iter_skip_next(&iter);
+
+       ok = g_at_result_iter_next_number(&iter, &format);
+
+       if (ok == FALSE || format != 0)
+               goto error;
+
+       if (g_at_result_iter_next_string(&iter, &name) == FALSE)
+               goto error;
+
+       strncpy(op.name, name, HFP_MAX_OPERATOR_NAME_LENGTH);
+       op.name[HFP_MAX_OPERATOR_NAME_LENGTH] = '\0';
+
+       op.mcc[0] = '\0';
+       op.mnc[0] = '\0';
+       op.status = 2;
+       op.tech = -1;
+
+       cb(&error, &op, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void ciev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GAtResultIter iter;
+       int index, value, status;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIEV:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &index))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &value))
+               return;
+
+       if (index == nd->cind_pos[HFP_INDICATOR_SERVICE]) {
+               nd->cind_val[HFP_INDICATOR_SERVICE] = value;
+               if (value)
+                       status = NETWORK_REGISTRATION_STATUS_REGISTERED;
+               else
+                       status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+
+               ofono_netreg_status_notify(netreg, status, -1, -1, -1);
+       } else if (index == nd->cind_pos[HFP_INDICATOR_ROAM]) {
+               nd->cind_val[HFP_INDICATOR_ROAM] = value;
+
+               if (value)
+                       status = NETWORK_REGISTRATION_STATUS_ROAMING;
+               else if (nd->cind_val[HFP_INDICATOR_SERVICE])
+                       status = NETWORK_REGISTRATION_STATUS_REGISTERED;
+               else
+                       status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+
+               ofono_netreg_status_notify(netreg, status, -1, -1, -1);
+       } else if (index == nd->cind_pos[HFP_INDICATOR_SIGNAL]) {
+               nd->cind_val[HFP_INDICATOR_SIGNAL] = value;
+               ofono_netreg_strength_notify(netreg, value * 20);
+       }
+
+       return;
+}
+
+static void signal_strength_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_strength_cb_t cb = cbd->cb;
+       struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
+       GAtResultIter iter;
+       int index, strength;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIND:")) {
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       index = 1;
+
+       while (g_at_result_iter_next_number(&iter, &strength)) {
+               if (index == nd->cind_pos[HFP_INDICATOR_SIGNAL]) {
+                       nd->cind_val[HFP_INDICATOR_SIGNAL] = strength;
+                       break;
+               }
+
+               index++;
+       }
+
+       DBG("signal_strength_cb: %d", strength);
+
+       cb(&error, strength * 20, cbd->data);
+}
+
+static void registration_status_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_netreg_status_cb_t cb = cbd->cb;
+       struct netreg_data *nd = ofono_netreg_get_data(cbd->user);
+       GAtResultIter iter;
+       int index, value, status;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, -1, -1, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIND:")) {
+               CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data);
+               return;
+       }
+
+       index = 1;
+
+       while (g_at_result_iter_next_number(&iter, &value)) {
+               if (index == nd->cind_pos[HFP_INDICATOR_SERVICE])
+                       nd->cind_val[HFP_INDICATOR_SERVICE] = value;
+
+               if (index == nd->cind_pos[HFP_INDICATOR_ROAM])
+                       nd->cind_val[HFP_INDICATOR_ROAM] = value;
+
+               index++;
+       }
+
+       if (nd->cind_val[HFP_INDICATOR_SERVICE])
+               status = NETWORK_REGISTRATION_STATUS_REGISTERED;
+       else
+               status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+
+       if (nd->cind_val[HFP_INDICATOR_ROAM])
+               status = NETWORK_REGISTRATION_STATUS_ROAMING;
+
+       cb(&error, status, -1, -1, -1, cbd->data);
+}
+
+static void hfp_registration_status(struct ofono_netreg *netreg,
+                                       ofono_netreg_status_cb_t cb,
+                                       void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       gboolean ok;
+
+       cbd->user = netreg;
+
+       ok = g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix,
+                               registration_status_cb, cbd, g_free);
+       if (ok)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
+}
+
+static void hfp_current_operator(struct ofono_netreg *netreg,
+                               ofono_netreg_operator_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       gboolean ok;
+
+       cbd->user = netreg;
+
+       ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", NULL,
+                       NULL, cbd, NULL);
+
+       if (ok)
+               ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix,
+                               cops_cb, cbd, g_free);
+
+       if (ok)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+}
+
+static void hfp_signal_strength(struct ofono_netreg *netreg,
+                               ofono_netreg_strength_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       cbd->user = netreg;
+
+       if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix,
+                               signal_strength_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static gboolean hfp_netreg_register(gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       nd->register_source = 0;
+
+       ofono_netreg_register(netreg);
+
+       return FALSE;
+}
+
+static int hfp_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
+                               void *user_data)
+{
+       struct hfp_slc_info *info = user_data;
+       struct netreg_data *nd;
+
+       nd = g_new0(struct netreg_data, 1);
+
+       nd->chat = g_at_chat_clone(info->chat);
+       memcpy(nd->cind_pos, info->cind_pos, HFP_INDICATOR_LAST);
+       memcpy(nd->cind_val, info->cind_val, HFP_INDICATOR_LAST);
+
+       ofono_netreg_set_data(netreg, nd);
+
+       g_at_chat_register(nd->chat, "+CIEV:", ciev_notify, FALSE,
+                               netreg, NULL);
+
+       nd->register_source = g_idle_add(hfp_netreg_register, netreg);
+
+       return 0;
+}
+
+static void hfp_netreg_remove(struct ofono_netreg *netreg)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (nd->register_source != 0)
+               g_source_remove(nd->register_source);
+
+       ofono_netreg_set_data(netreg, NULL);
+
+       g_at_chat_unref(nd->chat);
+       g_free(nd);
+}
+
+static struct ofono_netreg_driver driver = {
+       .name                           = "hfpmodem",
+       .probe                          = hfp_netreg_probe,
+       .remove                         = hfp_netreg_remove,
+       .registration_status            = hfp_registration_status,
+       .current_operator               = hfp_current_operator,
+       .strength                       = hfp_signal_strength,
+};
+
+void hfp_netreg_init(void)
+{
+       ofono_netreg_driver_register(&driver);
+}
+
+void hfp_netreg_exit(void)
+{
+       ofono_netreg_driver_unregister(&driver);
+}
diff --git a/drivers/hfpmodem/slc.c b/drivers/hfpmodem/slc.c
new file mode 100644 (file)
index 0000000..4028479
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <gatchat.h>
+#include <gatresult.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/emulator.h>
+
+#include <drivers/atmodem/atutil.h>
+
+#include "slc.h"
+
+static const char *brsf_prefix[] = { "+BRSF:", NULL };
+static const char *cind_prefix[] = { "+CIND:", NULL };
+static const char *cmer_prefix[] = { "+CMER:", NULL };
+static const char *chld_prefix[] = { "+CHLD:", NULL };
+
+struct slc_establish_data {
+       gint ref_count;
+       struct hfp_slc_info *info;
+       hfp_slc_cb_t failed_cb;
+       hfp_slc_cb_t connect_cb;
+       gpointer userdata;
+};
+
+void hfp_slc_info_init(struct hfp_slc_info *info, guint16 version)
+{
+       info->ag_features = 0;
+       info->ag_mpty_features = 0;
+
+       info->hf_features = HFP_HF_FEATURE_3WAY;
+       info->hf_features |= HFP_HF_FEATURE_CLIP;
+       info->hf_features |= HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL;
+
+       if (version < HFP_VERSION_1_5)
+               goto done;
+
+       info->hf_features |= HFP_HF_FEATURE_ENHANCED_CALL_STATUS;
+       info->hf_features |= HFP_HF_FEATURE_ENHANCED_CALL_CONTROL;
+
+done:
+       memset(info->cind_val, 0, sizeof(info->cind_val));
+       memset(info->cind_pos, 0, sizeof(info->cind_pos));
+}
+
+static void slc_establish_data_unref(gpointer userdata)
+{
+       struct slc_establish_data *sed = userdata;
+
+       if (g_atomic_int_dec_and_test(&sed->ref_count))
+               g_free(sed);
+}
+
+static void slc_establish_data_ref(struct slc_establish_data *sed)
+{
+       g_atomic_int_inc(&sed->ref_count);
+}
+
+static void slc_failed(struct slc_establish_data *sed)
+{
+       sed->failed_cb(sed->userdata);
+}
+
+static void slc_established(struct slc_establish_data *sed)
+{
+       struct hfp_slc_info *info = sed->info;
+
+       g_at_chat_send(info->chat, "AT+CMEE=1", NULL, NULL, NULL, NULL);
+       sed->connect_cb(sed->userdata);
+}
+
+static void chld_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct slc_establish_data *sed = user_data;
+       struct hfp_slc_info *info = sed->info;
+       unsigned int ag_mpty_feature = 0;
+       GAtResultIter iter;
+       const char *str;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CHLD:"))
+               goto error;
+
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       while (g_at_result_iter_next_unquoted_string(&iter, &str)) {
+               if (!strcmp(str, "0"))
+                       ag_mpty_feature |= AG_CHLD_0;
+               else if (!strcmp(str, "1"))
+                       ag_mpty_feature |= AG_CHLD_1;
+               else if (!strcmp(str, "1x"))
+                       ag_mpty_feature |= AG_CHLD_1x;
+               else if (!strcmp(str, "2"))
+                       ag_mpty_feature |= AG_CHLD_2;
+               else if (!strcmp(str, "2x"))
+                       ag_mpty_feature |= AG_CHLD_2x;
+               else if (!strcmp(str, "3"))
+                       ag_mpty_feature |= AG_CHLD_3;
+               else if (!strcmp(str, "4"))
+                       ag_mpty_feature |= AG_CHLD_4;
+       }
+
+       if (!g_at_result_iter_close_list(&iter))
+               goto error;
+
+       info->ag_mpty_features = ag_mpty_feature;
+
+       slc_established(sed);
+       return;
+
+error:
+       slc_failed(sed);
+}
+
+static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct slc_establish_data *sed = user_data;
+       struct hfp_slc_info *info = sed->info;
+
+       if (!ok) {
+               slc_failed(sed);
+               return;
+       }
+
+       if (info->ag_features & HFP_AG_FEATURE_3WAY) {
+               slc_establish_data_ref(sed);
+               g_at_chat_send(info->chat, "AT+CHLD=?", chld_prefix,
+                               chld_cb, sed, slc_establish_data_unref);
+       } else
+               slc_established(sed);
+}
+
+static void cind_status_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct slc_establish_data *sed = user_data;
+       struct hfp_slc_info *info = sed->info;
+       GAtResultIter iter;
+       int index;
+       int value;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIND:"))
+               goto error;
+
+       index = 1;
+
+       while (g_at_result_iter_next_number(&iter, &value)) {
+               int i;
+
+               for (i = 0; i < HFP_INDICATOR_LAST; i++) {
+                       if (index != info->cind_pos[i])
+                               continue;
+
+                       info->cind_val[i] = value;
+               }
+
+               index += 1;
+       }
+
+       slc_establish_data_ref(sed);
+       g_at_chat_send(info->chat, "AT+CMER=3,0,0,1", cmer_prefix,
+                               cmer_cb, sed, slc_establish_data_unref);
+       return;
+
+error:
+       slc_failed(sed);
+}
+
+static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct slc_establish_data *sed = user_data;
+       struct hfp_slc_info *info = sed->info;
+       GAtResultIter iter;
+       const char *str;
+       int index;
+       int min, max;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+       if (!g_at_result_iter_next(&iter, "+CIND:"))
+               goto error;
+
+       index = 1;
+
+       while (g_at_result_iter_open_list(&iter)) {
+               if (!g_at_result_iter_next_string(&iter, &str))
+                       goto error;
+
+               if (!g_at_result_iter_open_list(&iter))
+                       goto error;
+
+               while (g_at_result_iter_next_range(&iter, &min, &max))
+                       ;
+
+               if (!g_at_result_iter_close_list(&iter))
+                       goto error;
+
+               if (!g_at_result_iter_close_list(&iter))
+                       goto error;
+
+               if (g_str_equal("service", str) == TRUE)
+                       info->cind_pos[HFP_INDICATOR_SERVICE] = index;
+               else if (g_str_equal("call", str) == TRUE)
+                       info->cind_pos[HFP_INDICATOR_CALL] = index;
+               else if (g_str_equal("callsetup", str) == TRUE)
+                       info->cind_pos[HFP_INDICATOR_CALLSETUP] = index;
+               else if (g_str_equal("callheld", str) == TRUE)
+                       info->cind_pos[HFP_INDICATOR_CALLHELD] = index;
+               else if (g_str_equal("signal", str) == TRUE)
+                       info->cind_pos[HFP_INDICATOR_SIGNAL] = index;
+               else if (g_str_equal("roam", str) == TRUE)
+                       info->cind_pos[HFP_INDICATOR_ROAM] = index;
+               else if (g_str_equal("battchg", str) == TRUE)
+                       info->cind_pos[HFP_INDICATOR_BATTCHG] = index;
+
+               index += 1;
+       }
+
+       slc_establish_data_ref(sed);
+       g_at_chat_send(info->chat, "AT+CIND?", cind_prefix,
+                       cind_status_cb, sed, slc_establish_data_unref);
+       return;
+
+error:
+       slc_failed(sed);
+}
+
+static void brsf_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct slc_establish_data *sed = user_data;
+       struct hfp_slc_info *info = sed->info;
+       GAtResultIter iter;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+BRSF:"))
+               goto error;
+
+       g_at_result_iter_next_number(&iter, (gint *)&info->ag_features);
+
+       slc_establish_data_ref(sed);
+       g_at_chat_send(info->chat, "AT+CIND=?", cind_prefix,
+                               cind_cb, sed, slc_establish_data_unref);
+       return;
+
+error:
+       slc_failed(sed);
+}
+
+void hfp_slc_establish(struct hfp_slc_info *info, hfp_slc_cb_t connect_cb,
+                       hfp_slc_cb_t failed_cb, void *userdata)
+{
+       char buf[64];
+       struct slc_establish_data *sed = g_new0(struct slc_establish_data, 1);
+
+       sed->ref_count = 1;
+       sed->connect_cb = connect_cb;
+       sed->failed_cb = failed_cb;
+       sed->userdata = userdata;
+       sed->info = info;
+
+       snprintf(buf, sizeof(buf), "AT+BRSF=%d", info->hf_features);
+       g_at_chat_send(info->chat, buf, brsf_prefix,
+                               brsf_cb, sed, slc_establish_data_unref);
+}
diff --git a/drivers/hfpmodem/slc.h b/drivers/hfpmodem/slc.h
new file mode 100644 (file)
index 0000000..a9f42f7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AG_CHLD_0      0x01
+#define AG_CHLD_1      0x02
+#define AG_CHLD_1x     0x04
+#define AG_CHLD_2      0x08
+#define AG_CHLD_2x     0x10
+#define AG_CHLD_3      0x20
+#define AG_CHLD_4      0x40
+
+enum hfp_version {
+       HFP_VERSION_1_5 =       0x0105,
+       HFP_VERSION_LATEST =    HFP_VERSION_1_5,
+};
+
+enum hfp_indicator {
+       HFP_INDICATOR_SERVICE = 0,
+       HFP_INDICATOR_CALL,
+       HFP_INDICATOR_CALLSETUP,
+       HFP_INDICATOR_CALLHELD,
+       HFP_INDICATOR_SIGNAL,
+       HFP_INDICATOR_ROAM,
+       HFP_INDICATOR_BATTCHG,
+       HFP_INDICATOR_LAST
+};
+
+typedef void (*hfp_slc_cb_t)(void *userdata);
+
+struct hfp_slc_info {
+       GAtChat *chat;
+       unsigned int ag_features;
+       unsigned int ag_mpty_features;
+       unsigned int hf_features;
+       unsigned char cind_pos[HFP_INDICATOR_LAST];
+       unsigned int cind_val[HFP_INDICATOR_LAST];
+};
+
+void hfp_slc_info_init(struct hfp_slc_info *info, guint16 version);
+void hfp_slc_info_free(struct hfp_slc_info *info);
+
+void hfp_slc_establish(struct hfp_slc_info *info, hfp_slc_cb_t connect_cb,
+                               hfp_slc_cb_t failed_cb, void *userdata);
diff --git a/drivers/hfpmodem/voicecall.c b/drivers/hfpmodem/voicecall.c
new file mode 100644 (file)
index 0000000..bcf4292
--- /dev/null
@@ -0,0 +1,1201 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gatresult.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "common.h"
+
+#include "hfpmodem.h"
+#include "slc.h"
+
+#define POLL_CLCC_INTERVAL 2000
+#define POLL_CLCC_DELAY 50
+#define CLIP_TIMEOUT 500
+
+static const char *none_prefix[] = { NULL };
+static const char *clcc_prefix[] = { "+CLCC:", NULL };
+
+struct voicecall_data {
+       GAtChat *chat;
+       GSList *calls;
+       unsigned int ag_features;
+       unsigned int ag_mpty_features;
+       unsigned char cind_pos[HFP_INDICATOR_LAST];
+       int cind_val[HFP_INDICATOR_LAST];
+       unsigned int local_release;
+       unsigned int clcc_source;
+       unsigned int clip_source;
+};
+
+struct release_id_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int id;
+};
+
+struct change_state_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int affected_types;
+};
+
+static gboolean poll_clcc(gpointer user_data);
+
+static GSList *find_dialing(GSList *calls)
+{
+       GSList *c;
+
+       c = g_slist_find_custom(calls, GINT_TO_POINTER(CALL_STATUS_DIALING),
+                               at_util_call_compare_by_status);
+
+       if (c == NULL)
+               c = g_slist_find_custom(calls,
+                                       GINT_TO_POINTER(CALL_STATUS_ALERTING),
+                                       at_util_call_compare_by_status);
+
+       return c;
+}
+
+static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
+                                       int direction, int status,
+                                       const char *num, int num_type, int clip)
+{
+       struct voicecall_data *d = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* Generate a call structure for the waiting call */
+       call = g_try_new(struct ofono_call, 1);
+       if (call == NULL)
+               return NULL;
+
+       ofono_call_init(call);
+
+       call->id = ofono_voicecall_get_next_callid(vc);
+       call->type = type;
+       call->direction = direction;
+       call->status = status;
+
+       if (clip != 2) {
+               strncpy(call->phone_number.number, num,
+                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = num_type;
+       }
+
+       d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
+
+       call->clip_validity = clip;
+
+       return call;
+}
+
+static struct ofono_call *new_call_notify(struct ofono_voicecall *vc, int type,
+                                       int direction, int status,
+                                       const char *num, int num_type, int clip)
+{
+       struct ofono_call *c;
+
+       c = create_call(vc, type, direction, status, num, num_type, clip);
+
+       ofono_voicecall_notify(vc, c);
+
+       return c;
+}
+
+static void release_call(struct ofono_voicecall *vc, struct ofono_call *call)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       enum ofono_disconnect_reason reason;
+
+       if (call == NULL)
+               return;
+
+       if (vd->local_release & (1 << call->id))
+               reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+       else
+               reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+
+       ofono_voicecall_disconnected(vc, call->id, reason, NULL);
+       vd->local_release &= ~(1 << call->id);
+
+       g_free(call);
+}
+
+static void release_all_calls(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *l;
+       struct ofono_call *call;
+
+       for (l = vd->calls; l; l = l->next) {
+               call = l->data;
+
+               release_call(vc, call);
+       }
+
+       g_slist_free(vd->calls);
+       vd->calls = NULL;
+}
+
+static void release_with_status(struct ofono_voicecall *vc, int status)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *p = NULL;
+       GSList *c = vd->calls;
+       GSList *t;
+       struct ofono_call *call;
+
+       while (c) {
+               call = c->data;
+
+               if (call->status != status) {
+                       p = c;
+                       c = c->next;
+                       continue;
+               }
+
+               release_call(vc, call);
+
+               if (p)
+                       p->next = c->next;
+               else
+                       vd->calls = c->next;
+
+               t = c;
+               c = c->next;
+               g_slist_free_1(t);
+       }
+}
+
+static void clcc_poll_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *calls;
+       GSList *n, *o;
+       struct ofono_call *nc, *oc;
+       unsigned int num_active = 0;
+       unsigned int num_held = 0;
+
+       if (!ok)
+               return;
+
+       calls = at_util_parse_clcc(result);
+
+       n = calls;
+       o = vd->calls;
+
+       while (n || o) {
+               nc = n ? n->data : NULL;
+               oc = o ? o->data : NULL;
+
+               if (nc && (nc->status == CALL_STATUS_ACTIVE))
+                       num_active++;
+
+               if (nc && (nc->status == CALL_STATUS_HELD))
+                       num_held++;
+
+               if (oc && (nc == NULL || (nc->id > oc->id))) {
+                       enum ofono_disconnect_reason reason;
+
+                       if (vd->local_release & (1 << oc->id))
+                               reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+                       else
+                               reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+
+                       if (!oc->type)
+                               ofono_voicecall_disconnected(vc, oc->id,
+                                                               reason, NULL);
+
+                       vd->local_release &= ~(1 << oc->id);
+
+                       o = o->next;
+               } else if (nc && (oc == NULL || (nc->id < oc->id))) {
+                       /* new call, signal it */
+                       if (nc->type == 0)
+                               ofono_voicecall_notify(vc, nc);
+
+                       n = n->next;
+               } else {
+                       /* Always use the clip_validity from old call
+                        * the only place this is truly told to us is
+                        * in the CLIP notify, the rest are fudged
+                        * anyway.  Useful when RING, CLIP is used,
+                        * and we're forced to use CLCC and clip_validity
+                        * is 1
+                        */
+                       nc->clip_validity = oc->clip_validity;
+
+                       if (memcmp(nc, oc, sizeof(struct ofono_call)) &&
+                                       !nc->type)
+                               ofono_voicecall_notify(vc, nc);
+
+                       n = n->next;
+                       o = o->next;
+               }
+       }
+
+       g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+       g_slist_free(vd->calls);
+
+       vd->calls = calls;
+
+       /* If either active/held call is more than 1, we are in mpty calls.
+        * we won't get indicator update if any of them is released by CHLD=1x.
+        * So we have to poll it.
+        */
+       if (num_active > 1 || num_held > 1)
+               vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL, poll_clcc,
+                                                       vc);
+}
+
+static gboolean poll_clcc(gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                               clcc_poll_cb, vc, NULL);
+
+       vd->clcc_source = 0;
+
+       return FALSE;
+}
+
+static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct change_state_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok && req->affected_types) {
+               GSList *l;
+               struct ofono_call *call;
+
+               for (l = vd->calls; l; l = l->next) {
+                       call = l->data;
+
+                       if (req->affected_types & (1 << call->status))
+                               vd->local_release |= (1 << call->id);
+               }
+       }
+
+       req->cb(&error, req->data);
+}
+
+static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_voicecall *vc = cbd->user;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       ofono_voicecall_cb_t cb = cbd->cb;
+       int type = 128;
+       int validity = 2;
+       struct ofono_error error;
+       struct ofono_call *call;
+       GSList *l;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto out;
+
+       /* On a success, make sure to put all active calls on hold */
+       for (l = vd->calls; l; l = l->next) {
+               call = l->data;
+
+               if (call->status != CALL_STATUS_ACTIVE)
+                       continue;
+
+               call->status = CALL_STATUS_HELD;
+               ofono_voicecall_notify(vc, call);
+       }
+
+       call = create_call(vc, 0, 0, CALL_STATUS_DIALING, NULL, type, validity);
+       if (call == NULL) {
+               ofono_error("Unable to allocate call, "
+                               "call tracking will fail!");
+               return;
+       }
+
+out:
+       cb(&error, cbd->data);
+}
+
+static void hfp_dial(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+                       void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[256];
+
+       cbd->user = vc;
+       if (ph->type == 145)
+               snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
+       else
+               snprintf(buf, sizeof(buf), "ATD%s", ph->number);
+
+       strcat(buf, ";");
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               atd_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_template(const char *cmd, struct ofono_voicecall *vc,
+                       GAtResultFunc result_cb, unsigned int affected_types,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->affected_types = affected_types;
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               result_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_answer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       hfp_template("ATA", vc, generic_cb, 0, cb, data);
+}
+
+static void hfp_hangup(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Hangup current active call */
+       hfp_template("AT+CHUP", vc, generic_cb, 0x1, cb, data);
+}
+
+static void hfp_hold_all_active(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       if (vd->ag_mpty_features & AG_CHLD_2) {
+               hfp_template("AT+CHLD=2", vc, generic_cb, 0, cb, data);
+               return;
+       }
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_release_all_held(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       unsigned int held_status = 1 << CALL_STATUS_HELD;
+
+       if (vd->ag_mpty_features & AG_CHLD_0) {
+               hfp_template("AT+CHLD=0", vc, generic_cb, held_status,
+                               cb, data);
+               return;
+       }
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_set_udub(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       unsigned int incoming_or_waiting =
+               (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
+
+       if (vd->ag_mpty_features & AG_CHLD_0) {
+               hfp_template("AT+CHLD=0", vc, generic_cb, incoming_or_waiting,
+                               cb, data);
+               return;
+       }
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_release_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       if (vd->ag_mpty_features & AG_CHLD_1) {
+               hfp_template("AT+CHLD=1", vc, generic_cb, 0x1, cb, data);
+               return;
+       }
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void release_id_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct release_id_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok)
+               vd->local_release |= (1 << req->id);
+
+       req->cb(&error, req->data);
+}
+
+static void hfp_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct release_id_req *req = NULL;
+       char buf[32];
+
+       if (!(vd->ag_mpty_features & AG_CHLD_1x))
+               goto error;
+
+       req = g_try_new0(struct release_id_req, 1);
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->id = id;
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id);
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               release_id_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_private_chat(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       char buf[32];
+
+       if (vd->ag_mpty_features & AG_CHLD_2x) {
+               snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id);
+
+               hfp_template(buf, vc, generic_cb, 0, cb, data);
+
+               return;
+       }
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_create_multiparty(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       if (vd->ag_mpty_features & AG_CHLD_3) {
+               hfp_template("AT+CHLD=3", vc, generic_cb, 0, cb, data);
+
+               return;
+       }
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_transfer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       /* Transfer can puts held & active calls together and disconnects
+        * from both.  However, some networks support transferring of
+        * dialing/ringing calls as well.
+        */
+       unsigned int transfer = 0x1 | 0x2 | 0x4 | 0x8;
+
+       if (vd->ag_mpty_features & AG_CHLD_4) {
+               hfp_template("AT+CHLD=4", vc, generic_cb, transfer, cb, data);
+
+               return;
+       }
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hfp_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+       char *buf;
+       int s;
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->affected_types = 0;
+
+       /* strlen("AT+VTS=) = 7 + NULL */
+       buf = g_try_new(char, strlen(dtmf) + 8);
+       if (buf == NULL)
+               goto error;
+
+       sprintf(buf, "AT+VTS=%s", dtmf);
+
+       s = g_at_chat_send(vd->chat, buf, none_prefix,
+                               generic_cb, req, g_free);
+
+       g_free(buf);
+
+       if (s > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void no_carrier_notify(GAtResult *result, gpointer user_data)
+{
+       DBG("");
+}
+
+static void ccwa_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int num_type, validity;
+       struct ofono_call *call;
+
+       /* CCWA can repeat, ignore if we already have an waiting call */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CCWA:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &num_type))
+               return;
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       DBG("ccwa_notify: %s %d %d", num, num_type, validity);
+
+       call = create_call(vc, 0, 1, CALL_STATUS_WAITING, num, num_type,
+                               validity);
+
+       if (call == NULL) {
+               ofono_error("malloc call struct failed.  "
+                               "Call management is fubar");
+               return;
+       }
+
+       ofono_voicecall_notify(vc, call);
+}
+
+static gboolean clip_timeout(gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+
+       if (l == NULL)
+               return FALSE;
+
+       call = l->data;
+
+       ofono_voicecall_notify(vc, call);
+
+       vd->clip_source = 0;
+
+       return FALSE;
+}
+
+static void ring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+       GSList *waiting;
+
+       /* RING can repeat, ignore if we already have an incoming call */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status))
+               return;
+
+       waiting = g_slist_find_custom(vd->calls,
+                                       GINT_TO_POINTER(CALL_STATUS_WAITING),
+                                       at_util_call_compare_by_status);
+
+       /* If we started receiving RINGS but have a waiting call, most
+        * likely all other calls were dropped and we just didn't get
+        * notified yet, drop all other calls and update the status to
+        * incoming
+        */
+       if (waiting) {
+               DBG("Triggering waiting -> incoming cleanup code");
+
+               vd->calls = g_slist_remove_link(vd->calls, waiting);
+               release_all_calls(vc);
+               vd->calls = waiting;
+
+               call = waiting->data;
+               call->status = CALL_STATUS_INCOMING;
+               ofono_voicecall_notify(vc, call);
+
+               return;
+       }
+
+       /* Generate an incoming call of voice type */
+       call = create_call(vc, 0, 1, CALL_STATUS_INCOMING, NULL, 128, 2);
+
+       if (call == NULL)
+               ofono_error("Couldn't create call, call management is fubar!");
+
+       /* We don't know the number must wait for CLIP to arrive before
+        * announcing the call. If timeout, we notify the call as it is.
+        */
+       vd->clip_source = g_timeout_add(CLIP_TIMEOUT, clip_timeout, vc);
+}
+
+static void clip_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int type, validity;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+
+       if (l == NULL) {
+               ofono_error("CLIP for unknown call");
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLIP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               return;
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       /* Skip subaddr, satype, alpha and validity */
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+
+       DBG("clip_notify: %s %d %d", num, type, validity);
+
+       call = l->data;
+
+       strncpy(call->phone_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->phone_number.type = type;
+       call->clip_validity = validity;
+
+       ofono_voicecall_notify(vc, call);
+
+       if (vd->clip_source) {
+               g_source_remove(vd->clip_source);
+               vd->clip_source = 0;
+       }
+}
+
+static void ciev_call_notify(struct ofono_voicecall *vc,
+                               unsigned int value)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       switch (value) {
+       case 0:
+               /* If call goes to 0, then we have no held or active calls
+                * in the system.  The waiting calls are promoted to incoming
+                * calls, dialing calls are kept.  This also handles the
+                * situation when dialing and waiting calls exist
+                */
+               release_with_status(vc, CALL_STATUS_HELD);
+               release_with_status(vc, CALL_STATUS_ACTIVE);
+
+               /* Promote waiting to incoming if it is the last call */
+               if (vd->calls && vd->calls->next == NULL) {
+                       call = vd->calls->data;
+
+                       if (call->status == CALL_STATUS_WAITING) {
+                               call->status = CALL_STATUS_INCOMING;
+                               ofono_voicecall_notify(vc, call);
+                       }
+               }
+
+               break;
+
+       case 1:
+       {
+               GSList *l;
+
+               /* In this case either dialing/alerting or the incoming call
+                * is promoted to active
+                */
+               for (l = vd->calls; l; l = l->next) {
+                       call = l->data;
+
+                       if (call->status == CALL_STATUS_DIALING ||
+                                       call->status == CALL_STATUS_ALERTING ||
+                                       call->status == CALL_STATUS_INCOMING) {
+                               call->status = CALL_STATUS_ACTIVE;
+                               ofono_voicecall_notify(vc, call);
+                       }
+               }
+
+               break;
+       }
+
+       default:
+               break;
+       }
+
+       vd->cind_val[HFP_INDICATOR_CALL] = value;
+}
+
+static void sync_dialing_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct ofono_error error;
+       GSList *calls;
+       GSList *o;
+       GSList *n;
+       struct ofono_call *oc;
+       struct ofono_call *nc;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               return;
+
+       calls = at_util_parse_clcc(result);
+
+       if (calls == NULL)
+               return;
+
+       /* Look for dialing or alerting calls on the new list */
+       n = find_dialing(calls);
+
+       /* Let us find if we have done the dial from HF by looking for
+        * existing dialing or alerting calls
+        */
+       o = find_dialing(vd->calls);
+
+       if (n == NULL && o) {
+               oc = o->data;
+               release_call(vc, oc);
+               vd->calls = g_slist_remove(vd->calls, oc);
+       } else if (n && o == NULL) {
+               nc = n->data;
+               new_call_notify(vc, nc->type, nc->direction, nc->status,
+                               nc->phone_number.number, nc->phone_number.type,
+                               nc->clip_validity);
+       } else if (n && o) {
+               oc = o->data;
+               nc = n->data;
+
+               memcpy(&oc->phone_number, &nc->phone_number,
+                               sizeof(struct ofono_phone_number));
+               oc->status = nc->status;
+               oc->clip_validity = nc->clip_validity;
+               ofono_voicecall_notify(vc, oc);
+       }
+
+       g_slist_foreach(calls, (GFunc) g_free, NULL);
+       g_slist_free(calls);
+}
+
+static void ciev_callsetup_notify(struct ofono_voicecall *vc,
+                                       unsigned int value)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       unsigned int ciev_call = vd->cind_val[HFP_INDICATOR_CALL];
+       unsigned int ciev_callheld = vd->cind_val[HFP_INDICATOR_CALLHELD];
+       GSList *dialing;
+       GSList *waiting;
+
+       dialing = find_dialing(vd->calls);
+
+       waiting = g_slist_find_custom(vd->calls,
+                                       GINT_TO_POINTER(CALL_STATUS_WAITING),
+                                       at_util_call_compare_by_status);
+
+       /* This is a truly bizarre case not covered at all by the specification
+        * (yes, they are complete idiots).  Here we assume the other side is
+        * semi sane and will send callsetup updates in case the dialing call
+        * connects or the call waiting drops.  In which case we must poll
+        */
+       if (waiting && dialing) {
+               g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                               clcc_poll_cb, vc, NULL);
+               goto out;
+       }
+
+       switch (value) {
+       case 0:
+               /* call=0 and callsetup=1: reject an incoming call
+                * call=0 and callsetup=2,3: interrupt an outgoing call
+                */
+               if (ciev_call == 0) {
+                       release_all_calls(vc);
+                       goto out;
+               }
+
+               /* If call=1 and no call is waiting or dialing, the call is
+                * active and we moved it to active state when call=1 arrived
+                */
+               if (waiting == NULL && dialing == NULL)
+                       goto out;
+
+               /*
+                * If call=1, in the waiting case we have to poll, since we
+                * have no idea whether a waiting call gave up or we accepted
+                * using release+accept or hold+accept
+                *
+                * If call=1, in the dialing + held case we have to poll as
+                * well, we have no idea whether the call connected, or released
+                */
+               if (waiting == NULL && ciev_callheld == 0) {
+                       struct ofono_call *call = dialing->data;
+
+                       /* We assume that the implementation follows closely
+                        * the sequence of events in Figure 4.21.  That is
+                        * call=1 arrives first, then callsetup=0
+                        */
+
+                       call->status = CALL_STATUS_ACTIVE;
+                       ofono_voicecall_notify(vc, call);
+               } else {
+                       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                                       clcc_poll_cb, vc, NULL);
+               }
+
+               break;
+
+       case 1:
+               /* Handled in RING/CCWA */
+               break;
+
+       case 2:
+               /* two cases of outgoing call: dial from HF or AG.
+                * from HF: query and sync the phone number.
+                * from AG: query and create call.
+                */
+               g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                               sync_dialing_cb, vc, NULL);
+               break;
+
+       case 3:
+       {
+               GSList *o = g_slist_find_custom(vd->calls,
+                                       GINT_TO_POINTER(CALL_STATUS_DIALING),
+                                       at_util_call_compare_by_status);
+
+               if (o) {
+                       struct ofono_call *call = o->data;
+
+                       call->status = CALL_STATUS_ALERTING;
+                       ofono_voicecall_notify(vc, call);
+               }
+
+               break;
+       }
+
+       default:
+               break;
+       }
+
+out:
+       vd->cind_val[HFP_INDICATOR_CALLSETUP] = value;
+}
+
+static void ciev_callheld_notify(struct ofono_voicecall *vc,
+                                       unsigned int value)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *l;
+       struct ofono_call *call;
+       unsigned int callheld = vd->cind_val[HFP_INDICATOR_CALLHELD];
+
+       switch (value) {
+       case 0:
+               /* We have to poll here, we have no idea whether the call was
+                * dropped using CHLD=0 or simply retrieved, or the two calls
+                * were merged
+                */
+               g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                               clcc_poll_cb, vc, NULL);
+               break;
+
+       case 1:
+               if (vd->clcc_source) {
+                       g_source_remove(vd->clcc_source);
+                       vd->clcc_source = 0;
+               }
+
+               /* We have to poll here, we have no idea whether the call was
+                * accepted by CHLD=1 or swapped by CHLD=2 or one call was
+                * chosed for private chat by CHLD=2x
+                */
+               g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
+                               clcc_poll_cb, vc, NULL);
+               break;
+       case 2:
+               if (callheld == 0) {
+                       for (l = vd->calls; l; l = l->next) {
+                               call = l->data;
+
+                               if (call->status != CALL_STATUS_ACTIVE)
+                                       continue;
+
+                               call->status = CALL_STATUS_HELD;
+                               ofono_voicecall_notify(vc, call);
+                       }
+               } else if (callheld == 1) {
+                       if (vd->clcc_source)
+                               g_source_remove(vd->clcc_source);
+
+                       /* We have to schedule a poll here, we have no idea
+                        * whether active call was dropped by remote or if this
+                        * is an intermediate state during call swap
+                        */
+                       vd->clcc_source = g_timeout_add(POLL_CLCC_DELAY,
+                                                       poll_clcc, vc);
+               }
+       }
+
+       vd->cind_val[HFP_INDICATOR_CALLHELD] = value;
+}
+
+static void ciev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       int index;
+       int value;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIEV:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &index))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &value))
+               return;
+
+       if (index == vd->cind_pos[HFP_INDICATOR_CALL])
+               ciev_call_notify(vc, value);
+       else if (index == vd->cind_pos[HFP_INDICATOR_CALLSETUP])
+               ciev_callsetup_notify(vc, value);
+       else if (index == vd->cind_pos[HFP_INDICATOR_CALLHELD])
+               ciev_callheld_notify(vc, value);
+}
+
+static void hfp_clcc_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *l;
+
+       if (!ok)
+               return;
+
+       vd->calls = at_util_parse_clcc(result);
+
+       for (l = vd->calls; l; l = l->next)
+               ofono_voicecall_notify(vc, l->data);
+}
+
+static void hfp_voicecall_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       DBG("hfp_voicecall_init: registering to notifications");
+
+       g_at_chat_register(vd->chat, "RING", ring_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CIEV:", ciev_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL);
+
+       g_at_chat_register(vd->chat, "NO CARRIER",
+                               no_carrier_notify, FALSE, vc, NULL);
+
+       ofono_voicecall_register(vc);
+
+       /* Populate the call list */
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, hfp_clcc_cb, vc, NULL);
+}
+
+static int hfp_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+                               gpointer user_data)
+{
+       struct hfp_slc_info *info = user_data;
+       struct voicecall_data *vd;
+
+       vd = g_new0(struct voicecall_data, 1);
+
+       vd->chat = g_at_chat_clone(info->chat);
+       vd->ag_features = info->ag_features;
+       vd->ag_mpty_features = info->ag_mpty_features;
+
+       memcpy(vd->cind_pos, info->cind_pos, HFP_INDICATOR_LAST);
+       memcpy(vd->cind_val, info->cind_val, HFP_INDICATOR_LAST);
+
+       ofono_voicecall_set_data(vc, vd);
+
+       g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CCWA=1", NULL,
+                               hfp_voicecall_initialized, vc, NULL);
+       return 0;
+}
+
+static void hfp_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       if (vd->clcc_source)
+               g_source_remove(vd->clcc_source);
+
+       if (vd->clip_source)
+               g_source_remove(vd->clip_source);
+
+       g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+       g_slist_free(vd->calls);
+
+       ofono_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "hfpmodem",
+       .probe                  = hfp_voicecall_probe,
+       .remove                 = hfp_voicecall_remove,
+       .dial                   = hfp_dial,
+       .answer                 = hfp_answer,
+       .hangup_active          = hfp_hangup,
+       .hold_all_active        = hfp_hold_all_active,
+       .release_all_held       = hfp_release_all_held,
+       .set_udub               = hfp_set_udub,
+       .release_all_active     = hfp_release_all_active,
+       .release_specific       = hfp_release_specific,
+       .private_chat           = hfp_private_chat,
+       .create_multiparty      = hfp_create_multiparty,
+       .transfer               = hfp_transfer,
+       .deflect                = NULL,
+       .swap_without_accept    = NULL,
+       .send_tones             = hfp_send_dtmf
+};
+
+void hfp_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void hfp_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/hsomodem/gprs-context.c b/drivers/hsomodem/gprs-context.c
new file mode 100644 (file)
index 0000000..ce8d21c
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "hsomodem.h"
+
+#define HSO_DISCONNECTED 0
+#define HSO_CONNECTED 1
+#define HSO_CONNECTING 2
+#define HSO_FAILED 3
+
+#define AUTH_BUF_LENGTH OFONO_GPRS_MAX_USERNAME_LENGTH + \
+                       OFONO_GPRS_MAX_PASSWORD_LENGTH + 128
+
+#define STATIC_IP_NETMASK "255.255.255.255"
+
+static const char *none_prefix[] = { NULL };
+static const char *owandata_prefix[] = { "_OWANDATA:", NULL };
+
+enum hso_state {
+       HSO_NONE = 0,
+       HSO_ENABLING = 1,
+       HSO_DISABLING = 2,
+};
+
+struct gprs_context_data {
+       GAtChat *chat;
+       unsigned int active_context;                    /* Currently active */
+       enum hso_state hso_state;                       /* Are we in req ? */
+       ofono_gprs_context_cb_t cb;
+       void *cb_data;                                  /* Callback data */
+       int owancall;                                   /* State of the call */
+};
+
+static void at_owancall_down_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+
+       /* Now we have to wait for the unsolicited notification to arrive */
+       if (ok && gcd->owancall != 0) {
+               gcd->hso_state = HSO_DISABLING;
+               gcd->cb = cb;
+               gcd->cb_data = cbd->data;
+               return;
+       }
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void at_owancall_up_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+
+       if (ok) {
+               gcd->hso_state = HSO_ENABLING;
+               gcd->cb = cb;
+               gcd->cb_data = cbd->data;
+               return;
+       }
+
+       gcd->active_context = 0;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void hso_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *ncbd;
+       char buf[64];
+
+       if (!ok) {
+               struct ofono_error error;
+
+               gcd->active_context = 0;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               cb(&error, cbd->data);
+               return;
+       }
+
+       ncbd = g_memdup(cbd, sizeof(struct cb_data));
+
+       snprintf(buf, sizeof(buf), "AT_OWANCALL=%u,1,1", gcd->active_context);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_owancall_up_cb, ncbd, g_free) > 0)
+               return;
+
+       g_free(ncbd);
+
+       gcd->active_context = 0;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void hso_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[AUTH_BUF_LENGTH];
+       int len;
+
+       /* IPv6 support not implemented */
+       if (ctx->proto != OFONO_GPRS_PROTO_IP)
+               goto error;
+
+       gcd->active_context = ctx->cid;
+
+       cbd->user = gc;
+
+       if (ctx->username[0] && ctx->password[0])
+               snprintf(buf, sizeof(buf), "AT$QCPDPP=%u,1,\"%s\",\"%s\"",
+                       ctx->cid, ctx->password, ctx->username);
+       else if (ctx->password[0])
+               snprintf(buf, sizeof(buf), "AT$QCPDPP=%u,2,,\"%s\"",
+                               ctx->cid, ctx->password);
+       else
+               snprintf(buf, sizeof(buf), "AT$QCPDPP=%u,0", ctx->cid);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               NULL, NULL, NULL) == 0)
+               goto error;
+
+       len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+                               ctx->apn);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               hso_cgdcont_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void hso_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int cid,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[128];
+
+       cbd->user = gc;
+
+       snprintf(buf, sizeof(buf), "AT_OWANCALL=%u,0,1", cid);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_owancall_down_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void owandata_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtResultIter iter;
+       int cid;
+       const char *ip = NULL;
+       const char *gateway = NULL;
+       const char *dns1 = NULL;
+       const char *dns2 = NULL;
+       const char *dns[3];
+       struct ofono_modem *modem;
+       const char *interface;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "_OWANDATA:") == FALSE)
+               return;
+
+       g_at_result_iter_next_number(&iter, &cid);
+       g_at_result_iter_next_unquoted_string(&iter, &ip);
+       g_at_result_iter_next_unquoted_string(&iter, &gateway);
+       g_at_result_iter_next_unquoted_string(&iter, &dns1);
+       g_at_result_iter_next_unquoted_string(&iter, &dns2);
+
+       if (ip && ip[0] == ' ')
+               ip += 1;
+
+       if (gateway && gateway[0] == ' ')
+               gateway += 1;
+
+       if (dns1 && dns1[0] == ' ')
+               dns1 += 1;
+
+       if (dns2 && dns2[0] == ' ')
+               dns2 += 1;
+
+       /* Don't bother reporting the same DNS twice */
+       if (g_str_equal(dns1, dns2))
+               dns2 = NULL;
+
+       dns[0] = dns1;
+       dns[1] = dns2;
+       dns[2] = 0;
+
+       modem = ofono_gprs_context_get_modem(gc);
+       interface = ofono_modem_get_string(modem, "NetworkInterface");
+
+       ofono_info("Got the following parameters for context: %d", cid);
+       ofono_info("IP: %s, Gateway: %s", ip, gateway);
+       ofono_info("DNS: %s, %s", dns1, dns2);
+
+       ofono_gprs_context_set_interface(gc, interface);
+       ofono_gprs_context_set_ipv4_address(gc, ip, TRUE);
+       ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK);
+       ofono_gprs_context_set_ipv4_gateway(gc, gateway);
+       ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+
+       gcd->hso_state = HSO_NONE;
+       gcd->cb = NULL;
+       gcd->cb_data = NULL;
+}
+
+static void owancall_notifier(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtResultIter iter;
+       int state;
+       int cid;
+
+       if (gcd->active_context == 0)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "_OWANCALL:") == FALSE)
+               return;
+
+       g_at_result_iter_next_number(&iter, &cid);
+       g_at_result_iter_next_number(&iter, &state);
+
+       if (gcd->active_context != (unsigned int) cid)
+               return;
+
+       switch (state) {
+       case HSO_DISCONNECTED:
+               DBG("HSO Context: disconnected");
+
+               if (gcd->hso_state == HSO_DISABLING) {
+                       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+                       gcd->hso_state = HSO_NONE;
+                       gcd->cb = NULL;
+                       gcd->cb_data = NULL;
+               } else {
+                       ofono_gprs_context_deactivated(gc, gcd->active_context);
+               }
+
+               gcd->active_context = 0;
+               break;
+
+       case HSO_CONNECTED:
+               DBG("HSO Context: connected");
+
+               if (gcd->hso_state == HSO_ENABLING) {
+                       char buf[128];
+
+                       snprintf(buf, sizeof(buf), "AT_OWANDATA=%u",
+                                       gcd->active_context);
+
+                       g_at_chat_send(gcd->chat, buf, owandata_prefix,
+                                       owandata_cb, gc, NULL);
+               }
+
+               break;
+
+       case HSO_FAILED:
+               DBG("HSO Context: failed");
+
+               if (gcd->hso_state == HSO_ENABLING) {
+                       CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+                       gcd->hso_state = HSO_NONE;
+                       gcd->cb = NULL;
+                       gcd->cb_data = NULL;
+               }
+
+               gcd->active_context = 0;
+               break;
+
+       default:
+               break;
+       };
+
+       gcd->owancall = state;
+}
+
+static int hso_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+
+       gcd = g_new0(struct gprs_context_data, 1);
+       gcd->chat = g_at_chat_clone(chat);
+
+       g_at_chat_register(gcd->chat, "_OWANCALL:", owancall_notifier,
+                               FALSE, gc, NULL);
+
+       ofono_gprs_context_set_data(gc, gcd);
+
+       return 0;
+}
+
+static void hso_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "hsomodem",
+       .probe                  = hso_gprs_context_probe,
+       .remove                 = hso_gprs_context_remove,
+       .activate_primary       = hso_gprs_activate_primary,
+       .deactivate_primary     = hso_gprs_deactivate_primary,
+};
+
+void hso_gprs_context_init(void)
+{
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void hso_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/hsomodem/hsomodem.c b/drivers/hsomodem/hsomodem.c
new file mode 100644 (file)
index 0000000..8cacc60
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "hsomodem.h"
+
+static int hsomodem_init(void)
+{
+       hso_gprs_context_init();
+       hso_radio_settings_init();
+
+       return 0;
+}
+
+static void hsomodem_exit(void)
+{
+       hso_gprs_context_exit();
+       hso_radio_settings_exit();
+}
+
+OFONO_PLUGIN_DEFINE(hsomodem, "HSO modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       hsomodem_init, hsomodem_exit)
diff --git a/drivers/hsomodem/hsomodem.h b/drivers/hsomodem/hsomodem.h
new file mode 100644 (file)
index 0000000..cbe7474
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void hso_gprs_context_init(void);
+extern void hso_gprs_context_exit(void);
+
+extern void hso_radio_settings_init(void);
+extern void hso_radio_settings_exit(void);
diff --git a/drivers/hsomodem/radio-settings.c b/drivers/hsomodem/radio-settings.c
new file mode 100644 (file)
index 0000000..0d4a06b
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "hsomodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *opsys_prefix[] = { "_OPSYS:", NULL };
+
+struct radio_settings_data {
+       GAtChat *chat;
+};
+
+static void opsys_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+       enum ofono_radio_access_mode mode;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int value;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "_OPSYS:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &value) == FALSE)
+               goto error;
+
+       switch (value) {
+       case 0:
+               mode = OFONO_RADIO_ACCESS_MODE_GSM;
+               break;
+       case 1:
+               mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+               break;
+       case 2:
+       case 3:
+       case 5:
+               mode = OFONO_RADIO_ACCESS_MODE_ANY;
+               break;
+       default:
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       cb(&error, mode, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void hso_query_rat_mode(struct ofono_radio_settings *rs,
+                               ofono_radio_settings_rat_mode_query_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(rsd->chat, "AT_OPSYS?", opsys_prefix,
+                                       opsys_query_cb, cbd, g_free) == 0) {
+               CALLBACK_WITH_FAILURE(cb, -1, data);
+               g_free(cbd);
+       }
+}
+
+static void opsys_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void hso_set_rat_mode(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode,
+                               ofono_radio_settings_rat_mode_set_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[20];
+       int value = 5;
+
+       switch (mode) {
+       case OFONO_RADIO_ACCESS_MODE_ANY:
+               value = 5;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_GSM:
+               value = 0;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_UMTS:
+               value = 1;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_LTE:
+               goto error;
+       }
+
+       snprintf(buf, sizeof(buf), "AT_OPSYS=%u,2", value);
+
+       if (g_at_chat_send(rsd->chat, buf, none_prefix,
+                                       opsys_modify_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void opsys_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_radio_settings *rs = user_data;
+
+       if (!ok)
+               return;
+
+       ofono_radio_settings_register(rs);
+}
+
+static int hso_radio_settings_probe(struct ofono_radio_settings *rs,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct radio_settings_data *rsd;
+
+       rsd = g_try_new0(struct radio_settings_data, 1);
+       if (rsd == NULL)
+               return -ENOMEM;
+
+       rsd->chat = g_at_chat_clone(chat);
+
+       ofono_radio_settings_set_data(rs, rsd);
+
+       g_at_chat_send(rsd->chat, "AT_OPBM?", none_prefix, NULL, NULL, NULL);
+
+       g_at_chat_send(rsd->chat, "AT_OPSYS=?", opsys_prefix,
+                                       opsys_support_cb, rs, NULL);
+
+       return 0;
+}
+
+static void hso_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+
+       ofono_radio_settings_set_data(rs, NULL);
+
+       g_at_chat_unref(rsd->chat);
+       g_free(rsd);
+}
+
+static struct ofono_radio_settings_driver driver = {
+       .name                   = "hsomodem",
+       .probe                  = hso_radio_settings_probe,
+       .remove                 = hso_radio_settings_remove,
+       .query_rat_mode         = hso_query_rat_mode,
+       .set_rat_mode           = hso_set_rat_mode
+};
+
+void hso_radio_settings_init(void)
+{
+       ofono_radio_settings_driver_register(&driver);
+}
+
+void hso_radio_settings_exit(void)
+{
+       ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/huaweimodem/audio-settings.c b/drivers/huaweimodem/audio-settings.c
new file mode 100644 (file)
index 0000000..1d4e903
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/audio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "huaweimodem.h"
+
+static const char *cvoice_prefix[] = { "^CVOICE:", NULL };
+
+struct audio_settings_data {
+       GAtChat *chat;
+};
+
+static void cring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_audio_settings *as = user_data;
+
+       ofono_audio_settings_active_notify(as, TRUE);
+}
+
+static void orig_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_audio_settings *as = user_data;
+
+       ofono_audio_settings_active_notify(as, TRUE);
+}
+
+static void cend_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_audio_settings *as = user_data;
+
+       ofono_audio_settings_active_notify(as, FALSE);
+}
+
+static void cvoice_support_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_audio_settings *as = user_data;
+       struct audio_settings_data *asd = ofono_audio_settings_get_data(as);
+
+       if (!ok)
+               return;
+
+       g_at_chat_register(asd->chat, "+CRING:", cring_notify, FALSE, as, NULL);
+       g_at_chat_register(asd->chat, "^ORIG:", orig_notify, FALSE, as, NULL);
+       g_at_chat_register(asd->chat, "^CEND:", cend_notify, FALSE, as, NULL);
+
+       ofono_audio_settings_register(as);
+}
+
+static int huawei_audio_settings_probe(struct ofono_audio_settings *as,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct audio_settings_data *asd;
+
+       asd = g_try_new0(struct audio_settings_data, 1);
+       if (asd == NULL)
+               return -ENOMEM;
+
+       asd->chat = g_at_chat_clone(chat);
+
+       ofono_audio_settings_set_data(as, asd);
+
+       g_at_chat_send(asd->chat, "AT^CVOICE=?", cvoice_prefix,
+                                       cvoice_support_cb, as, NULL);
+
+       return 0;
+}
+
+static void huawei_audio_settings_remove(struct ofono_audio_settings *as)
+{
+       struct audio_settings_data *asd = ofono_audio_settings_get_data(as);
+
+       ofono_audio_settings_set_data(as, NULL);
+
+       g_at_chat_unref(asd->chat);
+       g_free(asd);
+}
+
+static struct ofono_audio_settings_driver driver = {
+       .name           = "huaweimodem",
+       .probe          = huawei_audio_settings_probe,
+       .remove         = huawei_audio_settings_remove,
+};
+
+void huawei_audio_settings_init(void)
+{
+       ofono_audio_settings_driver_register(&driver);
+}
+
+void huawei_audio_settings_exit(void)
+{
+       ofono_audio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/huaweimodem/cdma-netreg.c b/drivers/huaweimodem/cdma-netreg.c
new file mode 100644 (file)
index 0000000..2ae66e1
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <glib.h>
+#include <errno.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cdma-netreg.h>
+
+#include "gatchat.h"
+
+#include "huaweimodem.h"
+
+static const char *sysinfo_prefix[] = { "^SYSINFO:", NULL };
+
+static gboolean parse_sysinfo(GAtResult *result, gint *status)
+{
+       GAtResultIter iter;
+       gint srv_status;
+       gint srv_domain;
+       gint roaming_status;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^SYSINFO:"))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, &srv_status))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, &srv_domain))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, &roaming_status))
+               return FALSE;
+
+       DBG("%d, %d, %d", srv_status, srv_domain, roaming_status);
+
+       switch (srv_status) {
+       case 1: /* Restricted service */
+       case 2: /* Service valid */
+       case 3: /* Restricted region service */
+               if (roaming_status)
+                       *status = CDMA_NETWORK_REGISTRATION_STATUS_ROAMING;
+               else
+                       *status = CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED;
+               break;
+       case 0: /* No service */
+       case 4: /* Not registered */
+       default:
+               *status = CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+               break;
+       }
+
+       switch (srv_domain) {
+       case 0: /* No service */
+               *status = CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+               break;
+       case 1: /* Only CS */
+       case 2: /* Only PS */
+       case 3: /* CS  PS */
+       case 4: /* CS registered, PS in searching state */
+       case 255: /* CDMA not supported */
+               break;
+       }
+
+       return TRUE;
+}
+
+static void sysinfo_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_netreg *netreg = user_data;
+       int status;
+
+       if (!ok)
+               return;
+
+       if (parse_sysinfo(result, &status) == FALSE) {
+               ofono_error("Invalid SYSINFO values");
+               return;
+       }
+
+       ofono_cdma_netreg_status_notify(netreg, status);
+}
+
+static void mode_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_netreg *netreg = user_data;
+       GAtChat *chat = ofono_cdma_netreg_get_data(netreg);
+
+       g_at_chat_send(chat, "AT^SYSINFO", sysinfo_prefix,
+                               sysinfo_cb, netreg, NULL);
+}
+
+static void rssilvl_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_netreg *netreg = user_data;
+       int strength;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^RSSILVL:"))
+               goto error;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               goto error;
+
+       if (strength == 99)
+               strength = 100;
+
+       ofono_cdma_netreg_strength_notify(netreg, strength);
+
+       return;
+
+error:
+       ofono_error("Invalid RSSILVL value");
+}
+
+static void hrssilvl_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_netreg *netreg = user_data;
+       int strength;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^HRSSILVL:"))
+               goto error;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               goto error;
+
+       if (strength == 99)
+               strength = 100;
+
+       ofono_cdma_netreg_data_strength_notify(netreg, strength);
+
+       return;
+
+error:
+       ofono_error("Invalid HRSSILVL value");
+}
+
+static void probe_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_cdma_netreg *netreg = user_data;
+       GAtChat *chat = ofono_cdma_netreg_get_data(netreg);
+
+       if (!ok) {
+               ofono_cdma_netreg_remove(netreg);
+               return;
+       }
+
+       g_at_chat_register(chat, "^MODE:",
+                               mode_notify, FALSE, netreg, NULL);
+
+       g_at_chat_register(chat, "^RSSILVL:",
+                               rssilvl_notify, FALSE, netreg, NULL);
+
+       g_at_chat_register(chat, "^HRSSILVL:",
+                               hrssilvl_notify, FALSE, netreg, NULL);
+
+       ofono_cdma_netreg_register(netreg);
+}
+
+static int huawei_cdma_netreg_probe(struct ofono_cdma_netreg *netreg,
+                               unsigned int vendor, void *data)
+{
+       GAtChat *chat = g_at_chat_clone(data);
+
+       ofono_cdma_netreg_set_data(netreg, chat);
+
+       g_at_chat_send(chat, "AT^SYSINFO", sysinfo_prefix,
+                               probe_cb, netreg, NULL);
+
+       return 0;
+}
+
+static void huawei_cdma_netreg_remove(struct ofono_cdma_netreg *netreg)
+{
+       GAtChat *chat = ofono_cdma_netreg_get_data(netreg);
+
+       ofono_cdma_netreg_set_data(netreg, NULL);
+
+       g_at_chat_unref(chat);
+}
+
+static struct ofono_cdma_netreg_driver driver = {
+       .name   = "huaweimodem",
+       .probe  = huawei_cdma_netreg_probe,
+       .remove = huawei_cdma_netreg_remove,
+};
+
+void huawei_cdma_netreg_init(void)
+{
+       ofono_cdma_netreg_driver_register(&driver);
+}
+
+void huawei_cdma_netreg_exit(void)
+{
+       ofono_cdma_netreg_driver_unregister(&driver);
+}
diff --git a/drivers/huaweimodem/gprs-context.c b/drivers/huaweimodem/gprs-context.c
new file mode 100644 (file)
index 0000000..db697db
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gattty.h"
+
+#include "huaweimodem.h"
+
+#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
+
+static const char *none_prefix[] = { NULL };
+static const char *dhcp_prefix[] = { "^DHCP:", NULL };
+
+struct gprs_context_data {
+       GAtChat *chat;
+       unsigned int active_context;
+       unsigned int dhcp_source;
+       unsigned int dhcp_count;
+       guint ndis_watch;
+       ofono_gprs_context_cb_t cb;
+       void *cb_data;                                  /* Callback data */
+};
+
+static void check_dhcp(struct ofono_gprs_context *gc);
+
+static gboolean dhcp_poll(gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       if (gcd->dhcp_count > 20)
+               CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+       else
+               check_dhcp(gc);
+
+       gcd->dhcp_count++;
+       gcd->dhcp_source = 0;
+
+       return FALSE;
+}
+
+static gboolean get_next_addr(GAtResultIter *iter, char **addr)
+{
+       const char *str;
+       guint32 val;
+
+       if (g_at_result_iter_next_unquoted_string(iter, &str) == FALSE)
+               return FALSE;
+
+       val = strtol(str, NULL, 16);
+
+       if (addr)
+               *addr = g_strdup_printf("%u.%u.%u.%u",
+                                       (val & 0x000000ff),
+                                       (val & 0x0000ff00) >> 8,
+                                       (val & 0x00ff0000) >> 16,
+                                       (val & 0xff000000) >> 24);
+
+       return TRUE;
+}
+
+static void dhcp_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtResultIter iter;
+       const char *interface;
+       char *ip = NULL;
+       char *netmask = NULL;
+       char *gateway = NULL;
+       char *dns1 = NULL;
+       char *dns2 = NULL;
+       const char *dns[3];
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               gcd->dhcp_source = g_timeout_add_seconds(1, dhcp_poll, gc);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "^DHCP:") == FALSE)
+               return;
+
+       get_next_addr(&iter, &ip);
+       get_next_addr(&iter, &netmask);
+       get_next_addr(&iter, &gateway);
+       get_next_addr(&iter, NULL);
+       get_next_addr(&iter, &dns1);
+       get_next_addr(&iter, &dns2);
+
+       dns[0] = dns1;
+       dns[1] = dns2;
+       dns[2] = 0;
+
+       ofono_info("Got the following parameters for context: %d",
+                                                       gcd->active_context);
+       ofono_info("IP: %s  Gateway: %s", ip, gateway);
+       ofono_info("DNS: %s, %s", dns1, dns2);
+
+       interface = "invalid";
+
+       ofono_gprs_context_set_interface(gc, interface);
+       ofono_gprs_context_set_ipv4_address(gc, ip, TRUE);
+       ofono_gprs_context_set_ipv4_netmask(gc, netmask);
+       ofono_gprs_context_set_ipv4_gateway(gc, gateway);
+       ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+       gcd->cb = NULL;
+       gcd->cb_data = NULL;
+
+       g_free(ip);
+       g_free(netmask);
+       g_free(gateway);
+       g_free(dns1);
+       g_free(dns2);
+}
+
+static void check_dhcp(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       g_at_chat_send(gcd->chat, "AT^DHCP?", dhcp_prefix,
+                                       dhcp_query_cb, gc, NULL);
+}
+
+static void at_ndisdup_down_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+
+       DBG("ok %d", ok);
+
+       if (ok) {
+               gcd->cb = cb;
+               gcd->cb_data = cbd->data;
+
+               if (gcd->ndis_watch > 0) {
+                       g_source_remove(gcd->ndis_watch);
+                       gcd->ndis_watch = 0;
+               }
+       }
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void at_ndisdup_up_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+
+       DBG("ok %d", ok);
+
+       if (ok) {
+               gcd->cb = cb;
+               gcd->cb_data = cbd->data;
+
+               gcd->dhcp_count = 0;
+
+               check_dhcp(gc);
+               return;
+       }
+
+       gcd->active_context = 0;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *ncbd;
+       char buf[64];
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               struct ofono_error error;
+
+               gcd->active_context = 0;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               cb(&error, cbd->data);
+               return;
+       }
+
+       ncbd = g_memdup(cbd, sizeof(struct cb_data));
+
+       snprintf(buf, sizeof(buf), "AT^NDISDUP=%u,1", gcd->active_context);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_ndisdup_up_cb, ncbd, g_free) > 0)
+               return;
+
+       g_free(ncbd);
+
+       gcd->active_context = 0;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void huawei_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+       int len;
+
+       /* IPv6 support not implemented */
+       if (ctx->proto != OFONO_GPRS_PROTO_IP)
+               goto error;
+
+       DBG("cid %u", ctx->cid);
+
+       gcd->active_context = ctx->cid;
+
+       cbd->user = gc;
+
+       len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+                               ctx->apn);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_cgdcont_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void huawei_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int cid,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[128];
+
+       DBG("cid %u", cid);
+
+       cbd->user = gc;
+
+       snprintf(buf, sizeof(buf), "AT^NDISDUP=%u,0", cid);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_ndisdup_down_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static int huawei_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+       struct stat st;
+
+       DBG("");
+
+       if (stat(TUN_SYSFS_DIR, &st) < 0) {
+               ofono_error("Missing support for TUN/TAP devices");
+               return -ENODEV;
+       }
+
+       gcd = g_try_new0(struct gprs_context_data, 1);
+       if (gcd == NULL)
+               return -ENOMEM;
+
+       gcd->chat = g_at_chat_clone(chat);
+
+       ofono_gprs_context_set_data(gc, gcd);
+
+       return 0;
+}
+
+static void huawei_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("");
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "huaweimodem",
+       .probe                  = huawei_gprs_context_probe,
+       .remove                 = huawei_gprs_context_remove,
+       .activate_primary       = huawei_gprs_activate_primary,
+       .deactivate_primary     = huawei_gprs_deactivate_primary,
+};
+
+void huawei_gprs_context_init(void)
+{
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void huawei_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/huaweimodem/huaweimodem.c b/drivers/huaweimodem/huaweimodem.c
new file mode 100644 (file)
index 0000000..7fd72fb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "huaweimodem.h"
+
+static int huaweimodem_init(void)
+{
+       huawei_ussd_init();
+       huawei_voicecall_init();
+       huawei_audio_settings_init();
+       huawei_radio_settings_init();
+       huawei_gprs_context_init();
+
+       huawei_cdma_netreg_init();
+
+       return 0;
+}
+
+static void huaweimodem_exit(void)
+{
+       huawei_cdma_netreg_exit();
+
+       huawei_gprs_context_exit();
+       huawei_radio_settings_exit();
+       huawei_audio_settings_exit();
+       huawei_voicecall_exit();
+       huawei_ussd_exit();
+}
+
+OFONO_PLUGIN_DEFINE(huaweimodem, "Huawei modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       huaweimodem_init, huaweimodem_exit)
diff --git a/drivers/huaweimodem/huaweimodem.h b/drivers/huaweimodem/huaweimodem.h
new file mode 100644 (file)
index 0000000..00c1fa4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void huawei_ussd_init(void);
+extern void huawei_ussd_exit(void);
+
+extern void huawei_voicecall_init(void);
+extern void huawei_voicecall_exit(void);
+
+extern void huawei_audio_settings_init(void);
+extern void huawei_audio_settings_exit(void);
+
+extern void huawei_radio_settings_init(void);
+extern void huawei_radio_settings_exit(void);
+
+extern void huawei_gprs_context_init(void);
+extern void huawei_gprs_context_exit(void);
+
+extern void huawei_cdma_netreg_init(void);
+extern void huawei_cdma_netreg_exit(void);
diff --git a/drivers/huaweimodem/radio-settings.c b/drivers/huaweimodem/radio-settings.c
new file mode 100644 (file)
index 0000000..c34653a
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "huaweimodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *syscfg_prefix[] = { "^SYSCFG:", NULL };
+
+#define HUAWEI_BAND_ANY 0x3FFFFFFF
+
+struct radio_settings_data {
+       GAtChat *chat;
+};
+
+static const struct huawei_band_gsm_table {
+       enum ofono_radio_band_gsm band_gsm;
+       unsigned int band_huawei;
+} huawei_band_gsm_table[] = {
+       { OFONO_RADIO_BAND_GSM_ANY, 0x80000 | 0x200 | 0x100 | 0x80 | 0x200000 },
+       { OFONO_RADIO_BAND_GSM_850, 0x80000 },
+       { OFONO_RADIO_BAND_GSM_900P, 0x200 },
+       { OFONO_RADIO_BAND_GSM_900E, 0x100 },
+       { OFONO_RADIO_BAND_GSM_1800, 0x80 },
+       { OFONO_RADIO_BAND_GSM_1900, 0x200000 },
+};
+
+static const struct huawei_band_umts_table {
+       enum ofono_radio_band_umts band_umts;
+       unsigned int band_huawei;
+} huawei_band_umts_table[] = {
+       { OFONO_RADIO_BAND_UMTS_ANY, 0x4000000 | 0x20000 | 800000 | 400000 },
+       { OFONO_RADIO_BAND_UMTS_850, 0x4000000 },
+       { OFONO_RADIO_BAND_UMTS_900, 0x20000 },
+       { OFONO_RADIO_BAND_UMTS_1900, 0x800000 },
+       { OFONO_RADIO_BAND_UMTS_2100, 0x400000 },
+};
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static unsigned int band_gsm_to_huawei(enum ofono_radio_band_gsm band)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(huawei_band_gsm_table); i++) {
+               if (huawei_band_gsm_table[i].band_gsm == band)
+                       return huawei_band_gsm_table[i].band_huawei;
+       }
+
+       return 0;
+}
+
+static unsigned int band_umts_to_huawei(enum ofono_radio_band_umts band)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(huawei_band_umts_table); i++) {
+               if (huawei_band_umts_table[i].band_umts == band)
+                       return huawei_band_umts_table[i].band_huawei;
+       }
+
+       return 0;
+}
+
+static enum ofono_radio_band_gsm band_gsm_from_huawei(unsigned int band)
+{
+       size_t i;
+
+       if (band == HUAWEI_BAND_ANY)
+               return OFONO_RADIO_BAND_UMTS_ANY;
+
+       for (i = ARRAY_SIZE(huawei_band_gsm_table) - 1; i > 0; i--) {
+               if (huawei_band_gsm_table[i].band_huawei & band)
+                       return huawei_band_gsm_table[i].band_gsm;
+       }
+
+       return OFONO_RADIO_BAND_GSM_ANY;
+}
+
+static enum ofono_radio_band_umts band_umts_from_huawei(unsigned int band)
+{
+       size_t i;
+
+       if (band == HUAWEI_BAND_ANY)
+               return OFONO_RADIO_BAND_UMTS_ANY;
+
+       for (i = ARRAY_SIZE(huawei_band_umts_table) - 1; i > 0; i--) {
+               if (huawei_band_umts_table[i].band_huawei & band)
+                       return huawei_band_umts_table[i].band_umts;
+       }
+
+       return OFONO_RADIO_BAND_UMTS_ANY;
+}
+
+static void syscfg_query_mode_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+       enum ofono_radio_access_mode mode;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int value;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "^SYSCFG:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &value) == FALSE)
+               goto error;
+
+       switch (value) {
+       case 2:
+               mode = OFONO_RADIO_ACCESS_MODE_ANY;
+               break;
+       case 13:
+               mode = OFONO_RADIO_ACCESS_MODE_GSM;
+               break;
+       case 14:
+               mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+               break;
+       default:
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       cb(&error, mode, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void huawei_query_rat_mode(struct ofono_radio_settings *rs,
+                       ofono_radio_settings_rat_mode_query_cb_t cb, void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(rsd->chat, "AT^SYSCFG?", syscfg_prefix,
+                               syscfg_query_mode_cb, cbd, g_free) == 0) {
+               CALLBACK_WITH_FAILURE(cb, -1, data);
+               g_free(cbd);
+       }
+}
+
+static void syscfg_modify_mode_cb(gboolean ok, GAtResult *result,
+                                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void huawei_set_rat_mode(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode,
+                               ofono_radio_settings_rat_mode_set_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[40];
+       unsigned int value = 2, acq_order = 0;
+
+       switch (mode) {
+       case OFONO_RADIO_ACCESS_MODE_ANY:
+               value = 2;
+               acq_order = 0;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_GSM:
+               value = 13;
+               acq_order = 1;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_UMTS:
+               value = 14;
+               acq_order = 2;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_LTE:
+               goto error;
+       }
+
+       snprintf(buf, sizeof(buf), "AT^SYSCFG=%u,%u,40000000,2,4",
+                                                       value, acq_order);
+
+       if (g_at_chat_send(rsd->chat, buf, none_prefix,
+                                       syscfg_modify_mode_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void syscfg_modify_band_cb(gboolean ok, GAtResult *result,
+                                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_band_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void huawei_set_band(struct ofono_radio_settings *rs,
+                                       enum ofono_radio_band_gsm band_gsm,
+                                       enum ofono_radio_band_umts band_umts,
+                                       ofono_radio_settings_band_set_cb_t cb,
+                                       void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[40];
+       unsigned int huawei_band;
+
+       if (band_gsm == OFONO_RADIO_BAND_GSM_ANY
+                       && band_umts == OFONO_RADIO_BAND_UMTS_ANY) {
+               huawei_band = HUAWEI_BAND_ANY;
+       } else {
+               unsigned int huawei_band_gsm;
+               unsigned int huawei_band_umts;
+
+               huawei_band_gsm = band_gsm_to_huawei(band_gsm);
+
+               if (!huawei_band_gsm)
+                       goto error;
+
+               huawei_band_umts = band_umts_to_huawei(band_umts);
+
+               if (!huawei_band_umts)
+                       goto error;
+
+               huawei_band = huawei_band_gsm | huawei_band_umts;
+       }
+
+       snprintf(buf, sizeof(buf), "AT^SYSCFG=16,3,%x,2,4", huawei_band);
+
+       if (g_at_chat_send(rsd->chat, buf, none_prefix,
+                                       syscfg_modify_band_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void syscfg_query_band_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_band_query_cb_t cb = cbd->cb;
+       enum ofono_radio_band_gsm band_gsm;
+       enum ofono_radio_band_umts band_umts;
+       struct ofono_error error;
+       GAtResultIter iter;
+       unsigned int band;
+       const char *band_str;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "^SYSCFG:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_skip_next(&iter) == FALSE)
+               goto error;
+
+       if (g_at_result_iter_skip_next(&iter) == FALSE)
+               goto error;
+
+       if(g_at_result_iter_next_unquoted_string(&iter, &band_str) == FALSE)
+               goto error;
+
+       sscanf((const char *) band_str, "%x", &band);
+
+       band_gsm = band_gsm_from_huawei(band);
+       band_umts = band_umts_from_huawei(band);
+
+       cb(&error, band_gsm, band_umts, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, -1, cbd->data);
+}
+
+static void huawei_query_band(struct ofono_radio_settings *rs,
+               ofono_radio_settings_band_query_cb_t cb, void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(rsd->chat, "AT^SYSCFG?", syscfg_prefix,
+                               syscfg_query_band_cb, cbd, g_free) == 0) {
+               CALLBACK_WITH_FAILURE(cb, -1, -1, data);
+               g_free(cbd);
+       }
+}
+
+static void syscfg_support_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct ofono_radio_settings *rs = user_data;
+
+       if (!ok) {
+               ofono_radio_settings_remove(rs);
+               return;
+       }
+
+       ofono_radio_settings_register(rs);
+}
+
+static int huawei_radio_settings_probe(struct ofono_radio_settings *rs,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct radio_settings_data *rsd;
+
+       rsd = g_try_new0(struct radio_settings_data, 1);
+       if (rsd == NULL)
+               return -ENOMEM;
+
+       rsd->chat = g_at_chat_clone(chat);
+
+       ofono_radio_settings_set_data(rs, rsd);
+
+       g_at_chat_send(rsd->chat, "AT^SYSCFG=?", syscfg_prefix,
+                                       syscfg_support_cb, rs, NULL);
+
+       return 0;
+}
+
+static void huawei_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+
+       ofono_radio_settings_set_data(rs, NULL);
+
+       g_at_chat_unref(rsd->chat);
+       g_free(rsd);
+}
+
+static struct ofono_radio_settings_driver driver = {
+       .name                   = "huaweimodem",
+       .probe                  = huawei_radio_settings_probe,
+       .remove                 = huawei_radio_settings_remove,
+       .query_rat_mode         = huawei_query_rat_mode,
+       .set_rat_mode           = huawei_set_rat_mode,
+       .query_band             = huawei_query_band,
+       .set_band               = huawei_set_band,
+};
+
+void huawei_radio_settings_init(void)
+{
+       ofono_radio_settings_driver_register(&driver);
+}
+
+void huawei_radio_settings_exit(void)
+{
+       ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/huaweimodem/ussd.c b/drivers/huaweimodem/ussd.c
new file mode 100644 (file)
index 0000000..002337a
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/ussd.h>
+#include "util.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "huaweimodem.h"
+
+static const char *cusd_prefix[] = { "+CUSD:", NULL };
+static const char *none_prefix[] = { NULL };
+
+struct ussd_data {
+       GAtChat *chat;
+};
+
+static void cusd_parse(GAtResult *result, struct ofono_ussd *ussd)
+{
+       GAtResultIter iter;
+       int status, dcs;
+       const char *content;
+       unsigned char msg[160];
+       const unsigned char *msg_ptr = NULL;
+       long msg_len;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CUSD:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &status))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &content))
+               goto out;
+
+       if (!g_at_result_iter_next_number(&iter, &dcs))
+               dcs = 0;
+
+       msg_ptr = decode_hex_own_buf(content, -1, &msg_len, 0, msg);
+
+out:
+       ofono_ussd_notify(ussd, status, dcs, msg_ptr, msg_ptr ? msg_len : 0);
+}
+
+static void cusd_request_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_ussd_cb_t cb = cbd->cb;
+       struct ofono_ussd *ussd = cbd->user;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+
+       cusd_parse(result, ussd);
+}
+
+static void huawei_ussd_request(struct ofono_ussd *ussd, int dcs,
+                               const unsigned char *pdu, int len,
+                               ofono_ussd_cb_t cb, void *user_data)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char buf[512], coded_buf[321];
+       char *converted;
+
+       cbd->user = ussd;
+
+       converted = encode_hex_own_buf(pdu, len, 0, coded_buf);
+       if (converted == NULL)
+               goto error;
+
+       snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%s\",%d", converted, dcs);
+
+       if (g_at_chat_send(data->chat, buf, cusd_prefix,
+                               cusd_request_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void cusd_cancel_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_ussd_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       /*
+        * All errors and notifications arrive unexpected and
+        * thus just reset the state here. This is safer than
+        * getting stuck in a dead-lock.
+        */
+       error.type = OFONO_ERROR_TYPE_NO_ERROR;
+       error.error = 0;
+
+       cb(&error, cbd->data);
+}
+
+static void huawei_ussd_cancel(struct ofono_ussd *ussd,
+                               ofono_ussd_cb_t cb, void *user_data)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+
+       cbd->user = data;
+
+       if (g_at_chat_send(data->chat, "AT+CUSD=2", none_prefix,
+                               cusd_cancel_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void cusd_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_ussd *ussd = user_data;
+
+       cusd_parse(result, ussd);
+}
+
+static void cusd_register(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_ussd *ussd = user_data;
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+
+       if (!ok) {
+               ofono_error("Could not enable CUSD notifications");
+               return;
+       }
+
+       g_at_chat_register(data->chat, "+CUSD:", cusd_notify,
+                                               FALSE, ussd, NULL);
+
+       ofono_ussd_register(ussd);
+}
+
+static int huawei_ussd_probe(struct ofono_ussd *ussd,
+                                       unsigned int vendor, void *user)
+{
+       GAtChat *chat = user;
+       struct ussd_data *data;
+
+       data = g_try_new0(struct ussd_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->chat = g_at_chat_clone(chat);
+
+       ofono_ussd_set_data(ussd, data);
+
+       g_at_chat_send(data->chat, "AT+CUSD=1", none_prefix,
+                                       cusd_register, ussd, NULL);
+
+       return 0;
+}
+
+static void huawei_ussd_remove(struct ofono_ussd *ussd)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+
+       ofono_ussd_set_data(ussd, NULL);
+
+       g_at_chat_unref(data->chat);
+       g_free(data);
+}
+
+static struct ofono_ussd_driver driver = {
+       .name           = "huaweimodem",
+       .probe          = huawei_ussd_probe,
+       .remove         = huawei_ussd_remove,
+       .request        = huawei_ussd_request,
+       .cancel         = huawei_ussd_cancel,
+};
+
+void huawei_ussd_init(void)
+{
+       ofono_ussd_driver_register(&driver);
+}
+
+void huawei_ussd_exit(void)
+{
+       ofono_ussd_driver_unregister(&driver);
+}
diff --git a/drivers/huaweimodem/voicecall.c b/drivers/huaweimodem/voicecall.c
new file mode 100644 (file)
index 0000000..1ea2613
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "common.h"
+#include "huaweimodem.h"
+
+static const char *none_prefix[] = { NULL };
+
+struct voicecall_data {
+       GAtChat *chat;
+       GSList *calls;
+};
+
+static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
+                                       int direction, int status,
+                                       const char *num, int num_type,
+                                       int clip, int id)
+{
+       struct voicecall_data *d = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* Generate a call structure for the waiting call */
+       call = g_try_new(struct ofono_call, 1);
+       if (call == NULL)
+               return NULL;
+
+       ofono_call_init(call);
+
+       call->id = id;
+       call->type = type;
+       call->direction = direction;
+       call->status = status;
+
+       if (clip != 2) {
+               strncpy(call->phone_number.number, num,
+                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = num_type;
+       }
+
+       call->clip_validity = clip;
+
+       d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
+
+       g_at_chat_send(d->chat, "AT^DDSETEX=2", none_prefix,
+                                               NULL, NULL, NULL);
+
+       return call;
+}
+
+static void huawei_generic_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void huawei_template(struct ofono_voicecall *vc, const char *cmd,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               huawei_generic_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void huawei_dial(struct ofono_voicecall *vc,
+                               const struct ofono_phone_number *ph,
+                               enum ofono_clir_option clir,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[256];
+
+       if (ph->type == 145)
+               snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
+       else
+               snprintf(buf, sizeof(buf), "ATD%s", ph->number);
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_INVOCATION:
+               strcat(buf, "I");
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               strcat(buf, "i");
+               break;
+       default:
+               break;
+       }
+
+       strcat(buf, ";");
+
+       huawei_template(vc, buf, cb, data);
+}
+
+static void huawei_answer(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       huawei_template(vc, "ATA", cb, data);
+}
+
+static void huawei_hangup(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       /* Hangup active call */
+       huawei_template(vc, "AT+CHUP", cb, data);
+}
+
+static void huawei_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id);
+       huawei_template(vc, buf, cb, data);
+}
+
+static void cring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *line;
+       int type;
+       int id;
+
+       /* CRING can repeat, ignore if we already have an incoming call */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status))
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CRING:"))
+               return;
+
+       line = g_at_result_iter_raw_line(&iter);
+       if (line == NULL)
+               return;
+
+       /* Ignore everything that is not voice for now */
+       if (!strcasecmp(line, "VOICE"))
+               type = 0;
+       else
+               type = 9;
+
+       id = ofono_voicecall_get_next_callid(vc);
+
+       /* Generate an incoming call */
+       create_call(vc, type, 1, CALL_STATUS_INCOMING, NULL, 128, 2, id);
+
+       /* Assume the CLIP always arrives, and we signal the call there */
+       DBG("%d", type);
+}
+
+static void clip_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int type, validity;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CLIP for unknown call");
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLIP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               return;
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       /* Skip subaddr, satype and alpha */
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d %d", num, type, validity);
+
+       call = l->data;
+
+       strncpy(call->phone_number.number, num,
+                               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->phone_number.type = type;
+       call->clip_validity = validity;
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+}
+
+static void ccwa_notify(GAtResult *result, gpointer user_data)
+{
+       GAtResultIter iter;
+       const char *num;
+       int num_type, validity, cls;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CCWA:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &num_type))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &cls))
+               return;
+
+       /* Skip alpha field */
+       g_at_result_iter_skip_next(&iter);
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d %d %d", num, num_type, cls, validity);
+}
+
+static void orig_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       GAtResultIter iter;
+       gint call_id, call_type;
+       struct ofono_call *call;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^ORIG:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_id))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_type))
+               return;
+
+       ofono_info("Call origin: id %d type %d", call_id, call_type);
+
+       call = create_call(vc, call_type, 0, CALL_STATUS_DIALING, NULL, 128, 2,
+                           call_id);
+       if (call == NULL) {
+               ofono_error("Unable to malloc, call tracking will fail!");
+               return;
+       }
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+}
+
+static void conf_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       gint call_id;
+       struct ofono_call *call;
+       GSList *l;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^CONF:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_id))
+               return;
+
+       ofono_info("Call setup: id %d", call_id);
+
+       l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id),
+                               at_util_call_compare_by_id);
+       if (l == NULL) {
+               ofono_error("Received CONF for untracked call");
+               return;
+       }
+
+       /* Set call to alerting */
+       call = l->data;
+       call->status = CALL_STATUS_ALERTING;
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+}
+
+static void conn_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       gint call_id, call_type;
+       struct ofono_call *call;
+       GSList *l;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^CONN:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_id))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_type))
+               return;
+
+       ofono_info("Call connect: id %d type %d", call_id, call_type);
+
+       l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id),
+                               at_util_call_compare_by_id);
+       if (l == NULL) {
+               ofono_error("Received CONN for untracked call");
+               return;
+       }
+
+       /* Set call to active */
+       call = l->data;
+       call->status = CALL_STATUS_ACTIVE;
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+}
+
+static void cend_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       gint call_id, duration, end_status, cc_pause;
+       struct ofono_call *call;
+       GSList *l;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^CEND:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_id))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &duration))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &end_status))
+               return;
+
+       /* parameter is not present on errors */
+       g_at_result_iter_next_number(&iter, &cc_pause);
+
+       ofono_info("Call end: id %d duration %ds status %d",
+                               call_id, duration, end_status);
+
+       l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id),
+                               at_util_call_compare_by_id);
+       if (l == NULL) {
+               ofono_error("Received CEND for untracked call");
+               return;
+       }
+
+       call = l->data;
+
+       if (call->type == 0)
+               ofono_voicecall_disconnected(vc, call->id,
+                               OFONO_DISCONNECT_REASON_UNKNOWN, NULL);
+
+       vd->calls = g_slist_remove(vd->calls, call);
+       g_free(call);
+}
+
+static void huawei_voicecall_initialized(gboolean ok, GAtResult *result,
+                                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       DBG("registering to notifications");
+
+       g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL);
+
+       g_at_chat_register(vd->chat, "^ORIG:", orig_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "^CONF:", conf_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "^CONN:", conn_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "^CEND:", cend_notify, FALSE, vc, NULL);
+
+       ofono_voicecall_register(vc);
+}
+
+static int huawei_voicecall_probe(struct ofono_voicecall *vc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_try_new0(struct voicecall_data, 1);
+       if (vd == NULL)
+               return -ENOMEM;
+
+       vd->chat = g_at_chat_clone(chat);
+
+       ofono_voicecall_set_data(vc, vd);
+
+       g_at_chat_send(vd->chat, "AT+CRC=1", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CLIP=1", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+COLP=1", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CCWA=1", none_prefix,
+                               huawei_voicecall_initialized, vc, NULL);
+
+       return 0;
+}
+
+static void huawei_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       ofono_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "huaweimodem",
+       .probe                  = huawei_voicecall_probe,
+       .remove                 = huawei_voicecall_remove,
+       .dial                   = huawei_dial,
+       .answer                 = huawei_answer,
+       .hangup_active          = huawei_hangup,
+       .release_specific       = huawei_release_specific,
+};
+
+void huawei_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void huawei_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/ifxmodem/audio-settings.c b/drivers/ifxmodem/audio-settings.c
new file mode 100644 (file)
index 0000000..ce31a06
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/audio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "ifxmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *xprogress_prefix[] = { "+XPROGRESS:", NULL };
+static const char *xdrv_prefix[] = { "+XDRV:", NULL };
+
+enum xdrv_destination {
+       XDRV_DESTINATION_SPEECH_TX =    0,
+       XDRV_DESTINATION_ANALOG_OUT =   1,
+       XDRV_DESTINATION_I2SX_TX =      2,
+       XDRV_DESTINATION_I2SY_TX =      3,
+       XDRV_DESTINATION_PCM_GENERAL =  4,
+};
+
+enum xdrv_source {
+       XDRV_SOURCE_SPEECH_RX =         0,
+       XDRV_SOURCE_SPEECH_ANALOG_IN =  1,
+       XDRV_SOURCE_DIGITAL_MIC_IN =    2,
+       XDRV_SOURCE_I2SX_RX =           3,
+       XDRV_SOURCE_I2SY_RX =           4,
+       XDRV_SOURCE_SIMPLE_TONES =      5,
+};
+
+enum xdrv_sampling_rate {
+       XDRV_SAMPLING_RATE_8KHZ =       0,
+       XDRV_SAMPLING_RATE_11KHZ =      1,
+       XDRV_SAMPLING_RATE_12KHZ =      2,
+       XDRV_SAMPLING_RATE_16KHZ =      3,
+       XDRV_SAMPLING_RATE_22KHZ =      4,
+       XDRV_SAMPLING_RATE_24KHZ =      5,
+       XDRV_SAMPLING_RATE_32KHZ =      6,
+       XDRV_SAMPLING_RATE_44KHZ =      7,
+       XDRV_SAMPLING_RATE_48KHZ =      8,
+       XDRV_SAMPLING_RATE_96KHZ =      9,
+       XDRV_SAMPLING_RATE_192KHZ =     10,
+};
+
+enum xdrv_sampling_width {
+       XDRV_SAMPLING_WIDTH_16 =        0,
+       XDRV_SAMPLING_WIDTH_18 =        1,
+       XDRV_SAMPLING_WIDTH_20 =        2,
+       XDRV_SAMPLING_WIDTH_24 =        3,
+       XDRV_SAMPLING_WIDTH_32 =        4,
+       XDRV_SAMPLING_WIDTH_48 =        5,
+       XDRV_SAMPLING_WIDTH_64 =        6,
+};
+
+enum xdrv_i2s_mode {
+       XDRV_I2S_MODE_MASTER =          0,
+       XDRV_I2S_MODE_SLAVE =           1,
+};
+
+enum xdrv_i2s_clock {
+       XDRV_I2S_CLOCK_0 =              0,
+       XDRV_I2S_CLOCK_1 =              1,
+};
+
+enum xdrv_i2s_configuration_mode {
+       XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL =        0,
+       XDRV_I2S_CONFIGURATION_MODE_UPDATE_HW =         1,
+       XDRV_I2S_CONFIGURATION_MODE_UPDATE_TRANSDUCER = 2,
+};
+
+enum xdrv_i2s_settings {
+       XDRV_I2S_SETTINGS_NORMAL =      0,
+       XDRV_I2S_SETTINGS_SPECIAL1 =    1,
+       XDRV_I2S_SETTINGS_SPECIAL2 =    2,
+};
+
+enum xdrv_i2s_transmission_mode {
+       XDRV_I2S_TRANSMISSION_MODE_PCM =        0,
+       XDRV_I2S_TRANSMISSION_MODE_NORMAL =     1,
+       XDRV_IS2_TRANSMISSION_MODE_PCM_BURST =  2,
+};
+
+enum xdrv_source_transducer {
+       XDRV_SOURCE_TRANSDUCER_DEFAULT =                0,
+       XDRV_SOURCE_TRANSDUCER_HANDSET =                1,
+       XDRV_SOURCE_TRANSDUCER_HEADSET =                2,
+       XDRV_SOURCE_TRANSDUCER_HF =                     3,
+       XDRV_SOURCE_TRANSDUCER_AUX =                    4,
+       XDRV_SOURCE_TRANSDUCER_TTY =                    5,
+       XDRV_SOURCE_TRANSDUCER_BLUETOOTH =              6,
+       XDRV_SOURCE_TRANSDUCER_USER_DEFINED_15 =        21,
+};
+
+enum xdrv_dest_transducer {
+       XDRV_DEST_TRANSDUCER_DEFAULT =          0,
+       XDRV_DEST_TRANSDUCER_HANDSET =          1,
+       XDRV_DEST_TRANSDUCER_HEADSET =          2,
+       XDRV_DEST_TRANSDUCER_BACKSPEAKER =      3,
+       XDRV_DEST_TRANSDUCER_TTY =              6,
+       XDRV_DEST_TRANSDUCER_BLUETOOTH =        7,
+       XDRV_DEST_TRANSDUCER_USER_DEFINED_15 =  22,
+};
+
+enum xdrv_audio_mode {
+       XDRV_AUDIO_MODE_MONO =          0,
+       XDRV_AUDIO_MODE_DUAL_MONO =     1,
+       XDRV_AUDIO_MODE_STEREO =        2,
+       XDRV_AUDIO_MODE_DUAL_MONO_R =   3,
+       XDRV_AUDIO_MODE_DUAL_MONO_L =   4,
+};
+
+struct audio_settings_data {
+       GAtChat *chat;
+};
+
+static inline void xdrv_enable_source(GAtChat *chat, enum xdrv_source src)
+{
+       char buf[256];
+
+       sprintf(buf, "AT+XDRV=40,2,%i", src);
+       g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL);
+}
+
+static inline void xdrv_disable_source(GAtChat *chat, enum xdrv_source src)
+{
+       char buf[256];
+
+       sprintf(buf, "AT+XDRV=40,3,%i", src);
+       g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL);
+}
+
+static inline void xdrv_configure_source(GAtChat *chat, enum xdrv_source src,
+                               enum xdrv_i2s_clock clock,
+                               enum xdrv_i2s_mode master_slave,
+                               enum xdrv_sampling_rate sample_rate,
+                               enum xdrv_sampling_width bits,
+                               enum xdrv_i2s_transmission_mode tx_mode,
+                               enum xdrv_i2s_settings settings,
+                               enum xdrv_audio_mode mode,
+                               enum xdrv_i2s_configuration_mode config_mode,
+                               enum xdrv_source_transducer transducer_mode)
+{
+       char buf[256];
+       int ctx = 0; /* This is always 0 for now */
+
+       sprintf(buf, "AT+XDRV=40,4,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i",
+               src, ctx, clock, master_slave, sample_rate, bits,
+               tx_mode, settings, mode, config_mode, transducer_mode);
+       g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL);
+}
+
+static inline void xdrv_configure_destination(GAtChat *chat,
+                               enum xdrv_destination dest,
+                               enum xdrv_i2s_clock clock,
+                               enum xdrv_i2s_mode master_slave,
+                               enum xdrv_sampling_rate sample_rate,
+                               enum xdrv_sampling_width bits,
+                               enum xdrv_i2s_transmission_mode tx_mode,
+                               enum xdrv_i2s_settings settings,
+                               enum xdrv_audio_mode mode,
+                               enum xdrv_i2s_configuration_mode config_mode,
+                               enum xdrv_dest_transducer transducer_mode)
+{
+       char buf[256];
+       int ctx = 0; /* This is always 0 for now */
+
+       sprintf(buf, "AT+XDRV=40,5,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i",
+               dest, ctx, clock, master_slave, sample_rate, bits,
+               tx_mode, settings, mode, config_mode, transducer_mode);
+       g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL);
+}
+
+static inline void xdrv_set_destination_for_source(GAtChat *chat,
+                                               enum xdrv_source src,
+                                               enum xdrv_destination dest)
+{
+       char buf[256];
+
+       sprintf(buf, "AT+XDRV=40,6,%i,%i", src, dest);
+       g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL);
+}
+
+static inline void xdrv_set_destination_volume(GAtChat *chat,
+                                               enum xdrv_destination dest,
+                                               int volume)
+{
+       char buf[256];
+
+       sprintf(buf, "AT+XDRV=40,8,%i,%i", dest, volume);
+       g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL);
+}
+
+static void send_xdrv_setup_sequence(struct ofono_audio_settings *as)
+{
+       struct audio_settings_data *asd = ofono_audio_settings_get_data(as);
+
+       /* Mute */
+       xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_I2SX_TX, 0);
+       xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_SPEECH_TX, 0);
+
+       xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SPEECH_RX,
+                                       XDRV_DESTINATION_PCM_GENERAL);
+
+       xdrv_disable_source(asd->chat, XDRV_SOURCE_I2SX_RX);
+       xdrv_disable_source(asd->chat, XDRV_SOURCE_I2SY_RX);
+
+       xdrv_configure_source(asd->chat, XDRV_SOURCE_I2SX_RX, XDRV_I2S_CLOCK_1,
+                               XDRV_I2S_MODE_MASTER, XDRV_SAMPLING_RATE_48KHZ,
+                               XDRV_SAMPLING_WIDTH_16,
+                               XDRV_I2S_TRANSMISSION_MODE_NORMAL,
+                               XDRV_I2S_SETTINGS_NORMAL,
+                               XDRV_AUDIO_MODE_STEREO,
+                               XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL,
+                               XDRV_SOURCE_TRANSDUCER_USER_DEFINED_15);
+       xdrv_configure_destination(asd->chat, XDRV_DESTINATION_I2SX_TX,
+                                       XDRV_I2S_CLOCK_1, XDRV_I2S_MODE_MASTER,
+                                       XDRV_SAMPLING_RATE_48KHZ,
+                                       XDRV_SAMPLING_WIDTH_16,
+                                       XDRV_I2S_TRANSMISSION_MODE_NORMAL,
+                                       XDRV_I2S_SETTINGS_NORMAL,
+                                       XDRV_AUDIO_MODE_STEREO,
+                                       XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL,
+                                       XDRV_DEST_TRANSDUCER_USER_DEFINED_15);
+
+       xdrv_configure_source(asd->chat, XDRV_SOURCE_I2SY_RX, XDRV_I2S_CLOCK_0,
+                               XDRV_I2S_MODE_MASTER, XDRV_SAMPLING_RATE_48KHZ,
+                               XDRV_SAMPLING_WIDTH_16,
+                               XDRV_I2S_TRANSMISSION_MODE_NORMAL,
+                               XDRV_I2S_SETTINGS_NORMAL,
+                               XDRV_AUDIO_MODE_STEREO,
+                               XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL,
+                               XDRV_SOURCE_TRANSDUCER_USER_DEFINED_15);
+       xdrv_configure_destination(asd->chat, XDRV_DESTINATION_I2SY_TX,
+                                       XDRV_I2S_CLOCK_0, XDRV_I2S_MODE_MASTER,
+                                       XDRV_SAMPLING_RATE_48KHZ,
+                                       XDRV_SAMPLING_WIDTH_16,
+                                       XDRV_I2S_TRANSMISSION_MODE_NORMAL,
+                                       XDRV_I2S_SETTINGS_NORMAL,
+                                       XDRV_AUDIO_MODE_STEREO,
+                                       XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL,
+                                       XDRV_DEST_TRANSDUCER_USER_DEFINED_15);
+
+       /* Seems unnecessary
+       xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SPEECH_RX,
+                                       XDRV_DESTINATION_PCM_GENERAL);
+       */
+       xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_I2SX_RX,
+                                       XDRV_DESTINATION_SPEECH_TX);
+       xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_I2SY_RX,
+                                       XDRV_DESTINATION_I2SX_TX);
+       xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SIMPLE_TONES,
+                                       XDRV_DESTINATION_I2SX_TX);
+
+       xdrv_enable_source(asd->chat, XDRV_SOURCE_I2SX_RX);
+       xdrv_enable_source(asd->chat, XDRV_SOURCE_I2SY_RX);
+
+       xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SPEECH_RX,
+                                       XDRV_DESTINATION_I2SX_TX);
+
+       /* Unmute */
+       xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_I2SX_TX, 66);
+       xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_SPEECH_TX, 100);
+}
+
+static void xprogress_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_audio_settings *as = user_data;
+       GAtResultIter iter;
+       int id, status;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+XPROGRESS:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &id) == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &status) == FALSE)
+               return;
+
+       switch (status) {
+       case 0:
+       case 1:
+       case 4:
+       case 9:
+       case 10:
+       case 11:
+               ofono_audio_settings_active_notify(as, FALSE);
+               break;
+       case 2:
+       case 3:
+       case 5:
+       case 6:
+       case 7:
+       case 8:
+               ofono_audio_settings_active_notify(as, TRUE);
+               break;
+       }
+}
+
+static void xprogress_support_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_audio_settings *as = user_data;
+       struct audio_settings_data *asd = ofono_audio_settings_get_data(as);
+
+       if (!ok)
+               return;
+
+       g_at_chat_register(asd->chat, "+XPROGRESS:", xprogress_notify,
+                                                       FALSE, as, NULL);
+
+       g_at_chat_send(asd->chat, "AT+XPROGRESS=1", none_prefix,
+                                               NULL, NULL, NULL);
+
+       ofono_audio_settings_register(as);
+
+       send_xdrv_setup_sequence(as);
+}
+
+static int ifx_audio_settings_probe(struct ofono_audio_settings *as,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct audio_settings_data *asd;
+
+       asd = g_try_new0(struct audio_settings_data, 1);
+       if (asd == NULL)
+               return -ENOMEM;
+
+       asd->chat = g_at_chat_clone(chat);
+
+       ofono_audio_settings_set_data(as, asd);
+
+       g_at_chat_send(asd->chat, "AT+XPROGRESS=?", xprogress_prefix,
+                                       xprogress_support_cb, as, NULL);
+
+       return 0;
+}
+
+static void ifx_audio_settings_remove(struct ofono_audio_settings *as)
+{
+       struct audio_settings_data *asd = ofono_audio_settings_get_data(as);
+
+       ofono_audio_settings_set_data(as, NULL);
+
+       g_at_chat_unref(asd->chat);
+       g_free(asd);
+}
+
+static struct ofono_audio_settings_driver driver = {
+       .name           = "ifxmodem",
+       .probe          = ifx_audio_settings_probe,
+       .remove         = ifx_audio_settings_remove,
+};
+
+void ifx_audio_settings_init(void)
+{
+       ofono_audio_settings_driver_register(&driver);
+}
+
+void ifx_audio_settings_exit(void)
+{
+       ofono_audio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/ifxmodem/ctm.c b/drivers/ifxmodem/ctm.c
new file mode 100644 (file)
index 0000000..827bf3b
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/ctm.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "ifxmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *xctms_prefix[] = { "+XCTMS:", NULL };
+
+struct ctm_data {
+       GAtChat *chat;
+};
+
+static void xctms_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_ctm_query_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int value;
+       ofono_bool_t enable;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+XCTMS:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &value) == FALSE)
+               goto error;
+
+       /* FULL TTY mode status only sent to oFono */
+       enable = (value == 1) ? TRUE : FALSE;
+
+       cb(&error, enable, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void ifx_query_tty(struct ofono_ctm *ctm, ofono_ctm_query_cb_t cb,
+                               void *data)
+{
+       struct ctm_data *ctmd = ofono_ctm_get_data(ctm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(ctmd->chat, "AT+XCTMS?", xctms_prefix,
+                               xctms_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void xctms_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_ctm_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       /* TODO: Audio path configuration */
+
+       cb(&error, cbd->data);
+}
+
+static void ifx_set_tty(struct ofono_ctm *ctm, ofono_bool_t enable,
+                               ofono_ctm_set_cb_t cb, void *data)
+{
+       struct ctm_data *ctmd = ofono_ctm_get_data(ctm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[20];
+
+       /* Only FULL TTY mode enabled/disabled */
+       snprintf(buf, sizeof(buf), "AT+XCTMS=%i", enable ? 1 : 0);
+
+       if (g_at_chat_send(ctmd->chat, buf, none_prefix,
+                               xctms_modify_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void xctms_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_ctm *ctm = user_data;
+
+       if (ok)
+               ofono_ctm_register(ctm);
+}
+
+static int ifx_ctm_probe(struct ofono_ctm *ctm,
+                               unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct ctm_data *ctmd;
+
+       ctmd = g_try_new0(struct ctm_data, 1);
+       if (ctmd == NULL)
+               return -ENOMEM;
+
+       ctmd->chat = g_at_chat_clone(chat);
+
+       ofono_ctm_set_data(ctm, ctmd);
+
+       g_at_chat_send(ctmd->chat, "AT+XCTMS=?", xctms_prefix,
+                       xctms_support_cb, ctm, NULL);
+
+       return 0;
+}
+
+static void ifx_ctm_remove(struct ofono_ctm *ctm)
+{
+       struct ctm_data *ctmd = ofono_ctm_get_data(ctm);
+
+       ofono_ctm_set_data(ctm, NULL);
+
+       g_at_chat_unref(ctmd->chat);
+       g_free(ctmd);
+}
+
+static struct ofono_ctm_driver driver = {
+       .name           = "ifxmodem",
+       .probe          = ifx_ctm_probe,
+       .remove         = ifx_ctm_remove,
+       .query_tty      = ifx_query_tty,
+       .set_tty        = ifx_set_tty,
+};
+
+void ifx_ctm_init(void)
+{
+       ofono_ctm_driver_register(&driver);
+}
+
+void ifx_ctm_exit(void)
+{
+       ofono_ctm_driver_unregister(&driver);
+}
diff --git a/drivers/ifxmodem/gprs-context.c b/drivers/ifxmodem/gprs-context.c
new file mode 100644 (file)
index 0000000..6902f17
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gatrawip.h"
+
+#include "ifxmodem.h"
+
+#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
+
+#define STATIC_IP_NETMASK "255.255.255.255"
+
+static const char *none_prefix[] = { NULL };
+static const char *xdns_prefix[] = { "+XDNS:", NULL };
+static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
+
+enum state {
+       STATE_IDLE,
+       STATE_ENABLING,
+       STATE_DISABLING,
+       STATE_ACTIVE,
+};
+
+struct gprs_context_data {
+       GAtChat *chat;
+       unsigned int active_context;
+       char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+       char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+       GAtRawIP *rawip;
+       enum state state;
+       char address[32];
+       char dns1[32];
+       char dns2[32];
+       ofono_gprs_context_cb_t cb;
+       void *cb_data;                                  /* Callback data */
+};
+
+static void rawip_debug(const char *str, void *data)
+{
+       ofono_info("%s: %s", (const char *) data, str);
+}
+
+static const char *setup_rawip(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtIO *io;
+
+       DBG("");
+
+       io = g_at_chat_get_io(gcd->chat);
+
+       g_at_chat_suspend(gcd->chat);
+
+       gcd->rawip = g_at_rawip_new_from_io(io);
+
+       if (gcd->rawip == NULL) {
+               g_at_chat_resume(gcd->chat);
+               return NULL;
+       }
+
+       if (getenv("OFONO_IP_DEBUG"))
+               g_at_rawip_set_debug(gcd->rawip, rawip_debug, "IP");
+
+       g_at_rawip_open(gcd->rawip);
+
+       return g_at_rawip_get_interface(gcd->rawip);
+}
+
+static void failed_setup(struct ofono_gprs_context *gc,
+                               GAtResult *result, gboolean deactivate)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+       char buf[64];
+
+       DBG("deactivate %d", deactivate);
+
+       if (deactivate == TRUE) {
+               sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+               g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+       }
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+
+       if (result == NULL) {
+               CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+               return;
+       }
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       gcd->cb(&error, gcd->cb_data);
+}
+
+static void session_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       const char *interface;
+       const char *dns[3];
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               ofono_error("Failed to establish session");
+               failed_setup(gc, result, TRUE);
+               return;
+       }
+
+       gcd->state = STATE_ACTIVE;
+
+       dns[0] = gcd->dns1;
+       dns[1] = gcd->dns2;
+       dns[2] = 0;
+
+       interface = setup_rawip(gc);
+       if (interface == NULL)
+               interface = "invalid";
+
+       ofono_gprs_context_set_interface(gc, interface);
+       ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE);
+       ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK);
+       ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+
+       gcd->cb = NULL;
+       gcd->cb_data = NULL;
+}
+
+static void dns_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[64];
+       int cid;
+       const char *dns1, *dns2;
+       GAtResultIter iter;
+       gboolean found = FALSE;
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               ofono_error("Unable to get DNS details");
+               failed_setup(gc, result, TRUE);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+XDNS:")) {
+               if (!g_at_result_iter_next_number(&iter, &cid))
+                       goto error;
+
+               if (!g_at_result_iter_next_string(&iter, &dns1))
+                       goto error;
+
+               if (!g_at_result_iter_next_string(&iter, &dns2))
+                       goto error;
+
+               if ((unsigned int) cid == gcd->active_context) {
+                       found = TRUE;
+                       strncpy(gcd->dns1, dns1, sizeof(gcd->dns1));
+                       strncpy(gcd->dns2, dns2, sizeof(gcd->dns2));
+               }
+       }
+
+       if (found == FALSE)
+               goto error;
+
+       ofono_info("IP: %s", gcd->address);
+       ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
+
+       sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context);
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                                       session_cb, gc, NULL) > 0)
+               return;
+
+error:
+       failed_setup(gc, NULL, TRUE);
+}
+
+static void address_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       int cid;
+       const char *address;
+       GAtResultIter iter;
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               ofono_error("Unable to get context address");
+               failed_setup(gc, result, TRUE);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CGPADDR:"))
+               goto error;
+
+       if (!g_at_result_iter_next_number(&iter, &cid))
+               goto error;
+
+       if ((unsigned int) cid != gcd->active_context)
+               goto error;
+
+       if (!g_at_result_iter_next_string(&iter, &address))
+               goto error;
+
+       strncpy(gcd->address, address, sizeof(gcd->address));
+
+       if (g_at_chat_send(gcd->chat, "AT+XDNS?", xdns_prefix,
+                                       dns_cb, gc, NULL) > 0)
+               return;
+
+error:
+       failed_setup(gc, NULL, TRUE);
+}
+
+static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[64];
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               ofono_error("Unable to activate context");
+               failed_setup(gc, result, FALSE);
+               return;
+       }
+
+       sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
+       if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
+                                       address_cb, gc, NULL) > 0)
+               return;
+
+       failed_setup(gc, NULL, TRUE);
+}
+
+static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[128];
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               ofono_error("Failed to setup context");
+               failed_setup(gc, result, FALSE);
+               return;
+       }
+
+       if (gcd->username[0] && gcd->password[0])
+               sprintf(buf, "AT+XGAUTH=%u,1,\"%s\",\"%s\"",
+                       gcd->active_context, gcd->username, gcd->password);
+       else
+               sprintf(buf, "AT+XGAUTH=%u,0,\"\",\"\"", gcd->active_context);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+               goto error;
+
+       sprintf(buf, "AT+XDNS=%u,1", gcd->active_context);
+       if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+               goto error;
+
+       sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               activate_cb, gc, NULL) > 0)
+               return;
+
+error:
+       failed_setup(gc, NULL, FALSE);
+}
+
+static void ifx_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+       int len;
+
+       /* IPv6 support not implemented */
+       if (ctx->proto != OFONO_GPRS_PROTO_IP)
+               goto error;
+
+       DBG("cid %u", ctx->cid);
+
+       gcd->active_context = ctx->cid;
+       gcd->cb = cb;
+       gcd->cb_data = data;
+       memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+       memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+
+       gcd->state = STATE_ENABLING;
+
+       len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len - 3,
+                                       ",\"%s\"", ctx->apn);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               setup_cb, gc, NULL) > 0)
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void deactivate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("ok %d", ok);
+
+       g_at_rawip_unref(gcd->rawip);
+       gcd->rawip = NULL;
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+
+       g_at_chat_resume(gcd->chat);
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+}
+
+static void ifx_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int cid,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtChat *chat = g_at_chat_get_slave(gcd->chat);
+       char buf[64];
+
+       DBG("cid %u", cid);
+
+       gcd->state = STATE_DISABLING;
+       gcd->cb = cb;
+       gcd->cb_data = data;
+
+       g_at_rawip_shutdown(gcd->rawip);
+
+       sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               deactivate_cb, gc, NULL) > 0)
+               return;
+
+       CALLBACK_WITH_SUCCESS(cb, data);
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       const char *event;
+       int cid;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CGEV:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+               return;
+
+       if (g_str_has_prefix(event, "NW DEACT") == FALSE)
+               return;
+
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &cid))
+               return;
+
+       DBG("cid %d", cid);
+
+       if ((unsigned int) cid != gcd->active_context)
+               return;
+
+       if (gcd->state != STATE_IDLE && gcd->rawip) {
+               g_at_rawip_shutdown(gcd->rawip);
+
+               g_at_rawip_unref(gcd->rawip);
+               gcd->rawip = NULL;
+       }
+
+       ofono_gprs_context_deactivated(gc, gcd->active_context);
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+
+       g_at_chat_resume(gcd->chat);
+}
+
+static int ifx_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+       struct stat st;
+
+       DBG("");
+
+       if (stat(TUN_SYSFS_DIR, &st) < 0) {
+               ofono_error("Missing support for TUN/TAP devices");
+               return -ENODEV;
+       }
+
+       if (g_at_chat_get_slave(chat) == NULL)
+               return -EINVAL;
+
+       gcd = g_try_new0(struct gprs_context_data, 1);
+       if (gcd == NULL)
+               return -ENOMEM;
+
+       gcd->chat = g_at_chat_clone(chat);
+
+       ofono_gprs_context_set_data(gc, gcd);
+
+       chat = g_at_chat_get_slave(gcd->chat);
+
+       g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+       return 0;
+}
+
+static void ifx_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("");
+
+       if (gcd->state != STATE_IDLE && gcd->rawip) {
+               g_at_rawip_unref(gcd->rawip);
+               g_at_chat_resume(gcd->chat);
+       }
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "ifxmodem",
+       .probe                  = ifx_gprs_context_probe,
+       .remove                 = ifx_gprs_context_remove,
+       .activate_primary       = ifx_gprs_activate_primary,
+       .deactivate_primary     = ifx_gprs_deactivate_primary,
+};
+
+void ifx_gprs_context_init(void)
+{
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void ifx_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/ifxmodem/ifxmodem.c b/drivers/ifxmodem/ifxmodem.c
new file mode 100644 (file)
index 0000000..7e293af
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "ifxmodem.h"
+
+static int ifxmodem_init(void)
+{
+       ifx_voicecall_init();
+       ifx_audio_settings_init();
+       ifx_radio_settings_init();
+       ifx_gprs_context_init();
+       ifx_stk_init();
+       ifx_ctm_init();
+
+       return 0;
+}
+
+static void ifxmodem_exit(void)
+{
+       ifx_stk_exit();
+       ifx_gprs_context_exit();
+       ifx_radio_settings_exit();
+       ifx_audio_settings_exit();
+       ifx_voicecall_exit();
+       ifx_ctm_exit();
+}
+
+OFONO_PLUGIN_DEFINE(ifxmodem, "Infineon modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       ifxmodem_init, ifxmodem_exit)
diff --git a/drivers/ifxmodem/ifxmodem.h b/drivers/ifxmodem/ifxmodem.h
new file mode 100644 (file)
index 0000000..43badc9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void ifx_voicecall_init(void);
+extern void ifx_voicecall_exit(void);
+
+extern void ifx_audio_settings_init(void);
+extern void ifx_audio_settings_exit(void);
+
+extern void ifx_radio_settings_init(void);
+extern void ifx_radio_settings_exit(void);
+
+extern void ifx_gprs_context_init(void);
+extern void ifx_gprs_context_exit(void);
+
+extern void ifx_stk_init(void);
+extern void ifx_stk_exit(void);
+
+extern void ifx_ctm_init(void);
+extern void ifx_ctm_exit(void);
\ No newline at end of file
diff --git a/drivers/ifxmodem/radio-settings.c b/drivers/ifxmodem/radio-settings.c
new file mode 100644 (file)
index 0000000..080f7ee
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "ifxmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *xrat_prefix[] = { "+XRAT:", NULL };
+
+struct radio_settings_data {
+       GAtChat *chat;
+};
+
+static void xrat_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+       enum ofono_radio_access_mode mode;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int value, preferred;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+XRAT:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &value) == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &preferred) == FALSE)
+               goto error;
+
+       switch (value) {
+       case 0:
+               mode = OFONO_RADIO_ACCESS_MODE_GSM;
+               break;
+       case 1:
+               mode = OFONO_RADIO_ACCESS_MODE_ANY;
+               break;
+       case 2:
+               mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+               break;
+       default:
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       cb(&error, mode, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void ifx_query_rat_mode(struct ofono_radio_settings *rs,
+                               ofono_radio_settings_rat_mode_query_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(rsd->chat, "AT+XRAT?", xrat_prefix,
+                                       xrat_query_cb, cbd, g_free) == 0) {
+               CALLBACK_WITH_FAILURE(cb, -1, data);
+               g_free(cbd);
+       }
+}
+
+static void xrat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void ifx_set_rat_mode(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode,
+                               ofono_radio_settings_rat_mode_set_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[20];
+       int value = 1, preferred = 2;
+
+       switch (mode) {
+       case OFONO_RADIO_ACCESS_MODE_ANY:
+               value = 1;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_GSM:
+               value = 0;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_UMTS:
+               value = 2;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_LTE:
+               goto error;
+       }
+
+       if (value == 1)
+               snprintf(buf, sizeof(buf), "AT+XRAT=%u,%u", value, preferred);
+       else
+               snprintf(buf, sizeof(buf), "AT+XRAT=%u", value);
+
+       if (g_at_chat_send(rsd->chat, buf, none_prefix,
+                                       xrat_modify_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void xrat_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_radio_settings *rs = user_data;
+
+       if (!ok)
+               return;
+
+       ofono_radio_settings_register(rs);
+}
+
+static int ifx_radio_settings_probe(struct ofono_radio_settings *rs,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct radio_settings_data *rsd;
+
+       rsd = g_try_new0(struct radio_settings_data, 1);
+       if (rsd == NULL)
+               return -ENOMEM;
+
+       rsd->chat = g_at_chat_clone(chat);
+
+       ofono_radio_settings_set_data(rs, rsd);
+
+       g_at_chat_send(rsd->chat, "AT+XRAT=?", xrat_prefix,
+                                       xrat_support_cb, rs, NULL);
+
+       return 0;
+}
+
+static void ifx_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+
+       ofono_radio_settings_set_data(rs, NULL);
+
+       g_at_chat_unref(rsd->chat);
+       g_free(rsd);
+}
+
+static struct ofono_radio_settings_driver driver = {
+       .name                   = "ifxmodem",
+       .probe                  = ifx_radio_settings_probe,
+       .remove                 = ifx_radio_settings_remove,
+       .query_rat_mode         = ifx_query_rat_mode,
+       .set_rat_mode           = ifx_set_rat_mode
+};
+
+void ifx_radio_settings_init(void)
+{
+       ofono_radio_settings_driver_register(&driver);
+}
+
+void ifx_radio_settings_exit(void)
+{
+       ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/ifxmodem/stk.c b/drivers/ifxmodem/stk.c
new file mode 100644 (file)
index 0000000..22aac62
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/stk.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "ifxmodem.h"
+
+struct stk_data {
+       GAtChat *chat;
+};
+
+static const char *none_prefix[] = { NULL };
+static const char *sate_prefix[] = { "+SATE:", NULL };
+static const char *xsatk_prefix[] = { "+XSATK:", NULL };
+
+static void sate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_envelope_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       struct ofono_error error;
+       int sw1, sw2, envelope, event;
+       const guint8 *pdu = NULL;
+       gint len = 0;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto done;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+SATE:") == FALSE)
+               goto done;
+
+       if (g_at_result_iter_next_number(&iter, &sw1) == FALSE)
+               goto done;
+
+       if (g_at_result_iter_next_number(&iter, &sw2) == FALSE)
+               goto done;
+
+       if (g_at_result_iter_next_number(&iter, &envelope) == FALSE)
+               goto done;
+
+       if (g_at_result_iter_next_number(&iter, &event) == FALSE)
+               goto done;
+
+       DBG("sw1 %d sw2 %d envelope %d event %d", sw1, sw2, envelope, event);
+
+       /* Response data is optional */
+       g_at_result_iter_next_hexstring(&iter, &pdu, &len);
+
+       DBG("len %d", len);
+
+done:
+       cb(&error, pdu, len, cbd->data);
+}
+
+static void ifx_stk_envelope(struct ofono_stk *stk, int length,
+                               const unsigned char *command,
+                               ofono_stk_envelope_cb_t cb, void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, 64 + length * 2);
+       int len;
+
+       DBG("");
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT+SATE=\"");
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *command++);
+       len += sprintf(buf + len, "\"");
+
+       DBG("%s", buf);
+
+       if (g_at_chat_send(sd->chat, buf, sate_prefix,
+                                       sate_cb, cbd, g_free) > 0) {
+               g_free(buf);
+               return;
+       }
+
+error:
+       g_free(buf);
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void satr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_generic_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void ifx_stk_terminal_response(struct ofono_stk *stk, int length,
+                                       const unsigned char *command,
+                                       ofono_stk_generic_cb_t cb, void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, 64 + length * 2);
+       int len;
+
+       DBG("");
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT+SATR=\"");
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *command++);
+       len += sprintf(buf + len, "\"");
+
+       DBG("%s", buf);
+
+       if (g_at_chat_send(sd->chat, buf, none_prefix,
+                                       satr_cb, cbd, g_free) > 0) {
+               g_free(buf);
+               return;
+       }
+
+error:
+       g_free(buf);
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ifx_stk_user_confirmation(struct ofono_stk *stk, gboolean confirm)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       char buf[20];
+
+       snprintf(buf, sizeof(buf), "AT+SATD=%i", confirm ? 1 : 0);
+
+       g_at_chat_send(sd->chat, buf, none_prefix, NULL, NULL, NULL);
+}
+
+static void sati_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *pdu;
+       gint len;
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+SATI:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE)
+               return;
+
+       DBG("len %d", len);
+
+       ofono_stk_proactive_command_notify(stk, len, pdu);
+}
+
+static void satn_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *pdu;
+       gint len;
+
+       DBG("");
+
+       /* Proactive command has been handled by the modem. */
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+SATN:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE)
+               return;
+
+       if (len == 0)
+               return;
+
+       ofono_stk_proactive_command_handled_notify(stk, len, pdu);
+}
+
+static void satf_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       int sw1, sw2;
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+SATF:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &sw1) == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &sw2) == FALSE)
+               return;
+
+       DBG("sw1 %d sw2 %d", sw1, sw2);
+
+       if (sw1 == 0x90 && sw2 == 0x00)
+               ofono_stk_proactive_session_end_notify(stk);
+}
+
+static void xsatk_support_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       DBG("");
+
+       if (!ok)
+               return;
+
+       g_at_chat_register(sd->chat, "+SATI:", sati_notify, FALSE, stk, NULL);
+       g_at_chat_register(sd->chat, "+SATN:", satn_notify, FALSE, stk, NULL);
+       g_at_chat_register(sd->chat, "+SATF:", satf_notify, FALSE, stk, NULL);
+
+       g_at_chat_send(sd->chat, "AT+XSATK=1,1", none_prefix, NULL, NULL, NULL);
+
+       ofono_stk_register(stk);
+}
+
+static int ifx_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct stk_data *sd;
+
+       DBG("");
+
+       sd = g_try_new0(struct stk_data, 1);
+       if (sd == NULL)
+               return -ENOMEM;
+
+       sd->chat = g_at_chat_clone(chat);
+
+       ofono_stk_set_data(stk, sd);
+
+       g_at_chat_send(sd->chat, "AT+XSATK=?", xsatk_prefix, xsatk_support_cb,
+                       stk, NULL);
+
+       return 0;
+}
+
+static void ifx_stk_remove(struct ofono_stk *stk)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       DBG("");
+
+       ofono_stk_set_data(stk, NULL);
+
+       g_at_chat_unref(sd->chat);
+       g_free(sd);
+}
+
+static struct ofono_stk_driver driver = {
+       .name                   = "ifxmodem",
+       .probe                  = ifx_stk_probe,
+       .remove                 = ifx_stk_remove,
+       .envelope               = ifx_stk_envelope,
+       .terminal_response      = ifx_stk_terminal_response,
+       .user_confirmation      = ifx_stk_user_confirmation,
+};
+
+void ifx_stk_init(void)
+{
+       ofono_stk_driver_register(&driver);
+}
+
+void ifx_stk_exit(void)
+{
+       ofono_stk_driver_unregister(&driver);
+}
diff --git a/drivers/ifxmodem/voicecall.c b/drivers/ifxmodem/voicecall.c
new file mode 100644 (file)
index 0000000..13ed90b
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "common.h"
+#include "ifxmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *xlema_prefix[] = { "+XLEMA:", NULL };
+
+struct voicecall_data {
+       GSList *calls;
+       unsigned int local_release;
+       GAtChat *chat;
+       char **en_list;
+};
+
+struct release_id_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int id;
+};
+
+struct change_state_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int affected_types;
+};
+
+static int class_to_call_type(int cls)
+{
+       switch (cls) {
+       case 1:
+               return 0;
+       case 4:
+               return 2;
+       case 8:
+               return 9;
+       default:
+               return 1;
+       }
+}
+
+static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
+                                       int direction, int status,
+                                       const char *num, int num_type,
+                                       int clip, int id)
+{
+       struct voicecall_data *d = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* Generate a call structure for the waiting call */
+       call = g_try_new(struct ofono_call, 1);
+       if (call == NULL)
+               return NULL;
+
+       ofono_call_init(call);
+
+       call->id = id;
+       call->type = type;
+       call->direction = direction;
+       call->status = status;
+
+       if (clip != 2) {
+               strncpy(call->phone_number.number, num,
+                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = num_type;
+       }
+
+       call->clip_validity = clip;
+
+       d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
+
+       return call;
+}
+
+static void xcallstat_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       int id;
+       int status;
+       GSList *l;
+       struct ofono_call *new_call;
+       struct ofono_call *existing_call = NULL;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+XCALLSTAT:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &id) == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &status) == FALSE)
+               return;
+
+       l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(id),
+                               at_util_call_compare_by_id);
+
+       if (l == NULL && status != CALL_STATUS_DIALING &&
+                               status != CALL_STATUS_INCOMING &&
+                               status != CALL_STATUS_WAITING) {
+               ofono_error("Received XCALLSTAT for an untracked"
+                               " call, this indicates a bug!");
+               return;
+       }
+
+       if (l)
+               existing_call = l->data;
+
+       switch (status) {
+       case CALL_STATUS_DISCONNECTED:
+       {
+               enum ofono_disconnect_reason reason;
+
+               existing_call->status = status;
+
+               if (vd->local_release & (1 << existing_call->id))
+                       reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+               else
+                       reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+
+               ofono_voicecall_disconnected(vc, existing_call->id,
+                                               reason, NULL);
+
+               vd->local_release &= ~(1 << existing_call->id);
+               vd->calls = g_slist_remove(vd->calls, l->data);
+               g_free(existing_call);
+               break;
+       }
+       case CALL_STATUS_DIALING:
+       case CALL_STATUS_WAITING:
+       case CALL_STATUS_INCOMING:
+       {
+               int direction;
+
+               /* Handle the following situation:
+                * Active Call + Waiting Call. Active Call is Released.
+                * The Waiting call becomes Incoming. In this case, no
+                * need to create a new call. Call status change will be
+                * triggered from clip_notify.
+                */
+               if (existing_call) {
+                       existing_call->status = status;
+                       return;
+               }
+
+               if (status == CALL_STATUS_DIALING)
+                       direction = CALL_DIRECTION_MOBILE_ORIGINATED;
+               else
+                       direction = CALL_DIRECTION_MOBILE_TERMINATED;
+
+               new_call = create_call(vc, 0, direction, status,
+                                       NULL, 128,
+                                       CLIP_VALIDITY_NOT_AVAILABLE, id);
+               if (new_call == NULL) {
+                       ofono_error("Unable to malloc. "
+                                       "Call management is fubar");
+                       return;
+               }
+
+               new_call->id = id;
+               break;
+       }
+       case CALL_STATUS_ALERTING:
+       case CALL_STATUS_ACTIVE:
+       case CALL_STATUS_HELD:
+       default:
+               /* For connected status, simply reset back to active */
+               if (status == 7)
+                       status = CALL_STATUS_ACTIVE;
+
+               existing_call->status = status;
+               ofono_voicecall_notify(vc, existing_call);
+               break;
+       }
+}
+
+static void xem_notify(GAtResult *result, gpointer user_data)
+{
+       //struct ofono_voicecall *vc = user_data;
+       //struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       int state;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+XEM:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &state) == FALSE)
+               return;
+
+       DBG("state %d", state);
+
+       switch (state) {
+       case 0:
+               ofono_info("Emergency call is finished");
+               break;
+       case 1:
+               ofono_info("Emergency call is entered");
+               break;
+       }
+}
+
+static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct change_state_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok && req->affected_types) {
+               GSList *l;
+               struct ofono_call *call;
+
+               for (l = vd->calls; l; l = l->next) {
+                       call = l->data;
+
+                       if (req->affected_types & (1 << call->status))
+                               vd->local_release |= (1 << call->id);
+               }
+       }
+
+       req->cb(&error, req->data);
+}
+
+static void release_id_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct release_id_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok)
+               vd->local_release |= 1 << req->id;
+
+       req->cb(&error, req->data);
+}
+
+static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       /* Let oFono core will generate a call with the dialed number
+        * inside its dial callback.
+        */
+       cb(&error, cbd->data);
+}
+
+static void ifx_dial(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+                       void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[256];
+
+       cbd->user = vc;
+
+       if (ph->type == 145)
+               snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
+       else
+               snprintf(buf, sizeof(buf), "ATD%s", ph->number);
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_INVOCATION:
+               strcat(buf, "I");
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               strcat(buf, "i");
+               break;
+       default:
+               break;
+       }
+
+       strcat(buf, ";");
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               atd_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ifx_template(const char *cmd, struct ofono_voicecall *vc,
+                       GAtResultFunc result_cb, unsigned int affected_types,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->affected_types = affected_types;
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               result_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ifx_answer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       ifx_template("ATA", vc, generic_cb, 0, cb, data);
+}
+
+static void ifx_ath(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Hangup active + held call, but not waiting */
+       ifx_template("ATH", vc, generic_cb, 0x1f, cb, data);
+}
+
+static void ifx_chup(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Hangup active + but not held or waiting */
+       ifx_template("AT+CHUP", vc, generic_cb, 0x1d, cb, data);
+}
+
+static void ifx_hold_all_active(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       ifx_template("AT+CHLD=2", vc, generic_cb, 0, cb, data);
+}
+
+static void ifx_release_all_held(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int held_status = 1 << CALL_STATUS_HELD;
+       ifx_template("AT+CHLD=0", vc, generic_cb, held_status, cb, data);
+}
+
+static void ifx_set_udub(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int incoming_or_waiting =
+               (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
+
+       ifx_template("AT+CHLD=0", vc, generic_cb, incoming_or_waiting,
+                       cb, data);
+}
+
+static void ifx_release_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       ifx_template("AT+CHLD=1", vc, generic_cb, 0x1, cb, data);
+}
+
+static void ifx_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct release_id_req *req = g_try_new0(struct release_id_req, 1);
+       char buf[32];
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->id = id;
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id);
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               release_id_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ifx_private_chat(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id);
+       ifx_template(buf, vc, generic_cb, 0, cb, data);
+}
+
+static void ifx_create_multiparty(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       ifx_template("AT+CHLD=3", vc, generic_cb, 0, cb, data);
+}
+
+static void ifx_transfer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Held & Active */
+       unsigned int transfer = 0x1 | 0x2;
+
+       /* Transfer can puts held & active calls together and disconnects
+        * from both.  However, some networks support transferring of
+        * dialing/ringing calls as well.
+        */
+       transfer |= 0x4 | 0x8;
+
+       ifx_template("AT+CHLD=4", vc, generic_cb, transfer, cb, data);
+}
+
+static void ifx_deflect(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[128];
+       unsigned int incoming_or_waiting =
+               (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
+
+       snprintf(buf, sizeof(buf), "AT+CTFR=%s,%d", ph->number, ph->type);
+       ifx_template(buf, vc, generic_cb, incoming_or_waiting, cb, data);
+}
+
+static void ifx_swap_without_accept(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       ifx_template("AT+CHLD=6", vc, generic_cb, 0, cb, data);
+}
+
+static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void ifx_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       int len = strlen(dtmf);
+       int s;
+       int i;
+       char *buf;
+
+       /* strlen("+VTS=T\;") = 7 + initial AT + null */
+       buf = g_try_new(char, len * 7 + 3);
+       if (buf == NULL)
+               goto error;
+
+       s = sprintf(buf, "AT+VTS=%c", dtmf[0]);
+
+       for (i = 1; i < len; i++)
+               s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
+
+       s = g_at_chat_send(vd->chat, buf, none_prefix,
+                               vts_cb, cbd, g_free);
+
+       g_free(buf);
+
+       if (s > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void cring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *line;
+       GSList *l;
+       struct ofono_call *call;
+
+       /* Handle the following situation:
+        * Active Call + Waiting Call.  Active Call is Released.  The Waiting
+        * call becomes Incoming and CRING indications are signaled.
+        * Sometimes these arrive before the actual state change notification.
+        * If this happens, simply ignore the CRING when a waiting call
+        * exists (cannot have waiting + incoming in GSM)
+        */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CRING received before XCALLSTAT!!!");
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CRING:"))
+               return;
+
+       line = g_at_result_iter_raw_line(&iter);
+       if (line == NULL)
+               return;
+
+       call = l->data;
+
+       /* Ignore everything that is not voice for now */
+       if (!strcasecmp(line, "VOICE"))
+               call->type = 0;
+       else
+               call->type = 9;
+
+       /* Assume the CLIP always arrives, and we signal the call there */
+       DBG("cring_notify");
+}
+
+static void clip_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int type, validity;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CLIP for unknown call");
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLIP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               return;
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       /* Skip subaddr, satype and alpha */
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("clip_notify: %s %d %d", num, type, validity);
+
+       call = l->data;
+
+       strncpy(call->phone_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->phone_number.type = type;
+       call->clip_validity = validity;
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+}
+
+static void cnap_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *name;
+       int validity;
+       GSList *l;
+       struct ofono_call *call;
+
+       /*
+        * Currently, its not clear which URC will contain the
+        * calling party name for the waiting call.
+        */
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CNAP for unknown call");
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CNAP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &name))
+               return;
+
+       if (strlen(name) > 0)
+               validity = CNAP_VALIDITY_VALID;
+       else
+               validity = CNAP_VALIDITY_NOT_AVAILABLE;
+
+       /* If we have CNI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d", name, validity);
+
+       call = l->data;
+
+       strncpy(call->name, name, OFONO_MAX_CALLER_NAME_LENGTH);
+       call->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0';
+       call->cnap_validity = validity;
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+}
+
+static void ccwa_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int num_type, validity, cls;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CCWA received before XCALLSTAT!!!");
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CCWA:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &num_type))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &cls))
+               return;
+
+       /* Skip alpha field */
+       g_at_result_iter_skip_next(&iter);
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("ccwa_notify: %s %d %d %d", num, num_type, cls, validity);
+
+       call = l->data;
+
+       call->type = class_to_call_type(cls);
+       strncpy(call->phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->phone_number.type = num_type;
+       call->clip_validity = validity;
+
+       if (call->type == 0) /* Only notify voice calls */
+               ofono_voicecall_notify(vc, call);
+}
+
+static void xcolp_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int type, call_id;
+       GSList *l;
+       struct ofono_call *call;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XCOLP:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_id))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               return;
+
+       if (strlen(num) == 0) {
+               ofono_error("XCOLP received with invalid number!!!");
+               return;
+       }
+
+       DBG("xcolp_notify: %d %s %d", call_id, num, type);
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(call_id),
+                               at_util_call_compare_by_id);
+       if (l == NULL) {
+               ofono_error("XCOLP for unknown call");
+               return;
+       }
+
+       call = l->data;
+
+       strncpy(call->phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->phone_number.type = type;
+       call->clip_validity = CLIP_VALIDITY_VALID;
+
+       ofono_voicecall_notify(vc, call);
+}
+
+static void xlema_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       int index, total_cnt;
+       const char *number;
+       int len;
+       int count = (vd->en_list == NULL) ? 0 : g_strv_length(vd->en_list);
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XLEMA:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &index))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &total_cnt))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &number))
+               return;
+
+       /* Skip category */
+       if (g_at_result_iter_skip_next(&iter) == FALSE)
+               goto done;
+
+       /* Skip presence */
+       if (g_at_result_iter_skip_next(&iter) == FALSE)
+               goto done;
+
+       /* If we succeed here, then the number is from NVM or NITZ */
+       if (g_at_result_iter_skip_next(&iter) == FALSE)
+               goto done;
+
+       if (vd->en_list == NULL)
+               vd->en_list = g_new0(char *, total_cnt + 1);
+
+       len = strspn(number, "0123456789");
+       vd->en_list[count] = g_strndup(number, len);
+
+       if (number[len] != '\0')
+               ofono_warn("Malformed emergency number: %.*s", len, number);
+
+done:
+       if (index != total_cnt)
+               return;
+
+       if (vd->en_list) {
+               ofono_voicecall_en_list_notify(vc, vd->en_list);
+
+               g_strfreev(vd->en_list);
+               vd->en_list = NULL;
+       }
+}
+
+static void xlema_read(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       int num = 0;
+       int index, total_cnt;
+       const char *number;
+       int len;
+
+       if (!ok) {
+               DBG("Emergency number list read failed");
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+XLEMA:"))
+               num += 1;
+
+       vd->en_list = g_new0(char *, num + 1);
+
+       num = 0;
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+XLEMA:")) {
+               if (!g_at_result_iter_next_number(&iter, &index))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &total_cnt))
+                       continue;
+
+               if (!g_at_result_iter_next_string(&iter, &number))
+                       continue;
+
+               len = strspn(number, "0123456789");
+               vd->en_list[num++] = g_strndup(number, len);
+
+               if (number[len] != '\0')
+                       ofono_warn("Malformed emergency number: %.*s",
+                                                       len, number);
+       }
+
+       ofono_voicecall_en_list_notify(vc, vd->en_list);
+
+       g_strfreev(vd->en_list);
+       vd->en_list = NULL;
+}
+
+static void ifx_voicecall_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       DBG("voicecall_init: registering to notifications");
+
+       g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CNAP:", cnap_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+XEM:", xem_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+XCALLSTAT:", xcallstat_notify,
+                                                       FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+XCOLP:", xcolp_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+XLEMA:", xlema_notify, FALSE, vc, NULL);
+       /* Enable emergency number list notification */
+       g_at_chat_send(vd->chat, "AT+XLEMA=1", xlema_prefix, xlema_read, vc,
+                                                                       NULL);
+
+       ofono_voicecall_register(vc);
+}
+
+static int ifx_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_try_new0(struct voicecall_data, 1);
+       if (vd == NULL)
+               return -ENOMEM;
+
+       vd->chat = g_at_chat_clone(chat);
+
+       ofono_voicecall_set_data(vc, vd);
+
+       g_at_chat_send(vd->chat, "AT+XCALLSTAT=1", none_prefix, NULL, NULL,
+                       NULL);
+       g_at_chat_send(vd->chat, "AT+XEMC=1", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+XCOLP=1", none_prefix, NULL, NULL, NULL);
+
+       g_at_chat_send(vd->chat, "AT+CRC=1", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CLIP=1", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CNAP=1", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CCWA=1", none_prefix,
+                               ifx_voicecall_initialized, vc, NULL);
+
+       return 0;
+}
+
+static void ifx_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+       g_slist_free(vd->calls);
+
+       g_strfreev(vd->en_list);
+
+       ofono_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "ifxmodem",
+       .probe                  = ifx_voicecall_probe,
+       .remove                 = ifx_voicecall_remove,
+       .dial                   = ifx_dial,
+       .answer                 = ifx_answer,
+       .hangup_all             = ifx_ath,
+       .hangup_active          = ifx_chup,
+       .hold_all_active        = ifx_hold_all_active,
+       .release_all_held       = ifx_release_all_held,
+       .set_udub               = ifx_set_udub,
+       .release_all_active     = ifx_release_all_active,
+       .release_specific       = ifx_release_specific,
+       .private_chat           = ifx_private_chat,
+       .create_multiparty      = ifx_create_multiparty,
+       .transfer               = ifx_transfer,
+       .deflect                = ifx_deflect,
+       .swap_without_accept    = ifx_swap_without_accept,
+       .send_tones             = ifx_send_dtmf
+};
+
+void ifx_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void ifx_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/audio-settings.c b/drivers/isimodem/audio-settings.c
new file mode 100644 (file)
index 0000000..3dc2796
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/audio-settings.h>
+
+#include <gisi/modem.h>
+#include <gisi/client.h>
+#include <gisi/message.h>
+
+#include "isiutil.h"
+#include "isimodem.h"
+#include "call.h"
+#include "debug.h"
+
+struct audio_settings_data {
+       GIsiClient *client;
+};
+
+static void isi_call_server_status_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_audio_settings *oas = data;
+       uint8_t status;
+
+       if (g_isi_msg_id(msg) != CALL_SERVER_STATUS_IND)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &status))
+               return;
+
+       ofono_audio_settings_active_notify(oas, status ? TRUE : FALSE);
+}
+
+static void isi_call_verify_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_audio_settings *as = data;
+       struct audio_settings_data *asd = ofono_audio_settings_get_data(as);
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_audio_settings_remove(as);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_ind_subscribe(asd->client, CALL_SERVER_STATUS_IND,
+                                       isi_call_server_status_ind_cb,
+                                       as);
+
+       ofono_audio_settings_register(as);
+}
+
+static int isi_audio_settings_probe(struct ofono_audio_settings *as,
+                                       unsigned int vendor, void *data)
+{
+       GIsiModem *modem = data;
+       struct audio_settings_data *asd;
+
+       asd = g_try_new0(struct audio_settings_data, 1);
+       if (asd == NULL)
+               return -ENOMEM;
+
+       asd->client = g_isi_client_create(modem, PN_CALL);
+       if (asd->client == NULL) {
+               g_free(asd);
+               return -ENOMEM;
+       }
+
+       ofono_audio_settings_set_data(as, asd);
+
+       g_isi_client_verify(asd->client, isi_call_verify_cb, as, NULL);
+
+       return 0;
+}
+
+static void isi_audio_settings_remove(struct ofono_audio_settings *as)
+{
+       struct audio_settings_data *asd = ofono_audio_settings_get_data(as);
+
+       ofono_audio_settings_set_data(as, NULL);
+
+       if (asd == NULL)
+               return;
+
+       g_isi_client_destroy(asd->client);
+       g_free(asd);
+}
+
+static struct ofono_audio_settings_driver driver = {
+       .name           = "isimodem",
+       .probe          = isi_audio_settings_probe,
+       .remove         = isi_audio_settings_remove,
+};
+
+void isi_audio_settings_init(void)
+{
+       ofono_audio_settings_driver_register(&driver);
+}
+
+void isi_audio_settings_exit(void)
+{
+       ofono_audio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/call-barring.c b/drivers/isimodem/call-barring.c
new file mode 100644 (file)
index 0000000..833c5ce
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/message.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include "util.h"
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "ss.h"
+#include "debug.h"
+
+struct barr_data {
+       GIsiClient *client;
+};
+
+static int lock_code_to_mmi(const char *lock)
+{
+       if (strcmp(lock, "AO") == 0)
+               return SS_GSM_BARR_ALL_OUT;
+       else if (strcmp(lock, "OI") == 0)
+               return SS_GSM_BARR_OUT_INTER;
+       else if (strcmp(lock, "OX") == 0)
+               return SS_GSM_BARR_OUT_INTER_EXC_HOME;
+       else if (strcmp(lock, "AI") == 0)
+               return SS_GSM_BARR_ALL_IN;
+       else if (strcmp(lock, "IR") == 0)
+               return SS_GSM_BARR_ALL_IN_ROAM;
+       else if (strcmp(lock, "AB") == 0)
+               return SS_GSM_ALL_BARRINGS;
+       else if (strcmp(lock, "AG") == 0)
+               return SS_GSM_OUTGOING_BARR_SERV;
+       else if (strcmp(lock, "AC") == 0)
+               return SS_GSM_INCOMING_BARR_SERV;
+       else
+               return 0;
+}
+
+static void update_status_mask(uint32_t *mask, uint8_t bsc)
+{
+       switch (bsc) {
+       case SS_GSM_TELEPHONY:
+               *mask |= 1;
+               break;
+
+       case SS_GSM_ALL_DATA_TELE:
+               *mask |= 1 << 1;
+               break;
+
+       case SS_GSM_FACSIMILE:
+               *mask |= 1 << 2;
+               break;
+
+       case SS_GSM_SMS:
+               *mask |= 1 << 3;
+               break;
+
+       case SS_GSM_ALL_DATA_CIRCUIT_SYNC:
+               *mask |= 1 << 4;
+               break;
+
+       case SS_GSM_ALL_DATA_CIRCUIT_ASYNC:
+               *mask |= 1 << 5;
+               break;
+
+       case SS_GSM_ALL_DATA_PACKET_SYNC:
+               *mask |= 1 << 6;
+               break;
+
+       case SS_GSM_ALL_PAD_ACCESS:
+               *mask |= 1 << 7;
+               break;
+
+       default:
+               DBG("Unknown BSC value %d, please report", bsc);
+               break;
+       }
+}
+
+static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t type)
+{
+       uint8_t service;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       ss_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &service) || service != type) {
+               DBG("Unexpected service type: 0x%02X", service);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_gsm_bsc_info(GIsiSubBlockIter *iter, uint32_t *mask)
+{
+       uint8_t *bsc;
+       uint8_t num, i;
+
+       if (!g_isi_sb_iter_get_byte(iter, &num, 2))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_struct(iter, (void **) &bsc, num, 3))
+               return FALSE;
+
+       for (i = 0; i < num; i++)
+               update_status_mask(mask, bsc[i]);
+
+       return TRUE;
+}
+
+static gboolean decode_gsm_barring_info(GIsiSubBlockIter *outer, uint32_t *mask)
+{
+       GIsiSubBlockIter iter;
+       uint8_t status;
+       uint8_t bsc;
+
+       for (g_isi_sb_subiter_init(outer, &iter, 4);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SS_GSM_BARRING_FEATURE)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &bsc, 2))
+                       return FALSE;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &status, 3))
+                       return FALSE;
+
+               if (status & SS_GSM_ACTIVE)
+                       update_status_mask(mask, bsc);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void unset_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_barring_set_cb_t cb = cbd->cb;
+
+       if (check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_DEACTIVATION))
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void set_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_barring_set_cb_t cb = cbd->cb;
+
+       if (check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_ACTIVATION))
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_set(struct ofono_call_barring *barr, const char *lock,
+                       int enable, const char *passwd, int cls,
+                       ofono_call_barring_set_cb_t cb, void *data)
+{
+       struct barr_data *bd = ofono_call_barring_get_data(barr);
+       struct isi_cb_data *cbd = isi_cb_data_new(barr, cb, data);
+       int ss_code = lock_code_to_mmi(lock);
+
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               enable ? SS_ACTIVATION : SS_DEACTIVATION,
+               SS_ALL_TELE_AND_BEARER,
+               ss_code >> 8, ss_code & 0xFF,   /* Service code */
+               SS_SEND_ADDITIONAL_INFO,
+               1,                      /* Subblock count */
+               SS_GSM_PASSWORD,
+               28,                     /* Subblock length */
+               0, passwd[0], 0, passwd[1],
+               0, passwd[2], 0, passwd[3],
+               0, 0, 0, 0, 0, 0, 0, 0, /* Filler */
+               0, 0, 0, 0, 0, 0, 0, 0, /* Filler */
+               0, 0,                   /* Filler */
+       };
+
+       DBG("lock code %s enable %d class %d password %s",
+               lock, enable, cls, passwd);
+
+       if (cbd == NULL || bd == NULL)
+               goto error;
+
+       if (g_isi_client_send(bd->client, msg, sizeof(msg),
+                               enable ? set_resp_cb : unset_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void query_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_barring_query_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+       uint32_t mask = 0;
+       uint8_t status;
+
+       if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_INTERROGATION))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 6);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case SS_STATUS_RESULT:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &status, 2))
+                               goto error;
+
+                       if (status & SS_GSM_ACTIVE)
+                               mask = 1;
+
+                       break;
+
+               case SS_GSM_BARRING_INFO:
+
+                       if (!decode_gsm_barring_info(&iter, &mask))
+                               goto error;
+
+                       break;
+
+               case SS_GSM_BSC_INFO:
+
+                       if (!decode_gsm_bsc_info(&iter, &mask))
+                               goto error;
+
+                       break;
+
+               case SS_GSM_ADDITIONAL_INFO:
+                       break;
+
+               }
+       }
+
+       DBG("mask=0x%04X", mask);
+       CALLBACK_WITH_SUCCESS(cb, mask, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, cbd->data);
+}
+
+static void isi_query(struct ofono_call_barring *barr, const char *lock,
+                       int cls, ofono_call_barring_query_cb_t cb, void *data)
+{
+       struct barr_data *bd = ofono_call_barring_get_data(barr);
+       struct isi_cb_data *cbd = isi_cb_data_new(barr, cb, data);
+       int ss_code = lock_code_to_mmi(lock);
+
+       unsigned char msg[] = {
+               SS_SERVICE_REQ,
+               SS_INTERROGATION,
+               SS_ALL_TELE_AND_BEARER,
+               ss_code >> 8, ss_code & 0xFF,   /* services code */
+               SS_SEND_ADDITIONAL_INFO,        /* Get BER-encoded result */
+               0                               /* Subblock count */
+       };
+
+       DBG("barring query lock code %s", lock);
+
+       if (cbd == NULL || bd == NULL)
+               goto error;
+
+       if (g_isi_client_send(bd->client, msg, sizeof(msg), query_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+       g_free(cbd);
+}
+
+static void set_passwd_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_barring_set_cb_t cb = cbd->cb;
+
+       if (check_resp(msg, SS_SERVICE_COMPLETED_RESP,
+                       SS_GSM_PASSWORD_REGISTRATION))
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_set_passwd(struct ofono_call_barring *barr, const char *lock,
+                               const char *old_passwd, const char *new_passwd,
+                               ofono_call_barring_set_cb_t cb, void *data)
+{
+       struct barr_data *bd = ofono_call_barring_get_data(barr);
+       struct isi_cb_data *cbd = isi_cb_data_new(barr, cb, data);
+       int ss_code = lock_code_to_mmi(lock);
+
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               SS_GSM_PASSWORD_REGISTRATION,
+               SS_ALL_TELE_AND_BEARER,
+               ss_code >> 8, ss_code & 0xFF,   /* Service code */
+               SS_SEND_ADDITIONAL_INFO,
+               1,                              /* Subblock count */
+               SS_GSM_PASSWORD,
+               28,                             /* Subblock length */
+               0, old_passwd[0], 0, old_passwd[1],
+               0, old_passwd[2], 0, old_passwd[3],
+               0, new_passwd[0], 0, new_passwd[1],
+               0, new_passwd[2], 0, new_passwd[3],
+               0, new_passwd[0], 0, new_passwd[1],
+               0, new_passwd[2], 0, new_passwd[3],
+               0, 0,                           /* Filler */
+       };
+
+       DBG("lock code %s (%u) old password %s new password %s",
+               lock, ss_code, old_passwd, new_passwd);
+
+       if (cbd == NULL || bd == NULL)
+               goto error;
+
+       if (g_isi_client_send(bd->client, msg, sizeof(msg), set_passwd_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_call_barring *barr = data;
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_call_barring_remove(barr);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       ofono_call_barring_register(barr);
+}
+
+static int isi_call_barring_probe(struct ofono_call_barring *barr,
+                                       unsigned int vendor, void *user)
+{
+       GIsiModem *modem = user;
+       struct barr_data *bd;
+
+       bd = g_try_new0(struct barr_data, 1);
+       if (bd == NULL)
+               return -ENOMEM;
+
+       bd->client = g_isi_client_create(modem, PN_SS);
+       if (bd->client == NULL) {
+               g_free(bd);
+               return -ENOMEM;
+       }
+
+       ofono_call_barring_set_data(barr, bd);
+
+       g_isi_client_verify(bd->client, reachable_cb, barr, NULL);
+
+       return 0;
+}
+
+static void isi_call_barring_remove(struct ofono_call_barring *barr)
+{
+       struct barr_data *data = ofono_call_barring_get_data(barr);
+
+       ofono_call_barring_set_data(barr, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_call_barring_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_call_barring_probe,
+       .remove                 = isi_call_barring_remove,
+       .set                    = isi_set,
+       .query                  = isi_query,
+       .set_passwd             = isi_set_passwd
+};
+
+void isi_call_barring_init(void)
+{
+       ofono_call_barring_driver_register(&driver);
+}
+
+void isi_call_barring_exit(void)
+{
+       ofono_call_barring_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/call-forwarding.c b/drivers/isimodem/call-forwarding.c
new file mode 100644 (file)
index 0000000..6366c3f
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/message.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-forwarding.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "ss.h"
+#include "debug.h"
+
+struct forw_data {
+       GIsiClient *client;
+};
+
+struct forw_info {
+       uint8_t bsc;            /* Basic service code */
+       uint8_t status;         /* SS status */
+       uint8_t ton;            /* Type of number */
+       uint8_t noreply;        /* No reply timeout */
+       uint8_t forw_opt;       /* Forwarding option */
+       uint8_t numlen;         /* Number length */
+       uint8_t sublen;         /* Sub-address length */
+       uint8_t filler;
+};
+
+static int forw_type_to_isi_code(int type)
+{
+       int ss_code;
+
+       switch (type) {
+       case 0:
+               ss_code = SS_GSM_FORW_UNCONDITIONAL;
+               break;
+       case 1:
+               ss_code = SS_GSM_FORW_BUSY;
+               break;
+       case 2:
+               ss_code = SS_GSM_FORW_NO_REPLY;
+               break;
+       case 3:
+               ss_code = SS_GSM_FORW_NO_REACH;
+               break;
+       case 4:
+               ss_code = SS_GSM_ALL_FORWARDINGS;
+               break;
+       case 5:
+               ss_code = SS_GSM_ALL_COND_FORWARDINGS;
+               break;
+       default:
+               DBG("Unknown forwarding type %d", type);
+               ss_code = -1;
+               break;
+       }
+       return ss_code;
+}
+
+static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t type)
+{
+       uint8_t service;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       ss_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &service) || service != type) {
+               DBG("Unexpected service type: 0x%02X", service);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_gsm_forwarding_info(GIsiSubBlockIter *parent,
+                                               uint8_t *status, uint8_t *ton,
+                                               uint8_t *noreply, char **number)
+{
+       GIsiSubBlockIter iter;
+       struct forw_info *info;
+       size_t len = sizeof(struct forw_info);
+       char *tag = NULL;
+
+       for (g_isi_sb_subiter_init(parent, &iter, 4);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_FEATURE)
+                       continue;
+
+               if (!g_isi_sb_iter_get_struct(&iter, (void *) &info, len, 2))
+                       return FALSE;
+
+               if (info->numlen != 0) {
+                       if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag,
+                                                       info->numlen * 2,
+                                                       2 + len))
+                               return FALSE;
+
+                       if (number)
+                               *number = tag;
+                       else
+                               g_free(tag);
+               } else {
+                       if (number)
+                               *number = g_strdup("");
+               }
+
+               if (status)
+                       *status = info->status;
+
+               if (ton)
+                       *ton = info->ton;
+
+               if (noreply)
+                       *noreply = info->noreply;
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void registration_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_forwarding_set_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+       uint8_t status;
+
+       if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_REGISTRATION))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 6);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO)
+                       continue;
+
+               if (!decode_gsm_forwarding_info(&iter, &status, NULL, NULL,
+                                               NULL))
+                       goto error;
+
+               if (status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED)) {
+                       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+                       return;
+               }
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_registration(struct ofono_call_forwarding *cf, int type,
+                               int cls,
+                               const struct ofono_phone_number *number,
+                               int time, ofono_call_forwarding_set_cb_t cb,
+                               void *data)
+{
+       struct forw_data *fd = ofono_call_forwarding_get_data(cf);
+       struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data);
+       int ss_code = forw_type_to_isi_code(type);
+
+       char *ucs2 = NULL;
+
+       size_t numlen = strlen(number->number);
+       size_t sb_len = ALIGN4(6 + 2 * numlen);
+       size_t pad_len = sb_len - (6 + 2 * numlen);
+
+       uint8_t msg[7 + 6 + 28 * 2 + 3] = {
+               SS_SERVICE_REQ,
+               SS_REGISTRATION,
+               SS_GSM_TELEPHONY,
+               ss_code >> 8, ss_code & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               1,      /* Subblock count */
+               SS_FORWARDING,
+               sb_len,
+               number->type,
+               ss_code == SS_GSM_FORW_NO_REPLY ? time : SS_UNDEFINED_TIME,
+               numlen,
+               0,      /* Sub address length */
+               /*
+                * Followed by number in UCS-2 (no NULL termination),
+                * zero sub address bytes, and 0 to 3 bytes of filler
+                */
+       };
+       size_t msg_len = 7 + 6 + numlen * 2 + pad_len;
+
+       if (cbd == NULL || fd == NULL || numlen > 28)
+               goto error;
+
+       DBG("forwarding type %d class %d number %s", type, cls, number->number);
+
+       if (ss_code < 0)
+               goto error;
+
+       ucs2 = g_convert(number->number, numlen, "UCS-2BE", "UTF-8//TRANSLIT",
+                               NULL, NULL, NULL);
+       if (ucs2 == NULL)
+               goto error;
+
+       memcpy(msg + 13, ucs2, numlen * 2);
+       g_free(ucs2);
+
+       if (g_isi_client_send(fd->client, msg, msg_len, registration_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void erasure_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_forwarding_set_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+       uint8_t status;
+
+       if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_ERASURE))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 6);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO)
+                       continue;
+
+               if (!decode_gsm_forwarding_info(&iter, &status, NULL, NULL,
+                                               NULL))
+                       goto error;
+
+               if (status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED))
+                       goto error;
+
+       }
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_erasure(struct ofono_call_forwarding *cf, int type, int cls,
+                               ofono_call_forwarding_set_cb_t cb, void *data)
+{
+       struct forw_data *fd = ofono_call_forwarding_get_data(cf);
+       struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data);
+       int ss_code = forw_type_to_isi_code(type);
+
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               SS_ERASURE,
+               SS_GSM_TELEPHONY,
+               ss_code >> 8, ss_code & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               0,              /* Subblock count */
+       };
+
+       DBG("forwarding type %d class %d", type, cls);
+
+       if (cbd == NULL || fd == NULL || ss_code < 0)
+               goto error;
+
+       if (g_isi_client_send(fd->client, msg, sizeof(msg),
+                               erasure_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void query_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_forwarding_query_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+
+       struct ofono_call_forwarding_condition list = {
+               .status = 0,
+               .cls = 7,
+               .time = 0,
+               .phone_number = {
+                       .number[0] = '\0',
+                       .type = 0,
+               },
+       };
+       uint8_t status;
+       uint8_t ton;
+       uint8_t noreply;
+       char *number = NULL;
+
+       if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_INTERROGATION))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 6);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               DBG("Got %s", ss_subblock_name(g_isi_sb_iter_get_id(&iter)));
+
+               if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO)
+                       continue;
+
+               if (!decode_gsm_forwarding_info(&iter, &status, &ton, &noreply,
+                                               &number))
+                       goto error;
+
+               list.status = status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED |
+                                       SS_GSM_PROVISIONED);
+               list.time = noreply;
+               list.phone_number.type = ton | 0x80;
+
+               DBG("Number <%s>", number);
+
+               strncpy(list.phone_number.number, number,
+                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+               list.phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+               g_free(number);
+
+               DBG("forwarding query: %d, %d, %s(%d) - %d sec",
+                       list.status, list.cls, list.phone_number.number,
+                       list.phone_number.type, list.time);
+       }
+       CALLBACK_WITH_SUCCESS(cb, 1, &list, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
+}
+
+
+static void isi_query(struct ofono_call_forwarding *cf, int type, int cls,
+                               ofono_call_forwarding_query_cb_t cb,
+                               void *data)
+{
+       struct forw_data *fd = ofono_call_forwarding_get_data(cf);
+       struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data);
+       int ss_code = forw_type_to_isi_code(type);
+
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               SS_INTERROGATION,
+               SS_GSM_TELEPHONY,
+               ss_code >> 8, ss_code & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               0, /* Subblock count */
+       };
+
+       DBG("forwarding type %d class %d", type, cls);
+
+       if (cbd == NULL || fd == NULL || cls != 7 || ss_code < 0)
+               goto error;
+
+       if (g_isi_client_send(fd->client, msg, sizeof(msg), query_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
+       g_free(cbd);
+}
+
+static void reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_call_forwarding_remove(cf);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       ofono_call_forwarding_register(cf);
+}
+
+
+static int isi_call_forwarding_probe(struct ofono_call_forwarding *cf,
+                                       unsigned int vendor, void *user)
+{
+       GIsiModem *modem = user;
+       struct forw_data *fd;
+
+       fd = g_try_new0(struct forw_data, 1);
+       if (fd == NULL)
+               return -ENOMEM;
+
+       fd->client = g_isi_client_create(modem, PN_SS);
+       if (fd->client == NULL) {
+               g_free(fd);
+               return -ENOMEM;
+       }
+
+       ofono_call_forwarding_set_data(cf, fd);
+
+       g_isi_client_verify(fd->client, reachable_cb, cf, NULL);
+
+       return 0;
+}
+
+static void isi_call_forwarding_remove(struct ofono_call_forwarding *cf)
+{
+       struct forw_data *data = ofono_call_forwarding_get_data(cf);
+
+       ofono_call_forwarding_set_data(cf, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_call_forwarding_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_call_forwarding_probe,
+       .remove                 = isi_call_forwarding_remove,
+       .activation             = NULL,
+       .registration           = isi_registration,
+       .deactivation           = NULL,
+       .erasure                = isi_erasure,
+       .query                  = isi_query
+};
+
+void isi_call_forwarding_init(void)
+{
+       ofono_call_forwarding_driver_register(&driver);
+}
+
+void isi_call_forwarding_exit(void)
+{
+       ofono_call_forwarding_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/call-meter.c b/drivers/isimodem/call-meter.c
new file mode 100644 (file)
index 0000000..3a558f6
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-meter.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "ss.h"
+
+struct call_meter_data {
+       GIsiClient *client;
+};
+
+static void isi_call_meter_query(struct ofono_call_meter *cm,
+                                       ofono_call_meter_query_cb_t cb,
+                                       void *data)
+{
+}
+
+static void isi_acm_query(struct ofono_call_meter *cm,
+                               ofono_call_meter_query_cb_t cb,
+                               void *data)
+{
+}
+
+static void isi_acm_reset(struct ofono_call_meter *cm, const char *sim_pin2,
+                               ofono_call_meter_set_cb_t cb, void *data)
+{
+}
+
+static void isi_acm_max_query(struct ofono_call_meter *cm,
+                               ofono_call_meter_query_cb_t cb, void *data)
+{
+}
+
+static void isi_acm_max_set(struct ofono_call_meter *cm, int new_value,
+                               const char *sim_pin2,
+                               ofono_call_meter_set_cb_t cb, void *data)
+{
+}
+
+static void isi_puct_query(struct ofono_call_meter *cm,
+                               ofono_call_meter_puct_query_cb_t cb, void *data)
+{
+}
+
+static void isi_puct_set(struct ofono_call_meter *cm, const char *currency,
+                               double ppu, const char *sim_pin2,
+                               ofono_call_meter_set_cb_t cb, void *data)
+{
+}
+
+static int isi_call_meter_probe(struct ofono_call_meter *cm,
+                               unsigned int vendor, void *user)
+{
+       GIsiModem *modem = user;
+       struct call_meter_data *cmd;
+
+       cmd = g_try_new0(struct call_meter_data, 1);
+       if (cmd == NULL)
+               return -ENOMEM;
+
+       cmd->client = g_isi_client_create(modem, PN_SS);
+       if (cmd->client == NULL) {
+               g_free(cmd);
+               return -ENOMEM;
+       }
+
+       ofono_call_meter_set_data(cm, cmd);
+
+       return 0;
+}
+
+static void isi_call_meter_remove(struct ofono_call_meter *cm)
+{
+       struct call_meter_data *data = ofono_call_meter_get_data(cm);
+
+       ofono_call_meter_set_data(cm, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_call_meter_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_call_meter_probe,
+       .remove                 = isi_call_meter_remove,
+       .call_meter_query       = isi_call_meter_query,
+       .acm_query              = isi_acm_query,
+       .acm_reset              = isi_acm_reset,
+       .acm_max_query          = isi_acm_max_query,
+       .acm_max_set            = isi_acm_max_set,
+       .puct_query             = isi_puct_query,
+       .puct_set               = isi_puct_set
+};
+
+void isi_call_meter_init(void)
+{
+       ofono_call_meter_driver_register(&driver);
+}
+
+void isi_call_meter_exit(void)
+{
+       ofono_call_meter_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/call-settings.c b/drivers/isimodem/call-settings.c
new file mode 100644 (file)
index 0000000..b4533bb
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-settings.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "ss.h"
+#include "debug.h"
+
+struct settings_data {
+       GIsiClient *client;
+};
+
+static void update_status_mask(uint32_t *mask, uint8_t bsc)
+{
+       switch (bsc) {
+       case SS_GSM_TELEPHONY:
+               *mask |= 1;
+               break;
+
+       case SS_GSM_ALL_DATA_TELE:
+               *mask |= 1 << 1;
+               break;
+
+       case SS_GSM_FACSIMILE:
+               *mask |= 1 << 2;
+               break;
+
+       case SS_GSM_SMS:
+               *mask |= 1 << 3;
+               break;
+
+       case SS_GSM_ALL_DATA_CIRCUIT_SYNC:
+               *mask |= 1 << 4;
+               break;
+
+       case SS_GSM_ALL_DATA_CIRCUIT_ASYNC:
+               *mask |= 1 << 5;
+               break;
+
+       case SS_GSM_ALL_DATA_PACKET_SYNC:
+               *mask |= 1 << 6;
+               break;
+
+       case SS_GSM_ALL_PAD_ACCESS:
+               *mask |= 1 << 7;
+               break;
+
+       default:
+               DBG("Unknown BSC value %d, please report", bsc);
+               break;
+       }
+}
+
+static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t type)
+{
+       uint8_t service;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       ss_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &service) || service != type) {
+               DBG("Unexpected service type: 0x%02X", service);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_gsm_bsc_info(GIsiSubBlockIter *iter, uint32_t *mask)
+{
+       uint8_t *bsc;
+       uint8_t num, i;
+
+       if (!g_isi_sb_iter_get_byte(iter, &num, 2))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_struct(iter, (void **) &bsc, num, 3))
+               return FALSE;
+
+       for (i = 0; i < num; i++)
+               update_status_mask(mask, bsc[i]);
+
+       return TRUE;
+}
+
+static void status_query_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_settings_status_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+
+       uint32_t mask = 0;
+       uint8_t status;
+
+       if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_INTERROGATION))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 6);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case SS_STATUS_RESULT:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &status, 2))
+                               goto error;
+
+                       if (status & SS_GSM_PROVISIONED)
+                               mask = 1;
+
+                       break;
+
+               case SS_GSM_ADDITIONAL_INFO:
+                       break;
+
+               case SS_GSM_BSC_INFO:
+
+                       if (!decode_gsm_bsc_info(&iter, &mask))
+                               goto error;
+
+                       break;
+               }
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, mask, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, cbd->data);
+}
+
+static void isi_clip_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       struct settings_data *sd = ofono_call_settings_get_data(cs);
+       struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data);
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               SS_INTERROGATION,
+               SS_ALL_TELE_AND_BEARER,
+               SS_GSM_CLIP >> 8, SS_GSM_CLIP & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               0,
+       };
+
+       if (sd == NULL || cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+       g_free(cbd);
+}
+
+static void isi_colp_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       struct settings_data *sd = ofono_call_settings_get_data(cs);
+       struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data);
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               SS_INTERROGATION,
+               SS_ALL_TELE_AND_BEARER,
+               SS_GSM_COLP >> 8, SS_GSM_COLP & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               0,
+       };
+
+       if (sd == NULL || cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+       g_free(cbd);
+}
+
+static void isi_colr_query(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data)
+{
+       struct settings_data *sd = ofono_call_settings_get_data(cs);
+       struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data);
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               SS_INTERROGATION,
+               SS_ALL_TELE_AND_BEARER,
+               SS_GSM_COLR >> 8, SS_GSM_COLR & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               0,
+       };
+
+       if (sd == NULL || cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+       g_free(cbd);
+}
+
+static void isi_cw_query(struct ofono_call_settings *cs, int cls,
+                       ofono_call_settings_status_cb_t cb, void *data)
+{
+       struct settings_data *sd = ofono_call_settings_get_data(cs);
+       struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data);
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               SS_INTERROGATION,
+               SS_ALL_TELE_AND_BEARER,
+               SS_GSM_CALL_WAITING >> 8, SS_GSM_CALL_WAITING & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               0,
+       };
+
+       if (sd == NULL || cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+       g_free(cbd);
+}
+
+static gboolean check_cw_resp(const GIsiMessage *msg, uint8_t type)
+{
+       GIsiSubBlockIter iter;
+       uint8_t status;
+
+       if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, type))
+               return FALSE;
+
+       for (g_isi_sb_iter_init(&iter, msg, 6);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SS_GSM_DATA)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &status, 2))
+                       return FALSE;
+
+               if (status & SS_GSM_ACTIVE)
+                       return type == SS_ACTIVATION;
+               else
+                       return type == SS_DEACTIVATION;
+       }
+
+       return FALSE;
+}
+
+static void cw_set_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_settings_set_cb_t cb = cbd->cb;
+
+       if (check_cw_resp(msg, SS_ACTIVATION))
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void cw_unset_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_call_settings_set_cb_t cb = cbd->cb;
+
+       if (check_cw_resp(msg, SS_DEACTIVATION))
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_cw_set(struct ofono_call_settings *cs, int mode, int cls,
+                       ofono_call_settings_set_cb_t cb, void *data)
+{
+       struct settings_data *sd = ofono_call_settings_get_data(cs);
+       struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data);
+       const uint8_t msg[] = {
+               SS_SERVICE_REQ,
+               mode ? SS_ACTIVATION : SS_DEACTIVATION,
+               SS_ALL_TELE_AND_BEARER,
+               SS_GSM_CALL_WAITING >> 8, SS_GSM_CALL_WAITING & 0xFF,
+               SS_SEND_ADDITIONAL_INFO,
+               0, /* Subblock count */
+       };
+
+       if (cbd == NULL || sd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, sizeof(msg),
+                               mode ? cw_set_resp_cb : cw_unset_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_call_settings_remove(cs);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       ofono_call_settings_register(cs);
+}
+
+static int isi_call_settings_probe(struct ofono_call_settings *cs,
+                                       unsigned int vendor, void *user)
+{
+       GIsiModem *modem = user;
+       struct settings_data *sd;
+
+       sd = g_try_new0(struct settings_data, 1);
+       if (sd == NULL)
+               return -ENOMEM;
+
+       sd->client = g_isi_client_create(modem, PN_SS);
+
+       if (sd->client == NULL) {
+               g_free(sd);
+               return -ENOMEM;
+       }
+
+       ofono_call_settings_set_data(cs, sd);
+
+       g_isi_client_verify(sd->client, reachable_cb, cs, NULL);
+
+       return 0;
+}
+
+static void isi_call_settings_remove(struct ofono_call_settings *cs)
+{
+       struct settings_data *data = ofono_call_settings_get_data(cs);
+
+       ofono_call_settings_set_data(cs, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_call_settings_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_call_settings_probe,
+       .remove                 = isi_call_settings_remove,
+       .clip_query             = isi_clip_query,
+       .colp_query             = isi_colp_query,
+       .colr_query             = isi_colr_query,
+       .clir_query             = NULL,
+       .clir_set               = NULL,
+       .cw_query               = isi_cw_query,
+       .cw_set                 = isi_cw_set
+};
+
+void isi_call_settings_init(void)
+{
+       ofono_call_settings_driver_register(&driver);
+}
+
+void isi_call_settings_exit(void)
+{
+       ofono_call_settings_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/call.h b/drivers/isimodem/call.h
new file mode 100644 (file)
index 0000000..dcafae3
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_CALL_H
+#define __ISIMODEM_CALL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_CALL                                        0x01
+#define PN_MODEM_CALL                          0xC9
+#define CALL_MODEM_PROP_PRESENT_DEFAULT                0x00
+
+enum call_message_id {
+       CALL_CREATE_REQ =                       0x01,
+       CALL_CREATE_RESP =                      0x02,
+       CALL_COMING_IND =                       0x03,
+       CALL_MO_ALERT_IND =                     0x04,
+       CALL_MT_ALERT_IND =                     0x05,
+       CALL_WAITING_IND =                      0x06,
+       CALL_ANSWER_REQ =                       0x07,
+       CALL_ANSWER_RESP =                      0x08,
+       CALL_RELEASE_REQ =                      0x09,
+       CALL_RELEASE_RESP =                     0x0A,
+       CALL_RELEASE_IND =                      0x0B,
+       CALL_TERMINATED_IND =                   0x0C,
+       CALL_STATUS_REQ =                       0x0D,
+       CALL_STATUS_RESP =                      0x0E,
+       CALL_STATUS_IND =                       0x0F,
+       CALL_SERVER_STATUS_IND =                0x10,
+       CALL_CONTROL_REQ =                      0x11,
+       CALL_CONTROL_RESP =                     0x12,
+       CALL_CONTROL_IND =                      0x13,
+       CALL_MODE_SWITCH_REQ =                  0x14,
+       CALL_MODE_SWITCH_RESP =                 0x15,
+       CALL_MODE_SWITCH_IND =                  0x16,
+       CALL_DTMF_SEND_REQ =                    0x17,
+       CALL_DTMF_SEND_RESP =                   0x18,
+       CALL_DTMF_STOP_REQ =                    0x19,
+       CALL_DTMF_STOP_RESP =                   0x1A,
+       CALL_DTMF_STATUS_IND =                  0x1B,
+       CALL_DTMF_TONE_IND =                    0x1C,
+       CALL_RECONNECT_IND =                    0x1E,
+       CALL_PROPERTY_GET_REQ =                 0x1F,
+       CALL_PROPERTY_GET_RESP =                0x20,
+       CALL_PROPERTY_SET_REQ =                 0x21,
+       CALL_PROPERTY_SET_RESP =                0x22,
+       CALL_PROPERTY_SET_IND =                 0x23,
+       CALL_EMERGENCY_NBR_CHECK_REQ =          0x28,
+       CALL_EMERGENCY_NBR_CHECK_RESP =         0x29,
+       CALL_EMERGENCY_NBR_GET_REQ =            0x26,
+       CALL_EMERGENCY_NBR_GET_RESP =           0x27,
+       CALL_EMERGENCY_NBR_MODIFY_REQ =         0x24,
+       CALL_EMERGENCY_NBR_MODIFY_RESP =        0x25,
+       CALL_GSM_NOTIFICATION_IND =             0xA0,
+       CALL_GSM_USER_TO_USER_REQ =             0xA1,
+       CALL_GSM_USER_TO_USER_RESP =            0xA2,
+       CALL_GSM_USER_TO_USER_IND =             0xA3,
+       CALL_GSM_BLACKLIST_CLEAR_REQ =          0xA4,
+       CALL_GSM_BLACKLIST_CLEAR_RESP =         0xA5,
+       CALL_GSM_BLACKLIST_TIMER_IND =          0xA6,
+       CALL_GSM_DATA_CH_INFO_IND =             0xA7,
+       CALL_GSM_CCP_GET_REQ =                  0xAA,
+       CALL_GSM_CCP_GET_RESP =                 0xAB,
+       CALL_GSM_CCP_CHECK_REQ =                0xAC,
+       CALL_GSM_CCP_CHECK_RESP =               0xAD,
+       CALL_GSM_COMING_REJ_IND =               0xA9,
+       CALL_GSM_RAB_IND =                      0xA8,
+       CALL_GSM_IMMEDIATE_MODIFY_IND =         0xAE,
+       CALL_CREATE_NO_SIMATK_REQ =             0x2A,
+       CALL_GSM_SS_DATA_IND =                  0xAF,
+       CALL_TIMER_REQ =                        0x2B,
+       CALL_TIMER_RESP =                       0x2C,
+       CALL_TIMER_NTF =                        0x2D,
+       CALL_TIMER_IND =                        0x2E,
+       CALL_TIMER_RESET_REQ =                  0x2F,
+       CALL_TIMER_RESET_RESP =                 0x30,
+       CALL_EMERGENCY_NBR_IND =                0x31,
+       CALL_SERVICE_DENIED_IND =               0x32,
+       CALL_RELEASE_END_REQ =                  0x34,
+       CALL_RELEASE_END_RESP =                 0x35,
+       CALL_USER_CONNECT_IND =                 0x33,
+       CALL_AUDIO_CONNECT_IND =                0x40,
+       CALL_KODIAK_ALLOW_CTRL_REQ =            0x36,
+       CALL_KODIAK_ALLOW_CTRL_RESP =           0x37,
+       CALL_SERVICE_ACTIVATE_IND =             0x38,
+       CALL_SERVICE_ACTIVATE_REQ =             0x39,
+       CALL_SERVICE_ACTIVATE_RESP =            0x3A,
+       CALL_SIM_ATK_IND =                      0x3B,
+       CALL_CONTROL_OPER_IND =                 0x3C,
+       CALL_TEST_CALL_STATUS_IND =             0x3E,
+       CALL_SIM_ATK_INFO_IND =                 0x3F,
+       CALL_SECURITY_IND =                     0x41,
+       CALL_MEDIA_HANDLE_REQ =                 0x42,
+       CALL_MEDIA_HANDLE_RESP =                0x43,
+};
+
+enum call_status {
+       CALL_STATUS_IDLE =                      0x00,
+       CALL_STATUS_CREATE =                    0x01,
+       CALL_STATUS_COMING =                    0x02,
+       CALL_STATUS_PROCEEDING =                0x03,
+       CALL_STATUS_MO_ALERTING =               0x04,
+       CALL_STATUS_MT_ALERTING =               0x05,
+       CALL_STATUS_WAITING =                   0x06,
+       CALL_STATUS_ANSWERED =                  0x07,
+       CALL_STATUS_ACTIVE =                    0x08,
+       CALL_STATUS_MO_RELEASE =                0x09,
+       CALL_STATUS_MT_RELEASE =                0x0A,
+       CALL_STATUS_HOLD_INITIATED =            0x0B,
+       CALL_STATUS_HOLD =                      0x0C,
+       CALL_STATUS_RETRIEVE_INITIATED =        0x0D,
+       CALL_STATUS_RECONNECT_PENDING =         0x0E,
+       CALL_STATUS_TERMINATED =                0x0F,
+       CALL_STATUS_SWAP_INITIATED =            0x10,
+};
+
+enum call_isi_cause {
+       CALL_CAUSE_NO_CAUSE =                   0x00,
+       CALL_CAUSE_NO_CALL =                    0x01,
+       CALL_CAUSE_TIMEOUT =                    0x02,
+       CALL_CAUSE_RELEASE_BY_USER =            0x03,
+       CALL_CAUSE_BUSY_USER_REQUEST =          0x04,
+       CALL_CAUSE_ERROR_REQUEST =              0x05,
+       CALL_CAUSE_COST_LIMIT_REACHED =         0x06,
+       CALL_CAUSE_CALL_ACTIVE =                0x07,
+       CALL_CAUSE_NO_CALL_ACTIVE =             0x08,
+       CALL_CAUSE_INVALID_CALL_MODE =          0x09,
+       CALL_CAUSE_SIGNALLING_FAILURE =         0x0A,
+       CALL_CAUSE_TOO_LONG_ADDRESS =           0x0B,
+       CALL_CAUSE_INVALID_ADDRESS =            0x0C,
+       CALL_CAUSE_EMERGENCY =                  0x0D,
+       CALL_CAUSE_NO_TRAFFIC_CHANNEL =         0x0E,
+       CALL_CAUSE_NO_COVERAGE =                0x0F,
+       CALL_CAUSE_CODE_REQUIRED =              0x10,
+       CALL_CAUSE_NOT_ALLOWED =                0x11,
+       CALL_CAUSE_NO_DTMF =                    0x12,
+       CALL_CAUSE_CHANNEL_LOSS =               0x13,
+       CALL_CAUSE_FDN_NOT_OK =                 0x14,
+       CALL_CAUSE_USER_TERMINATED =            0x15,
+       CALL_CAUSE_BLACKLIST_BLOCKED =          0x16,
+       CALL_CAUSE_BLACKLIST_DELAYED =          0x17,
+       CALL_CAUSE_NUMBER_NOT_FOUND =           0x18,
+       CALL_CAUSE_NUMBER_CANNOT_REMOVE =       0x19,
+       CALL_CAUSE_EMERGENCY_FAILURE =          0x1A,
+       CALL_CAUSE_CS_SUSPENDED =               0x1B,
+       CALL_CAUSE_DCM_DRIVE_MODE =             0x1C,
+       CALL_CAUSE_MULTIMEDIA_NOT_ALLOWED =     0x1D,
+       CALL_CAUSE_SIM_REJECTED =               0x1E,
+       CALL_CAUSE_NO_SIM =                     0x1F,
+       CALL_CAUSE_SIM_LOCK_OPERATIVE =         0x20,
+       CALL_CAUSE_SIMATKCC_REJECTED =          0x21,
+       CALL_CAUSE_SIMATKCC_MODIFIED =          0x22,
+       CALL_CAUSE_DTMF_INVALID_DIGIT =         0x23,
+       CALL_CAUSE_DTMF_SEND_ONGOING =          0x24,
+       CALL_CAUSE_CS_INACTIVE =                0x25,
+       CALL_CAUSE_SECURITY_MODE =              0x26,
+       CALL_CAUSE_TRACFONE_FAILED =            0x27,
+       CALL_CAUSE_TRACFONE_WAIT_FAILED =       0x28,
+       CALL_CAUSE_TRACFONE_CONF_FAILED =       0x29,
+       CALL_CAUSE_TEMPERATURE_LIMIT =          0x2A,
+       CALL_CAUSE_KODIAK_POC_FAILED =          0x2B,
+       CALL_CAUSE_NOT_REGISTERED =             0x2C,
+       CALL_CAUSE_CS_CALLS_ONLY =              0x2D,
+       CALL_CAUSE_VOIP_CALLS_ONLY =            0x2E,
+       CALL_CAUSE_LIMITED_CALL_ACTIVE =        0x2F,
+       CALL_CAUSE_LIMITED_CALL_NOT_ALLOWED =   0x30,
+       CALL_CAUSE_SECURE_CALL_NOT_POSSIBLE =   0x31,
+       CALL_CAUSE_INTERCEPT =                  0x32,
+};
+
+enum call_gsm_cause {
+       CALL_GSM_CAUSE_UNASSIGNED_NUMBER =      0x01,
+       CALL_GSM_CAUSE_NO_ROUTE =               0x03,
+       CALL_GSM_CAUSE_CH_UNACCEPTABLE =        0x06,
+       CALL_GSM_CAUSE_OPER_BARRING =           0x08,
+       CALL_GSM_CAUSE_NORMAL =                 0x10,
+       CALL_GSM_CAUSE_USER_BUSY =              0x11,
+       CALL_GSM_CAUSE_NO_USER_RESPONSE =       0x12,
+       CALL_GSM_CAUSE_ALERT_NO_ANSWER =        0x13,
+       CALL_GSM_CAUSE_CALL_REJECTED =          0x15,
+       CALL_GSM_CAUSE_NUMBER_CHANGED =         0x16,
+       CALL_GSM_CAUSE_NON_SELECT_CLEAR =       0x1A,
+       CALL_GSM_CAUSE_DEST_OUT_OF_ORDER =      0x1B,
+       CALL_GSM_CAUSE_INVALID_NUMBER =         0x1C,
+       CALL_GSM_CAUSE_FACILITY_REJECTED =      0x1D,
+       CALL_GSM_CAUSE_RESP_TO_STATUS =         0x1E,
+       CALL_GSM_CAUSE_NORMAL_UNSPECIFIED =     0x1F,
+       CALL_GSM_CAUSE_NO_CHANNEL =             0x22,
+       CALL_GSM_CAUSE_NETW_OUT_OF_ORDER =      0x26,
+       CALL_GSM_CAUSE_TEMPORARY_FAILURE =      0x29,
+       CALL_GSM_CAUSE_CONGESTION =             0x2A,
+       CALL_GSM_CAUSE_ACCESS_INFO_DISC =       0x2B,
+       CALL_GSM_CAUSE_CHANNEL_NA =             0x2C,
+       CALL_GSM_CAUSE_RESOURCES_NA =           0x2F,
+       CALL_GSM_CAUSE_QOS_NA =                 0x31,
+       CALL_GSM_CAUSE_FACILITY_UNSUBS =        0x32,
+       CALL_GSM_CAUSE_COMING_BARRED_CUG =      0x37,
+       CALL_GSM_CAUSE_BC_UNAUTHORIZED =        0x39,
+       CALL_GSM_CAUSE_BC_NA =                  0x3A,
+       CALL_GSM_CAUSE_SERVICE_NA =             0x3F,
+       CALL_GSM_CAUSE_BEARER_NOT_IMPL =        0x41,
+       CALL_GSM_CAUSE_ACM_MAX =                0x44,
+       CALL_GSM_CAUSE_FACILITY_NOT_IMPL =      0x45,
+       CALL_GSM_CAUSE_ONLY_RDI_BC =            0x46,
+       CALL_GSM_CAUSE_SERVICE_NOT_IMPL =       0x4F,
+       CALL_GSM_CAUSE_INVALID_TI =             0x51,
+       CALL_GSM_CAUSE_NOT_IN_CUG =             0x57,
+       CALL_GSM_CAUSE_INCOMPATIBLE_DEST =      0x58,
+       CALL_GSM_CAUSE_INV_TRANS_NET_SEL =      0x5B,
+       CALL_GSM_CAUSE_SEMANTICAL_ERR =         0x5F,
+       CALL_GSM_CAUSE_INVALID_MANDATORY =      0x60,
+       CALL_GSM_CAUSE_MSG_TYPE_INEXIST =       0x61,
+       CALL_GSM_CAUSE_MSG_TYPE_INCOMPAT =      0x62,
+       CALL_GSM_CAUSE_IE_NON_EXISTENT =        0x63,
+       CALL_GSM_CAUSE_COND_IE_ERROR =          0x64,
+       CALL_GSM_CAUSE_MSG_INCOMPATIBLE =       0x65,
+       CALL_GSM_CAUSE_TIMER_EXPIRY =           0x66,
+       CALL_GSM_CAUSE_PROTOCOL_ERROR =         0x6F,
+       CALL_GSM_CAUSE_INTERWORKING =           0x7F,
+};
+
+enum call_cause_type {
+       CALL_CAUSE_TYPE_DEFAULT =               0x00,
+       CALL_CAUSE_TYPE_CLIENT =                0x01,
+       CALL_CAUSE_TYPE_SERVER =                0x02,
+       CALL_CAUSE_TYPE_NETWORK =               0x03,
+};
+
+enum call_subblock {
+       CALL_ORIGIN_ADDRESS =                   0x01,
+       CALL_ORIGIN_SUBADDRESS =                0x02,
+       CALL_DESTINATION_ADDRESS =              0x03,
+       CALL_DESTINATION_SUBADDRESS =           0x04,
+       CALL_DESTINATION_PRE_ADDRESS =          0x05,
+       CALL_DESTINATION_POST_ADDRESS =         0x06,
+       CALL_MODE =                             0x07,
+       CALL_CAUSE =                            0x08,
+       CALL_OPERATION =                        0x09,
+       CALL_STATUS =                           0x0A,
+       CALL_STATUS_INFO =                      0x0B,
+       CALL_ALERTING_INFO =                    0x0C,
+       CALL_RELEASE_INFO =                     0x0D,
+       CALL_ORIGIN_INFO =                      0x0E,
+       CALL_DTMF_DIGIT =                       0x0F,
+       CALL_DTMF_STRING =                      0x10,
+       CALL_DTMF_BCD_STRING =                  0x19,
+       CALL_DTMF_INFO =                        0x1A,
+       CALL_PROPERTY_INFO =                    0x13,
+       CALL_EMERGENCY_NUMBER =                 0x14,
+       CALL_DTMF_STATUS =                      0x11,
+       CALL_DTMF_TONE =                        0x12,
+       CALL_GSM_CUG_INFO =                     0xA0,
+       CALL_GSM_ALERTING_PATTERN =             0xA1,
+       CALL_GSM_DEFLECTION_ADDRESS =           0xA2,
+       CALL_GSM_DEFLECTION_SUBADDRESS =        0xA3,
+       CALL_GSM_REDIRECTING_ADDRESS =          0xA4,
+       CALL_GSM_REDIRECTING_SUBADDRESS =       0xA5,
+       CALL_GSM_REMOTE_ADDRESS =               0xA6,
+       CALL_GSM_REMOTE_SUBADDRESS =            0xA7,
+       CALL_GSM_USER_TO_USER_INFO =            0xA8,
+       CALL_GSM_DIAGNOSTICS =                  0xA9,
+       CALL_GSM_SS_DIAGNOSTICS =               0xAA,
+       CALL_GSM_NEW_DESTINATION =              0xAB,
+       CALL_GSM_CCBS_INFO =                    0xAC,
+       CALL_GSM_ADDRESS_OF_B =                 0xAD,
+       CALL_GSM_SUBADDRESS_OF_B =              0xB0,
+       CALL_GSM_NOTIFY =                       0xB1,
+       CALL_GSM_SS_NOTIFY =                    0xB2,
+       CALL_GSM_SS_CODE =                      0xB3,
+       CALL_GSM_SS_STATUS =                    0xB4,
+       CALL_GSM_SS_NOTIFY_INDICATOR =          0xB5,
+       CALL_GSM_SS_HOLD_INDICATOR =            0xB6,
+       CALL_GSM_SS_ECT_INDICATOR =             0xB7,
+       CALL_GSM_DATA_CH_INFO =                 0xB8,
+       CALL_DESTINATION_CS_ADDRESS =           0x16,
+       CALL_GSM_CCP =                          0xBA,
+       CALL_GSM_RAB_INFO =                     0xB9,
+       CALL_GSM_FNUR_INFO =                    0xBB,
+       CALL_GSM_CAUSE_OF_NO_CLI =              0xBC,
+       CALL_GSM_MM_CAUSE =                     0xBD,
+       CALL_GSM_EVENT_INFO =                   0xBE,
+       CALL_GSM_DETAILED_CAUSE =               0xBF,
+       CALL_GSM_SS_DATA =                      0xC0,
+       CALL_TIMER =                            0x17,
+       CALL_GSM_ALS_INFO =                     0xC1,
+       CALL_STATE_AUTO_CHANGE =                0x18,
+       CALL_EMERGENCY_NUMBER_INFO =            0x1B,
+       CALL_STATUS_MODE =                      0x1C,
+       CALL_ADDR_AND_STATUS_INFO =             0x1D,
+       CALL_DTMF_TIMERS =                      0x1E,
+       CALL_NAS_SYNC_INDICATOR =               0x1F,
+       CALL_NW_CAUSE =                         0x20,
+       CALL_TRACFONE_RESULT =                  0x21,
+       CALL_KODIAK_POC =                       0x22,
+       CALL_DISPLAY_NUMBER =                   0x23,
+       CALL_DESTINATION_URI =                  0x24,
+       CALL_ORIGIN_URI =                       0x25,
+       CALL_URI =                              0x26,
+       CALL_SYSTEM_INFO =                      0x27,
+       CALL_SYSTEMS =                          0x28,
+       CALL_VOIP_TIMER =                       0x29,
+       CALL_REDIRECTING_URI =                  0x2A,
+       CALL_REMOTE_URI =                       0x2B,
+       CALL_DEFLECTION_URI =                   0x2C,
+       CALL_TRANSFER_INFO =                    0x2D,
+       CALL_FORWARDING_INFO =                  0x2E,
+       CALL_ID_INFO =                          0x2F,
+       CALL_TEST_CALL =                        0x30,
+       CALL_AUDIO_CONF_INFO =                  0x31,
+       CALL_SECURITY_INFO =                    0x33,
+       CALL_SINGLE_TIMERS =                    0x32,
+       CALL_MEDIA_INFO =                       0x35,
+       CALL_MEDIA_HANDLE =                     0x34,
+       CALL_MODE_CHANGE_INFO =                 0x36,
+       CALL_ADDITIONAL_PARAMS =                0x37,
+       CALL_DSAC_INFO =                        0x38,
+       CALL_LINE_ID =                          0x47,
+};
+
+enum call_id {
+       CALL_ID_NONE =                          0x00,
+       CALL_ID_1 =                             0x01,
+       CALL_ID_2 =                             0x02,
+       CALL_ID_3 =                             0x03,
+       CALL_ID_4 =                             0x04,
+       CALL_ID_5 =                             0x05,
+       CALL_ID_6 =                             0x06,
+       CALL_ID_7 =                             0x07,
+       CALL_ID_CONFERENCE =                    0x10,
+       CALL_ID_WAITING =                       0x20,
+       CALL_ID_HOLD =                          0x40,
+       CALL_ID_ACTIVE =                        0x80,
+       CALL_ID_ALL =                           0xF0,
+};
+
+enum call_dtmf_pause_values {
+       CALL_DTMF_PAUSE_1S = 0x01
+};
+
+enum call_mode {
+       CALL_MODE_EMERGENCY =                   0x00,
+       CALL_MODE_SPEECH =                      0x01,
+       CALL_GSM_MODE_ALS_LINE_1 =              0xA5,
+       CALL_GSM_MODE_ALS_LINE_2 =              0xA2,
+};
+
+enum {
+       CALL_MODE_INFO_NONE =                   0x00,
+       CALL_MODE_ORIGINATOR =                  0x01,
+};
+
+enum {
+       CALL_PRESENTATION_ALLOWED =             0x00,
+       CALL_PRESENTATION_RESTRICTED =          0x01,
+       CALL_GSM_PRESENTATION_DEFAULT =         0x07,
+};
+
+enum call_modem_line_id {
+       CALL_MODEM_PRESENT_DEFAULT =            0x00,
+       CALL_MODEM_PRESENT_ALLOWED =            0x01,
+       CALL_MODEM_PRESENT_RESTRICTED =         0x02
+};
+
+enum call_operation {
+       CALL_OP_HOLD =                          0x01,
+       CALL_OP_RETRIEVE =                      0x02,
+       CALL_OP_SWAP =                          0x03,
+       CALL_OP_CONFERENCE_BUILD =              0x04,
+       CALL_OP_CONFERENCE_SPLIT =              0x05,
+       CALL_OP_DATA_RATE_CHANGE =              0x06,
+       CALL_GSM_OP_CUG =                       0xA0,
+       CALL_GSM_OP_TRANSFER =                  0xA1,
+       CALL_GSM_OP_DEFLECT =                   0xA2,
+       CALL_GSM_OP_CCBS =                      0xA3,
+       CALL_GSM_OP_UUS1 =                      0xA4,
+       CALL_GSM_OP_UUS2 =                      0xA5,
+       CALL_GSM_OP_UUS3 =                      0xA6,
+};
+
+enum {
+       CALL_GSM_OP_UUS_REQUIRED =              0x01,
+};
+
+enum call_status_mode {
+       CALL_STATUS_MODE_DEFAULT =              0x00,
+       CALL_STATUS_MODE_ADDR =                 0x01,
+       CALL_STATUS_MODE_ADDR_AND_ORIGIN =      0x02,
+       CALL_STATUS_MODE_POC =                  0x03,
+       CALL_STATUS_MODE_VOIP_ADDR =            0x04,
+};
+
+enum {
+       CALL_DTMF_ENABLE_TONE_IND_SEND =        0x01,
+       CALL_DTMF_DISABLE_TONE_IND_SEND =       0x02,
+};
+
+enum call_notification_indicator {
+       CALL_NOTIFY_USER_SUSPENDED =            0x00,
+       CALL_NOTIFY_USER_RESUMED =              0x01,
+       CALL_NOTIFY_BEARER_CHANGE =             0x02
+};
+
+enum call_mmi_ss_codes {
+       CALL_SSC_ALL_FWDS =                     0x0002,
+       CALL_SSC_ALL_COND_FWD =                 0x0004,
+       CALL_SSC_CFU =                          0x0015,
+       CALL_SSC_CFB =                          0x0043,
+       CALL_SSC_CFNRY =                        0x003D,
+       CALL_SSC_CFGNC =                        0x003E,
+       CALL_SSC_OUTGOING_BARR_SERV =           0x014D,
+       CALL_SSC_INCOMING_BARR_SERV =           0x0161,
+       CALL_SSC_CALL_WAITING =                 0x002B,
+       CALL_SSC_CLIR =                         0x001F,
+       CALL_SSC_ETC =                          0x0060,
+       CALL_SSC_MPTY =                         0xFFFE,
+       CALL_SSC_CALL_HOLD =                    0xFFFF
+};
+
+enum call_ss_status {
+       CALL_SS_STATUS_ACTIVE =                 0x01,
+       CALL_SS_STATUS_REGISTERED =             0x02,
+       CALL_SS_STATUS_PROVISIONED =            0x04,
+       CALL_SS_STATUS_QUIESCENT =              0x08
+};
+
+enum call_ss_notification {
+       CALL_SSN_INCOMING_IS_FWD =              0x01,
+       CALL_SSN_INCOMING_FWD =                 0x02,
+       CALL_SSN_OUTGOING_FWD =                 0x04
+};
+
+enum call_ss_indicator {
+       CALL_SSI_CALL_IS_WAITING =              0x01,
+       CALL_SSI_MPTY =                         0x02,
+       CALL_SSI_CLIR_SUPPR_REJ =               0x04
+};
+
+enum call_ss_hold_indicator {
+       CALL_HOLD_IND_RETRIEVED =               0x00,
+       CALL_HOLD_IND_ON_HOLD =                 0x01
+};
+
+enum call_ss_ect_indicator {
+       CALL_ECT_CALL_STATE_ALERT =             0x00,
+       CALL_ECT_CALL_STATE_ACTIVE =            0x01
+};
+
+/* 27.007 Section 7.7 */
+enum clir_status {
+       CLIR_STATUS_NOT_PROVISIONED = 0,
+       CLIR_STATUS_PROVISIONED_PERMANENT,
+       CLIR_STATUS_UNKNOWN,
+       CLIR_STATUS_TEMPORARY_RESTRICTED,
+       CLIR_STATUS_TEMPORARY_ALLOWED
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !__ISIMODEM_CALL_H */
diff --git a/drivers/isimodem/cbs.c b/drivers/isimodem/cbs.c
new file mode 100644 (file)
index 0000000..d102cdd
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/message.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cbs.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "sms.h"
+#include "debug.h"
+
+struct cbs_data {
+       GIsiClient *client;
+};
+
+struct cbs_info {
+       uint8_t pdu[88];
+};
+
+static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid)
+{
+       uint8_t cause;
+       uint8_t reason;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       sms_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &cause))
+               return FALSE;
+
+       if (cause == SMS_OK)
+               return TRUE;
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &reason))
+               return FALSE;
+
+       if (reason == SMS_ERR_PP_RESERVED) {
+               DBG("Request failed: 0x%02"PRIx8" (%s).\n\n  Unable to "
+                       "bootstrap CBS routing.\n  It appears some other "
+                       "component is already\n  registered as the CBS "
+                       "routing endpoint.\n  As a consequence, "
+                       "receiving CBSs is not going to work.\n\n",
+                       reason, sms_isi_cause_name(reason));
+       }
+       return FALSE;
+}
+
+static void isi_set_topics(struct ofono_cbs *cbs, const char *topics,
+                               ofono_cbs_set_cb_t cb, void *data)
+{
+       DBG("Not implemented (topics=%s), all topics accepted", topics);
+       CALLBACK_WITH_SUCCESS(cb, data);
+}
+
+static void isi_clear_topics(struct ofono_cbs *cbs,
+                               ofono_cbs_set_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_SUCCESS(cb, data);
+}
+
+static void routing_ntf_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_cbs *cbs = data;
+       struct cbs_info *info;
+       size_t len = sizeof(struct cbs_info);
+       GIsiSubBlockIter iter;
+
+       if (!check_resp(msg, SMS_GSM_CB_ROUTING_NTF))
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SMS_GSM_CB_MESSAGE)
+                       continue;
+
+               if (!g_isi_sb_iter_get_struct(&iter, (void *) &info, len, 2))
+                       return;
+
+               ofono_cbs_notify(cbs, info->pdu, len);
+               return;
+       }
+}
+
+static void routing_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_cbs *cbs = data;
+       struct cbs_data *cd = ofono_cbs_get_data(cbs);
+
+       if (!check_resp(msg, SMS_GSM_CB_ROUTING_RESP)) {
+               ofono_cbs_remove(cbs);
+               return;
+       }
+
+       g_isi_client_ntf_subscribe(cd->client, SMS_GSM_CB_ROUTING_NTF,
+                                       routing_ntf_cb, cbs);
+
+       ofono_cbs_register(cbs);
+}
+
+static void cbs_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_cbs *cbs = data;
+       struct cbs_data *cd = ofono_cbs_get_data(cbs);
+
+       const uint8_t req[] = {
+               SMS_GSM_CB_ROUTING_REQ,
+               SMS_ROUTING_SET,
+               SMS_GSM_ROUTING_MODE_ALL,
+               SMS_CB_NOT_ALLOWED_IDS_LIST,
+               0x00,  /* Subject count */
+               0x00,  /* Language count */
+               0x00,  /* CB range */
+               0x00,  /* Subject list MSBS */
+               0x00,  /* Subject list LSBS */
+               0x00   /* Languages */
+       };
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Unable to find CBS resource");
+               ofono_cbs_remove(cbs);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_send(cd->client, req, sizeof(req), routing_resp_cb,
+                               cbs, NULL);
+}
+
+static int isi_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *modem = user;
+       struct cbs_data *cd = g_try_new0(struct cbs_data, 1);
+
+       if (cd == NULL)
+               return -ENOMEM;
+
+       cd->client = g_isi_client_create(modem, PN_SMS);
+       if (cd->client == NULL) {
+               g_free(cd);
+               return -ENOMEM;
+       }
+
+       ofono_cbs_set_data(cbs, cd);
+
+       g_isi_client_verify(cd->client, cbs_reachable_cb, cbs, NULL);
+
+       return 0;
+}
+
+static void isi_cbs_remove(struct ofono_cbs *cbs)
+{
+       struct cbs_data *cd = ofono_cbs_get_data(cbs);
+       const uint8_t msg[] = {
+               SMS_GSM_CB_ROUTING_REQ,
+               SMS_ROUTING_RELEASE,
+               SMS_GSM_ROUTING_MODE_ALL,
+               SMS_CB_NOT_ALLOWED_IDS_LIST,
+               0x00,  /* Subject count */
+               0x00,  /* Language count */
+               0x00,  /* CB range */
+               0x00,  /* Subject list MSBS */
+               0x00,  /* Subject list LSBS */
+               0x00   /* Languages */
+       };
+
+       ofono_cbs_set_data(cbs, NULL);
+
+       if (cd == NULL)
+               return;
+
+       /*
+        * Send a promiscuous routing release, so as not to hog
+        * resources unnecessarily after being removed.
+        */
+       g_isi_client_send(cd->client, msg, sizeof(msg), NULL, NULL, NULL);
+
+       g_isi_client_destroy(cd->client);
+       g_free(cd);
+}
+
+static struct ofono_cbs_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_cbs_probe,
+       .remove                 = isi_cbs_remove,
+       .set_topics             = isi_set_topics,
+       .clear_topics           = isi_clear_topics
+};
+
+void isi_cbs_init(void)
+{
+       ofono_cbs_driver_register(&driver);
+}
+
+void isi_cbs_exit(void)
+{
+       ofono_cbs_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/debug.c b/drivers/isimodem/debug.c
new file mode 100644 (file)
index 0000000..a312c74
--- /dev/null
@@ -0,0 +1,1499 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sim.h>
+
+#include "debug.h"
+
+#define COMMON_MESSAGE 0xF0
+
+#define _(X) case X: return #X
+
+const char *pn_resource_name(int value)
+{
+       switch (value) {
+               _(PN_NETWORK);
+               _(PN_MODEM_NETWORK);
+               _(PN_PHONE_INFO);
+               _(PN_MODEM_INFO);
+               _(PN_EPOC_INFO);
+               _(PN_SS);
+               _(PN_CALL);
+               _(PN_MODEM_CALL);
+               _(PN_SMS);
+               _(PN_SIM);
+               _(PN_SECURITY);
+               _(PN_MTC);
+               _(PN_MODEM_MCE);
+               _(PN_GSS);
+               _(PN_GPDS);
+               _(PN_WRAN);
+               _(PN_UICC);
+       }
+       return "PN_<UNKNOWN>";
+}
+
+const char *ss_message_id_name(enum ss_message_id value)
+{
+       switch (value) {
+               _(SS_SERVICE_REQ);
+               _(SS_SERVICE_COMPLETED_RESP);
+               _(SS_SERVICE_FAILED_RESP);
+               _(SS_SERVICE_NOT_SUPPORTED_RESP);
+               _(SS_GSM_USSD_SEND_REQ);
+               _(SS_GSM_USSD_SEND_RESP);
+               _(SS_GSM_USSD_RECEIVE_IND);
+               _(SS_STATUS_IND);
+               _(SS_SERVICE_COMPLETED_IND);
+       }
+       return "SS_<UNKNOWN>";
+}
+
+const char *ss_ussd_type_name(enum ss_ussd_type value)
+{
+       switch (value) {
+               _(SS_GSM_USSD_MT_REPLY);
+               _(SS_GSM_USSD_COMMAND);
+               _(SS_GSM_USSD_REQUEST);
+               _(SS_GSM_USSD_NOTIFY);
+               _(SS_GSM_USSD_END);
+       }
+       return "SS_<UNKNOWN>";
+}
+
+
+const char *ss_subblock_name(enum ss_subblock value)
+{
+       switch (value) {
+               _(SS_FORWARDING);
+               _(SS_STATUS_RESULT);
+               _(SS_GSM_PASSWORD);
+               _(SS_GSM_FORWARDING_INFO);
+               _(SS_GSM_FORWARDING_FEATURE);
+               _(SS_GSM_DATA);
+               _(SS_GSM_BSC_INFO);
+               _(SS_GSM_GENERIC_SERVICE_INFO);
+               _(SS_GSM_CLIR_INFO);
+               _(SS_GSM_PASSWORD_INFO);
+               _(SS_GSM_INDICATE_PASSWORD_ERROR);
+               _(SS_GSM_INDICATE_ERROR);
+               _(SS_GSM_ADDITIONAL_INFO);
+               _(SS_GSM_BARRING_INFO);
+               _(SS_GSM_BARRING_FEATURE);
+               _(SS_GSM_USSD_STRING);
+       }
+       return "SS_<UNKNOWN>";
+}
+
+const char *mtc_isi_cause_name(enum mtc_isi_cause value)
+{
+       switch (value) {
+               _(MTC_OK);
+               _(MTC_FAIL);
+               _(MTC_NOT_ALLOWED);
+               _(MTC_STATE_TRANSITION_GOING_ON);
+               _(MTC_ALREADY_ACTIVE);
+               _(MTC_SERVICE_DISABLED);
+               _(MTC_NOT_READY_YET);
+               _(MTC_NOT_SUPPORTED);
+               _(MTC_TRANSITION_ONGOING);
+               _(MTC_RESET_REQUIRED);
+       }
+       return "MTC_<UNKNOWN>";
+}
+
+const char *mtc_message_id_name(enum mtc_message_id value)
+{
+       switch (value) {
+               _(MTC_STATE_REQ);
+               _(MTC_STATE_QUERY_REQ);
+               _(MTC_POWER_OFF_REQ);
+               _(MTC_POWER_ON_REQ);
+               _(MTC_STARTUP_SYNQ_REQ);
+               _(MTC_SHUTDOWN_SYNC_REQ);
+               _(MTC_STATE_RESP);
+               _(MTC_STATE_QUERY_RESP);
+               _(MTC_POWER_OFF_RESP);
+               _(MTC_POWER_ON_RESP);
+               _(MTC_STARTUP_SYNQ_RESP);
+               _(MTC_SHUTDOWN_SYNC_RESP);
+               _(MTC_STATE_INFO_IND);
+       }
+       return "MTC_<UNKNOWN>";
+}
+
+const char *mtc_modem_state_name(enum mtc_modem_state value)
+{
+       switch (value) {
+               _(MTC_POWER_OFF);
+               _(MTC_NORMAL);
+               _(MTC_CHARGING);
+               _(MTC_ALARM);
+               _(MTC_TEST);
+               _(MTC_LOCAL);
+               _(MTC_WARRANTY);
+               _(MTC_RELIABILITY);
+               _(MTC_SELFTEST_FAIL);
+               _(MTC_SWDL);
+               _(MTC_RF_INACTIVE);
+               _(MTC_ID_WRITE);
+               _(MTC_DISCHARGING);
+               _(MTC_DISK_WIPE);
+               _(MTC_SW_RESET);
+               _(MTC_CMT_ONLY_MODE);
+               _(MTC_STATE_NONE);
+       }
+       return "MTC_<UNKNOWN>";
+}
+
+const char *mce_message_id_name(enum mce_message_id value)
+{
+       switch (value) {
+               _(MCE_MODEM_STATE_IND);
+               _(MCE_MODEM_STATE_QUERY_REQ);
+               _(MCE_MODEM_STATE_QUERY_RESP);
+               _(MCE_RF_STATE_REQ);
+               _(MCE_RF_STATE_RESP);
+               _(MCE_RF_STATE_IND);
+               _(MCE_RF_STATE_QUERY_REQ);
+               _(MCE_RF_STATE_QUERY_RESP);
+               _(MCE_POWER_OFF_REQ);
+               _(MCE_POWER_OFF_RESP);
+       }
+       return "MCE_<UNKNOWN>";
+}
+
+const char *mce_modem_state_name(enum mce_modem_state value)
+{
+       switch (value) {
+               _(MCE_NORMAL);
+               _(MCE_LOCAL);
+               _(MCE_SW_RESET);
+               _(MCE_POWER_OFF);
+       }
+       return "MCE_<UNKNOWN>";
+}
+
+const char *mce_status_info(enum mce_status_info value)
+{
+       switch (value) {
+               _(MCE_OK);
+               _(MCE_FAIL);
+               _(MCE_ALREADY_ACTIVE);
+               _(MCE_TRANSITION_ONGOING);
+       }
+       return "MCE_<UNKNOWN>";
+}
+
+const char *mce_rf_state_name(enum mce_rf_state value)
+{
+       switch (value) {
+               _(MCE_RF_OFF);
+               _(MCE_RF_ON);
+       }
+       return "MCE_RF<UNKNOWN>";
+}
+
+const char *uicc_service_type_name(uint8_t value)
+{
+       switch (value) {
+               _(UICC_APPL_LIST);
+               _(UICC_APPL_HOST_ACTIVATE);
+               /*_(UICC_APPL_DEACTIVATE);*/
+               _(UICC_APPL_START_UP_COMPLETE);
+               /*_(UICC_SHUT_DOWN_INITIATED);*/
+               _(UICC_APPL_SHUT_DOWN_INITIATED);
+               _(UICC_APPL_STATUS_GET);
+               _(UICC_APPL_HOST_DEACTIVATE);
+               _(UICC_PIN_VERIFY);
+               _(UICC_PIN_UNBLOCK);
+               _(UICC_PIN_DISABLE);
+               _(UICC_PIN_ENABLE);
+               _(UICC_PIN_CHANGE);
+               _(UICC_PIN_SUBSTITUTE);
+               _(UICC_PIN_INFO);
+               _(UICC_PIN_PROMPT_VERIFY);
+               _(UICC_APPL_READ_TRANSPARENT);
+               _(UICC_APPL_UPDATE_TRANSPARENT);
+               _(UICC_APPL_READ_LINEAR_FIXED);
+               _(UICC_APPL_UPDATE_LINEAR_FIXED);
+               _(UICC_APPL_FILE_INFO);
+               _(UICC_APPL_APDU_SEND);
+               _(UICC_APPL_CLEAR_CACHE);
+               _(UICC_APPL_SESSION_START);
+               _(UICC_APPL_SESSION_END);
+               _(UICC_APPL_READ_CYCLIC);
+               _(UICC_APPL_UPDATE_CYCLIC);
+               /*_(UICC_APPL_CACHE_UPDATED);*/
+               _(UICC_CONNECT);
+               _(UICC_DISCONNECT);
+               _(UICC_RECONNECT);
+               _(UICC_CAT_ENABLE);
+               _(UICC_CAT_DISABLE);
+               _(UICC_CAT_TERMINAL_PROFILE);
+               _(UICC_CAT_TERMINAL_RESPONSE);
+               _(UICC_CAT_ENVELOPE);
+               _(UICC_CAT_POLLING_SET);
+               _(UICC_CAT_REFRESH);
+               _(UICC_CAT_POLL);
+               _(UICC_APDU_SEND);
+               _(UICC_APDU_ATR_GET);
+               _(UICC_APDU_CONTROL);
+               _(UICC_REFRESH_STATUS);
+               _(UICC_APPL_TERMINATED);
+               _(UICC_APPL_RECOVERED);
+               /*_(UICC_APPL_UNAVAILABLE);*/
+               /*_(UICC_APPL_SHUT_DOWN);*/
+               _(UICC_APPL_ACTIVATED);
+               _(UICC_PIN_VERIFY_NEEDED);
+               _(UICC_PIN_UNBLOCK_NEEDED);
+               _(UICC_PIN_PERMANENTLY_BLOCKED);
+               _(UICC_PIN_VERIFIED);
+               _(UICC_CAT_FETCHED_CMD);
+               _(UICC_CAT_NOT_SUPPORTED);
+               _(UICC_CAT_REG_FAILED);
+               _(UICC_CAT_REG_OK);
+               _(UICC_REFRESH_PERMISSION);
+               _(UICC_REFRESH_STARTING);
+               _(UICC_REFRESH_CANCELLED);
+               _(UICC_REFRESH_NOW);
+               _(UICC_START_UP_COMPLETE);
+               _(UICC_STATUS_GET);
+               _(UICC_READY);
+               /*_(UICC_READY_FOR_ACTIVATION);*/
+               _(UICC_INITIALIZED);
+               _(UICC_SHUTTING_DOWN);
+               /*_(UICC_SHUT_DOWN_CONFIG);*/
+               _(UICC_ERROR);
+               _(UICC_CARD_DISCONNECTED);
+               _(UICC_CARD_REMOVED);
+               _(UICC_CARD_NOT_PRESENT);
+               /*_(UICC_CARD_RESET);*/
+               _(UICC_CARD_READY);
+               _(UICC_CARD_STATUS_GET);
+               _(UICC_CARD_REJECTED);
+               _(UICC_CARD_INFO_GET);
+               _(UICC_SIMLOCK_ACTIVE);
+               _(UICC_APDU_SAP_ACTIVATE);
+               _(UICC_APDU_SAP_DEACTIVATE);
+               _(UICC_APDU_SAP_ATR_GET);
+               _(UICC_APDU_SAP_COLD_RESET);
+               _(UICC_APDU_SAP_WARM_RESET);
+               _(UICC_APDU_SAP_APDU_SEND);
+               _(UICC_APDU_SAP_RECOVERY);
+               _(UICC_APDU_SAP_CONFIG_GET);
+               _(UICC_PWR_CTRL_ENABLE);
+               _(UICC_PWR_CTRL_DISABLE);
+               _(UICC_PWR_CTRL_WAIT);
+               _(UICC_PWR_CTRL_PROCEED);
+               _(UICC_PWR_CTRL_PERMISSION);
+       }
+       return "UICC_SERVICE_<UNKNOWN>";
+}
+
+const char *uicc_details_name(uint8_t value)
+{
+
+       switch (value) {
+               /* Used when status differs from UICC_STATUS_FAIL */
+               _(UICC_NO_DETAILS);
+               /* Request was sent with one or more invalid parameter */
+               _(UICC_INVALID_PARAMETERS);
+               /* The file wasn't found */
+               _(UICC_FILE_NOT_FOUND);
+               /* User does not have the required priviledges for this */
+               _(UICC_SECURITY_CONDITIONS_NOT_SATISFIED);
+               /* Application can not be activated due to already active app */
+               _(UICC_APPL_CONFLICT);
+               /* Card Communication error */
+               _(UICC_CARD_ERROR);
+               /* Operation not supported */
+               _(UICC_SERVICE_NOT_SUPPORTED);
+               /* Session expired  */
+               _(UICC_SESSION_EXPIRED);
+       }
+       return "UICC_STATUS<UNKNOWN>";
+}
+
+const char *uicc_message_id_name(enum uicc_message_id value)
+{
+       switch (value) {
+               _(UICC_REQ);
+               _(UICC_RESP);
+               _(UICC_IND);
+               _(UICC_CARD_REQ);
+               _(UICC_CARD_RESP);
+               _(UICC_CARD_IND);
+               _(UICC_APPLICATION_REQ);
+               _(UICC_APPLICATION_RESP);
+               _(UICC_APPLICATION_IND);
+               _(UICC_PIN_REQ);
+               _(UICC_PIN_RESP);
+               _(UICC_PIN_IND);
+               _(UICC_APPL_CMD_REQ);
+               _(UICC_APPL_CMD_RESP);
+               _(UICC_APPL_CMD_IND);
+               _(UICC_CONNECTOR_REQ);
+               _(UICC_CONNECTOR_RESP);
+               _(UICC_CAT_REQ);
+               _(UICC_CAT_RESP);
+               _(UICC_CAT_IND);
+               _(UICC_APDU_REQ);
+               _(UICC_APDU_RESP);
+               _(UICC_APDU_RESET_IND);
+               _(UICC_REFRESH_REQ);
+               _(UICC_REFRESH_RESP);
+               _(UICC_REFRESH_IND);
+               _(UICC_SIMLOCK_REQ);
+               _(UICC_SIMLOCK_RESP);
+               _(UICC_APDU_SAP_REQ);
+               _(UICC_APDU_SAP_RESP);
+               _(UICC_APDU_SAP_IND);
+               _(UICC_PWR_CTRL_REQ);
+               _(UICC_PWR_CTRL_RESP);
+               _(UICC_PWR_CTRL_IND);
+               _(UICC_CARD_READER_IND);
+       }
+       return "UICC_<UNKNOWN>";
+}
+
+const char *uicc_status_name(uint8_t value)
+{
+       switch (value) {
+               /* Request performed successfully */
+               _(UICC_STATUS_OK);
+               /* Error in performing the command */
+               _(UICC_STATUS_FAIL);
+               /* Status is Unknown */
+               _(UICC_STATUS_UNKNOWN);
+               /* Server is not ready */
+               _(UICC_STATUS_NOT_READY);
+               /* Server start up is completed */
+               _(UICC_STATUS_START_UP_COMPLETED);
+               /* Server is shutting down */
+               _(UICC_STATUS_SHUTTING_DOWN);
+               /* Smart card is not ready */
+               _(UICC_STATUS_CARD_NOT_READY);
+               /* Smart card is ready */
+               _(UICC_STATUS_CARD_READY);
+               /* Smart card is disconnected */
+               _(UICC_STATUS_CARD_DISCONNECTED);
+               /* Smart card is not present */
+               _(UICC_STATUS_CARD_NOT_PRESENT);
+               /* Smart card has been rejected */
+               _(UICC_STATUS_CARD_REJECTED);
+               /* Application is active */
+               _(UICC_STATUS_APPL_ACTIVE);
+               /* Application is not active */
+               _(UICC_STATUS_APPL_NOT_ACTIVE);
+               /* PIN verification used */
+               _(UICC_STATUS_PIN_ENABLED);
+               /* PIN verification not used */
+               _(UICC_STATUS_PIN_DISABLED);
+       }
+       return "UICC_STATUS<UNKNOWN>";
+}
+
+const char *uicc_subblock_name(uint8_t value)
+{
+       switch (value) {
+               _(UICC_SB_SHUT_DOWN_CONFIG);
+               _(UICC_SB_CARD_STATUS);
+               _(UICC_SB_CARD_INFO);
+               _(UICC_SB_CARD_REJECT_CAUSE);
+               _(UICC_SB_CLIENT);
+               _(UICC_SB_APPL_DATA_OBJECT);
+               _(UICC_SB_APPLICATION);
+               _(UICC_SB_APPL_INFO);
+               _(UICC_SB_APPL_STATUS);
+               _(UICC_SB_FCP);
+               _(UICC_SB_FCI);
+               _(UICC_SB_CHV);
+               _(UICC_SB_PIN);
+               _(UICC_SB_PIN_REF);
+               _(UICC_SB_PUK);
+               _(UICC_SB_PIN_SUBST);
+               _(UICC_SB_PIN_INFO);
+               _(UICC_SB_APPL_PATH);
+               _(UICC_SB_SESSION);
+               _(UICC_SB_FILE_DATA);
+               _(UICC_SB_APDU);
+               _(UICC_SB_TRANSPARENT_READ);
+               _(UICC_SB_TRANSPARENT_UPDATE);
+               _(UICC_SB_TRANSPARENT);
+               _(UICC_SB_LINEAR_FIXED);
+               _(UICC_SB_CYCLIC);
+               _(UICC_SB_TERMINAL_PROFILE);
+               _(UICC_SB_TERMINAL_RESPONSE);
+               _(UICC_SB_ENVELOPE);
+               _(UICC_SB_POLLING_SET);
+               _(UICC_SB_REFRESH);
+               _(UICC_SB_AID);
+               _(UICC_SB_REFRESH_RESULT);
+               _(UICC_SB_APDU_ACTIONS);
+               _(UICC_SB_OBJECT_ID);
+               _(UICC_SB_STATUS_WORD);
+               _(UICC_SB_APDU_SAP_INFO);
+               _(UICC_SB_ACCESS_MODE);
+               _(UICC_SB_RESP_INFO);
+               _(UICC_SB_APDU_SAP_CONFIG);
+       }
+       return "UICC_<UNKNOWN>";
+}
+
+const char *sms_isi_cause_name(enum sms_isi_cause value)
+{
+       switch (value) {
+               _(SMS_OK);
+               _(SMS_ERR_ROUTING_RELEASED);
+               _(SMS_ERR_INVALID_PARAMETER);
+               _(SMS_ERR_DEVICE_FAILURE);
+               _(SMS_ERR_PP_RESERVED);
+               _(SMS_ERR_ROUTE_NOT_AVAILABLE);
+               _(SMS_ERR_ROUTE_NOT_ALLOWED);
+               _(SMS_ERR_SERVICE_RESERVED);
+               _(SMS_ERR_INVALID_LOCATION);
+               _(SMS_ERR_NO_SIM);
+               _(SMS_ERR_SIM_NOT_READY);
+               _(SMS_ERR_NO_NETW_RESPONSE);
+               _(SMS_ERR_DEST_ADDR_FDN_RESTRICTED);
+               _(SMS_ERR_SMSC_ADDR_FDN_RESTRICTED);
+               _(SMS_ERR_RESEND_ALREADY_DONE);
+               _(SMS_ERR_SMSC_ADDR_NOT_AVAILABLE);
+               _(SMS_ERR_ROUTING_FAILED);
+               _(SMS_ERR_CS_INACTIVE);
+               _(SMS_ERR_SAT_MO_CONTROL_MODIFIED);
+               _(SMS_ERR_SAT_MO_CONTROL_REJECT);
+               _(SMS_ERR_TRACFONE_FAILED);
+       }
+       return "SMS_<UNKNOWN>";
+}
+
+const char *sms_gsm_cause_name(enum sms_gsm_cause value)
+{
+       switch (value) {
+               _(SMS_GSM_ERR_UNASSIGNED_NUMBER);
+               _(SMS_GSM_ERR_OPER_DETERMINED_BARR);
+               _(SMS_GSM_ERR_CALL_BARRED);
+               _(SMS_GSM_ERR_RESERVED);
+               _(SMS_GSM_ERR_MSG_TRANSFER_REJ);
+               _(SMS_GSM_ERR_MEMORY_CAPACITY_EXC);
+               _(SMS_GSM_ERR_DEST_OUT_OF_ORDER);
+               _(SMS_GSM_ERR_UNDEFINED_SUBSCRIBER);
+               _(SMS_GSM_ERR_FACILITY_REJECTED);
+               _(SMS_GSM_ERR_UNKNOWN_SUBSCRIBER);
+               _(SMS_GSM_ERR_NETW_OUT_OF_ORDER);
+               _(SMS_GSM_ERR_TEMPORARY_FAILURE);
+               _(SMS_GSM_ERR_CONGESTION);
+               _(SMS_GSM_ERR_RESOURCE_UNAVAILABLE);
+               _(SMS_GSM_ERR_REQ_FACILITY_NOT_SUB);
+               _(SMS_GSM_ERR_REQ_FACILITY_NOT_IMP);
+               _(SMS_GSM_ERR_INVALID_REFERENCE);
+               _(SMS_GSM_ERR_INCORRECT_MESSAGE);
+               _(SMS_GSM_ERR_INVALID_MAND_INFO);
+               _(SMS_GSM_ERR_INVALID_MSG_TYPE);
+               _(SMS_GSM_ERR_MSG_NOT_COMP_WITH_ST);
+               _(SMS_GSM_ERR_INVALID_INFO_ELEMENT);
+               _(SMS_GSM_ERR_PROTOCOL_ERROR);
+               _(SMS_GSM_ERR_INTERWORKING);
+               _(SMS_GSM_ERR_NO_CAUSE);
+               _(SMS_GSM_ERR_IMSI_UNKNOWN_HLR);
+               _(SMS_GSM_ERR_ILLEGAL_MS);
+               _(SMS_GSM_ERR_IMSI_UNKNOWN_VLR);
+               _(SMS_GSM_ERR_IMEI_NOT_ACCEPTED);
+               _(SMS_GSM_ERR_ILLEGAL_ME);
+               _(SMS_GSM_ERR_PLMN_NOT_ALLOWED);
+               _(SMS_GSM_ERR_LA_NOT_ALLOWED);
+               _(SMS_GSM_ERR_ROAM_NOT_ALLOWED_LA);
+               _(SMS_GSM_ERR_NO_SUITABLE_CELLS_LA);
+               _(SMS_GSM_ERR_NETWORK_FAILURE);
+               _(SMS_GSM_ERR_MAC_FAILURE);
+               _(SMS_GSM_ERR_SYNC_FAILURE);
+               _(SMS_GSM_ERR_LOW_LAYER_CONGESTION);
+               _(SMS_GSM_ERR_AUTH_UNACCEPTABLE);
+               _(SMS_GSM_ERR_SERV_OPT_NOT_SUPPORTED);
+               _(SMS_GSM_ERR_SERV_OPT_NOT_SUBSCRIBED);
+               _(SMS_GSM_ERR_SERV_OPT_TEMP_OUT_OF_ORDER);
+               _(SMS_GSM_ERR_CALL_CANNOT_BE_IDENTIFIED);
+               _(SMS_GSM_ERR_SEMANTICALLY_INCORR_MSG);
+               _(SMS_GSM_ERR_LOW_LAYER_INVALID_MAND_INFO);
+               _(SMS_GSM_ERR_LOW_LAYER_INVALID_MSG_TYPE);
+               _(SMS_GSM_ERR_LOW_LAYER_MSG_TYPE_NOT_COMP_WITH_ST);
+               _(SMS_GSM_ERR_LOW_LAYER_INVALID_INFO_ELEMENT);
+               _(SMS_GSM_ERR_CONDITIONAL_IE_ERROR);
+               _(SMS_GSM_ERR_LOW_LAYER_MSG_NOT_COMP_WITH_ST);
+               _(SMS_GSM_ERR_CS_BARRED);
+               _(SMS_GSM_ERR_LOW_LAYER_PROTOCOL_ERROR);
+       }
+       return "SMS_<UNKNOWN>";
+}
+
+const char *sms_message_id_name(enum sms_message_id value)
+{
+       switch (value) {
+               _(SMS_MESSAGE_SEND_REQ);
+               _(SMS_MESSAGE_SEND_RESP);
+               _(SMS_PP_ROUTING_REQ);
+               _(SMS_PP_ROUTING_RESP);
+               _(SMS_PP_ROUTING_NTF);
+               _(SMS_GSM_RECEIVED_PP_REPORT_REQ);
+               _(SMS_GSM_RECEIVED_PP_REPORT_RESP);
+               _(SMS_GSM_CB_ROUTING_REQ);
+               _(SMS_GSM_CB_ROUTING_RESP);
+               _(SMS_GSM_CB_ROUTING_NTF);
+               _(SMS_MESSAGE_SEND_STATUS_IND);
+               _(SMS_SETTINGS_UPDATE_REQ);
+               _(SMS_SETTINGS_UPDATE_RESP);
+               _(SMS_SETTINGS_READ_REQ);
+               _(SMS_SETTINGS_READ_RESP);
+               _(SMS_RECEIVED_MSG_REPORT_REQ);
+               _(SMS_RECEIVED_MSG_REPORT_RESP);
+               _(SMS_RECEIVE_MESSAGE_REQ);
+               _(SMS_RECEIVE_MESSAGE_RESP);
+               _(SMS_RECEIVED_MSG_IND);
+       }
+       return "SMS_<UNKNOWN>";
+}
+
+const char *sms_subblock_name(enum sms_subblock value)
+{
+       switch (value) {
+               _(SMS_GSM_DELIVER);
+               _(SMS_GSM_STATUS_REPORT);
+               _(SMS_GSM_SUBMIT);
+               _(SMS_GSM_COMMAND);
+               _(SMS_GSM_DELIVER_REPORT);
+               _(SMS_GSM_REPORT);
+               _(SMS_GSM_ROUTING);
+               _(SMS_GSM_CB_MESSAGE);
+               _(SMS_GSM_TPDU);
+               _(SMS_SB_TPDU);
+               _(SMS_SB_ROUTE_INFO);
+               _(SMS_SB_SMS_PARAMETERS);
+               _(SMS_COMMON_DATA);
+               _(SMS_ADDRESS);
+               /* _(SMS_SB_ADDRESS); */
+       }
+       return "SMS_<UNKNOWN>";
+}
+
+const char *sim_isi_cause_name(enum sim_isi_cause value)
+{
+       switch (value) {
+               _(SIM_SERV_NOT_AVAIL);
+               _(SIM_SERV_OK);
+               _(SIM_SERV_PIN_VERIFY_REQUIRED);
+               _(SIM_SERV_PIN_REQUIRED);
+               _(SIM_SERV_SIM_BLOCKED);
+               _(SIM_SERV_SIM_PERMANENTLY_BLOCKED);
+               _(SIM_SERV_SIM_DISCONNECTED);
+               _(SIM_SERV_SIM_REJECTED);
+               _(SIM_SERV_LOCK_ACTIVE);
+               _(SIM_SERV_AUTOLOCK_CLOSED);
+               _(SIM_SERV_AUTOLOCK_ERROR);
+               _(SIM_SERV_INIT_OK);
+               _(SIM_SERV_INIT_NOT_OK);
+               _(SIM_SERV_WRONG_OLD_PIN);
+               _(SIM_SERV_PIN_DISABLED);
+               _(SIM_SERV_COMMUNICATION_ERROR);
+               _(SIM_SERV_UPDATE_IMPOSSIBLE);
+               _(SIM_SERV_NO_SECRET_CODE_IN_SIM);
+               _(SIM_SERV_PIN_ENABLE_OK);
+               _(SIM_SERV_PIN_DISABLE_OK);
+               _(SIM_SERV_WRONG_UNBLOCKING_KEY);
+               _(SIM_SERV_ILLEGAL_NUMBER);
+               _(SIM_SERV_NOT_OK);
+               _(SIM_SERV_PN_LIST_ENABLE_OK);
+               _(SIM_SERV_PN_LIST_DISABLE_OK);
+               _(SIM_SERV_NO_PIN);
+               _(SIM_SERV_PIN_VERIFY_OK);
+               _(SIM_SERV_PIN_BLOCKED);
+               _(SIM_SERV_PIN_PERM_BLOCKED);
+               _(SIM_SERV_DATA_NOT_AVAIL);
+               _(SIM_SERV_IN_HOME_ZONE);
+               _(SIM_SERV_STATE_CHANGED);
+               _(SIM_SERV_INF_NBR_READ_OK);
+               _(SIM_SERV_INF_NBR_READ_NOT_OK);
+               _(SIM_SERV_IMSI_EQUAL);
+               _(SIM_SERV_IMSI_NOT_EQUAL);
+               _(SIM_SERV_INVALID_LOCATION);
+               _(SIM_SERV_STA_SIM_REMOVED);
+               _(SIM_SERV_SECOND_SIM_REMOVED_CS);
+               _(SIM_SERV_CONNECTED_INDICATION_CS);
+               _(SIM_SERV_SECOND_SIM_CONNECTED_CS);
+               _(SIM_SERV_PIN_RIGHTS_LOST_IND_CS);
+               _(SIM_SERV_PIN_RIGHTS_GRANTED_IND_CS);
+               _(SIM_SERV_INIT_OK_CS);
+               _(SIM_SERV_INIT_NOT_OK_CS);
+               _(SIM_FDN_ENABLED);
+               _(SIM_FDN_DISABLED);
+               _(SIM_SERV_INVALID_FILE);
+               _(SIM_SERV_DATA_AVAIL);
+               _(SIM_SERV_ICC_EQUAL);
+               _(SIM_SERV_ICC_NOT_EQUAL);
+               _(SIM_SERV_SIM_NOT_INITIALISED);
+               _(SIM_SERV_SERVICE_NOT_AVAIL);
+               _(SIM_SERV_FDN_STATUS_ERROR);
+               _(SIM_SERV_FDN_CHECK_PASSED);
+               _(SIM_SERV_FDN_CHECK_FAILED);
+               _(SIM_SERV_FDN_CHECK_DISABLED);
+               _(SIM_SERV_FDN_CHECK_NO_FDN_SIM);
+               _(SIM_STA_ISIM_AVAILEBLE_PIN_REQUIRED);
+               _(SIM_STA_ISIM_AVAILEBLE);
+               _(SIM_STA_USIM_AVAILEBLE);
+               _(SIM_STA_SIM_AVAILEBLE);
+               _(SIM_STA_ISIM_NOT_INITIALIZED);
+               _(SIM_STA_IMS_READY);
+               _(SIM_STA_APP_DATA_READ_OK);
+               _(SIM_STA_APP_ACTIVATE_OK);
+               _(SIM_STA_APP_ACTIVATE_NOT_OK);
+               _(SIM_SERV_NOT_DEFINED);
+               _(SIM_SERV_NOSERVICE);
+               _(SIM_SERV_NOTREADY);
+               _(SIM_SERV_ERROR);
+               _(SIM_SERV_CIPHERING_INDICATOR_DISPLAY_REQUIRED);
+               _(SIM_SERV_CIPHERING_INDICATOR_DISPLAY_NOT_REQUIRED);
+               _(SIM_SERV_FILE_NOT_AVAILABLE);
+       }
+       return "SIM_<UNKNOWN>";
+}
+
+const char *sim_message_id_name(enum sim_message_id value)
+{
+       switch (value) {
+               _(SIM_NETWORK_INFO_REQ);
+               _(SIM_NETWORK_INFO_RESP);
+               _(SIM_IMSI_REQ_READ_IMSI);
+               _(SIM_IMSI_RESP_READ_IMSI);
+               _(SIM_SERV_PROV_NAME_REQ);
+               _(SIM_SERV_PROV_NAME_RESP);
+               _(SIM_DYNAMIC_FLAGS_REQ);
+               _(SIM_DYNAMIC_FLAGS_RESP);
+               _(SIM_READ_FIELD_REQ);
+               _(SIM_READ_FIELD_RESP);
+               _(SIM_SMS_REQ);
+               _(SIM_SMS_RESP);
+               _(SIM_STATUS_REQ);
+               _(SIM_STATUS_RESP);
+               _(SIM_PB_REQ_SIM_PB_READ);
+               _(SIM_PB_RESP_SIM_PB_READ);
+               _(SIM_SERVER_READY_IND);
+               _(SIM_IND);
+       }
+
+       return "SIM_<UNKNOWN>";
+}
+
+const char *sim_password_name(enum ofono_sim_password_type type)
+{
+       static const char *const passwd_name[] = {
+               [OFONO_SIM_PASSWORD_NONE] = "none",
+               [OFONO_SIM_PASSWORD_SIM_PIN] = "pin",
+               [OFONO_SIM_PASSWORD_SIM_PUK] = "puk",
+               [OFONO_SIM_PASSWORD_PHSIM_PIN] = "phone",
+               [OFONO_SIM_PASSWORD_PHFSIM_PIN] = "firstphone",
+               [OFONO_SIM_PASSWORD_PHFSIM_PUK] = "firstphonepuk",
+               [OFONO_SIM_PASSWORD_SIM_PIN2] = "pin2",
+               [OFONO_SIM_PASSWORD_SIM_PUK2] = "puk2",
+               [OFONO_SIM_PASSWORD_PHNET_PIN] = "network",
+               [OFONO_SIM_PASSWORD_PHNET_PUK] = "networkpuk",
+               [OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "netsub",
+               [OFONO_SIM_PASSWORD_PHNETSUB_PUK] = "netsubpuk",
+               [OFONO_SIM_PASSWORD_PHSP_PIN] = "service",
+               [OFONO_SIM_PASSWORD_PHSP_PUK] = "servicepuk",
+               [OFONO_SIM_PASSWORD_PHCORP_PIN] = "corp",
+               [OFONO_SIM_PASSWORD_PHCORP_PUK] = "corppuk",
+               [OFONO_SIM_PASSWORD_INVALID] = "invalid",
+       };
+
+       if (OFONO_SIM_PASSWORD_NONE <= (int)type &&
+                       type <= OFONO_SIM_PASSWORD_PHCORP_PUK)
+               return passwd_name[type];
+       else
+               return "UNKNOWN";
+}
+
+const char *sec_message_id_name(enum sec_message_id value)
+{
+       switch (value) {
+               _(SEC_CODE_STATE_REQ);
+               _(SEC_CODE_STATE_OK_RESP);
+               _(SEC_CODE_STATE_FAIL_RESP);
+               _(SEC_CODE_CHANGE_REQ);
+               _(SEC_CODE_CHANGE_OK_RESP);
+               _(SEC_CODE_CHANGE_FAIL_RESP);
+               _(SEC_CODE_VERIFY_REQ);
+               _(SEC_CODE_VERIFY_OK_RESP);
+               _(SEC_CODE_VERIFY_FAIL_RESP);
+               _(SEC_STATE_REQ);
+               _(SEC_STATE_RESP);
+       }
+
+       return "SEC_<UNKNOWN>";
+}
+
+const char *sim_subblock_name(enum sim_subblock value)
+{
+       switch (value) {
+               _(SIM_PB_INFO_REQUEST);
+               _(SIM_PB_STATUS);
+               _(SIM_PB_LOCATION);
+               _(SIM_PB_LOCATION_SEARCH);
+       }
+       return "SIM_<UNKNOWN>";
+}
+
+const char *info_isi_cause_name(enum info_isi_cause value)
+{
+       switch (value) {
+               _(INFO_OK);
+               _(INFO_FAIL);
+               _(INFO_NO_NUMBER);
+               _(INFO_NOT_SUPPORTED);
+       }
+       return "INFO_<UNKNOWN>";
+}
+
+const char *info_message_id_name(enum info_message_id value)
+{
+       switch (value) {
+               _(INFO_SERIAL_NUMBER_READ_REQ);
+               _(INFO_SERIAL_NUMBER_READ_RESP);
+               _(INFO_PP_READ_REQ);
+               _(INFO_PP_READ_RESP);
+               _(INFO_VERSION_READ_REQ);
+               _(INFO_VERSION_READ_RESP);
+               _(INFO_PRODUCT_INFO_READ_REQ);
+               _(INFO_PRODUCT_INFO_READ_RESP);
+       }
+       return "INFO_<UNKNOWN>";
+}
+
+const char *info_subblock_name(enum info_subblock value)
+{
+       switch (value) {
+               _(INFO_SB_MODEMSW_VERSION);
+               _(INFO_SB_PRODUCT_INFO_NAME);
+               _(INFO_SB_PRODUCT_INFO_MANUFACTURER);
+               _(INFO_SB_SN_IMEI_PLAIN);
+               _(INFO_SB_SN_IMEI_SV_TO_NET);
+               _(INFO_SB_PP);
+               _(INFO_SB_MCUSW_VERSION);
+       }
+       return "INFO_<UNKNOWN>";
+}
+
+const char *call_status_name(enum call_status value)
+{
+       switch (value) {
+               _(CALL_STATUS_IDLE);
+               _(CALL_STATUS_CREATE);
+               _(CALL_STATUS_COMING);
+               _(CALL_STATUS_PROCEEDING);
+               _(CALL_STATUS_MO_ALERTING);
+               _(CALL_STATUS_MT_ALERTING);
+               _(CALL_STATUS_WAITING);
+               _(CALL_STATUS_ANSWERED);
+               _(CALL_STATUS_ACTIVE);
+               _(CALL_STATUS_MO_RELEASE);
+               _(CALL_STATUS_MT_RELEASE);
+               _(CALL_STATUS_HOLD_INITIATED);
+               _(CALL_STATUS_HOLD);
+               _(CALL_STATUS_RETRIEVE_INITIATED);
+               _(CALL_STATUS_RECONNECT_PENDING);
+               _(CALL_STATUS_TERMINATED);
+               _(CALL_STATUS_SWAP_INITIATED);
+       }
+       return "CALL_<UNKNOWN>";
+}
+
+char const *call_message_id_name(enum call_message_id value)
+{
+       switch (value) {
+               _(CALL_CREATE_REQ);
+               _(CALL_CREATE_RESP);
+               _(CALL_COMING_IND);
+               _(CALL_MO_ALERT_IND);
+               _(CALL_MT_ALERT_IND);
+               _(CALL_WAITING_IND);
+               _(CALL_ANSWER_REQ);
+               _(CALL_ANSWER_RESP);
+               _(CALL_RELEASE_REQ);
+               _(CALL_RELEASE_RESP);
+               _(CALL_RELEASE_IND);
+               _(CALL_TERMINATED_IND);
+               _(CALL_STATUS_REQ);
+               _(CALL_STATUS_RESP);
+               _(CALL_STATUS_IND);
+               _(CALL_SERVER_STATUS_IND);
+               _(CALL_CONTROL_REQ);
+               _(CALL_CONTROL_RESP);
+               _(CALL_CONTROL_IND);
+               _(CALL_MODE_SWITCH_REQ);
+               _(CALL_MODE_SWITCH_RESP);
+               _(CALL_MODE_SWITCH_IND);
+               _(CALL_DTMF_SEND_REQ);
+               _(CALL_DTMF_SEND_RESP);
+               _(CALL_DTMF_STOP_REQ);
+               _(CALL_DTMF_STOP_RESP);
+               _(CALL_DTMF_STATUS_IND);
+               _(CALL_DTMF_TONE_IND);
+               _(CALL_RECONNECT_IND);
+               _(CALL_PROPERTY_GET_REQ);
+               _(CALL_PROPERTY_GET_RESP);
+               _(CALL_PROPERTY_SET_REQ);
+               _(CALL_PROPERTY_SET_RESP);
+               _(CALL_PROPERTY_SET_IND);
+               _(CALL_EMERGENCY_NBR_CHECK_REQ);
+               _(CALL_EMERGENCY_NBR_CHECK_RESP);
+               _(CALL_EMERGENCY_NBR_GET_REQ);
+               _(CALL_EMERGENCY_NBR_GET_RESP);
+               _(CALL_EMERGENCY_NBR_MODIFY_REQ);
+               _(CALL_EMERGENCY_NBR_MODIFY_RESP);
+               _(CALL_GSM_NOTIFICATION_IND);
+               _(CALL_GSM_USER_TO_USER_REQ);
+               _(CALL_GSM_USER_TO_USER_RESP);
+               _(CALL_GSM_USER_TO_USER_IND);
+               _(CALL_GSM_BLACKLIST_CLEAR_REQ);
+               _(CALL_GSM_BLACKLIST_CLEAR_RESP);
+               _(CALL_GSM_BLACKLIST_TIMER_IND);
+               _(CALL_GSM_DATA_CH_INFO_IND);
+               _(CALL_GSM_CCP_GET_REQ);
+               _(CALL_GSM_CCP_GET_RESP);
+               _(CALL_GSM_CCP_CHECK_REQ);
+               _(CALL_GSM_CCP_CHECK_RESP);
+               _(CALL_GSM_COMING_REJ_IND);
+               _(CALL_GSM_RAB_IND);
+               _(CALL_GSM_IMMEDIATE_MODIFY_IND);
+               _(CALL_CREATE_NO_SIMATK_REQ);
+               _(CALL_GSM_SS_DATA_IND);
+               _(CALL_TIMER_REQ);
+               _(CALL_TIMER_RESP);
+               _(CALL_TIMER_NTF);
+               _(CALL_TIMER_IND);
+               _(CALL_TIMER_RESET_REQ);
+               _(CALL_TIMER_RESET_RESP);
+               _(CALL_EMERGENCY_NBR_IND);
+               _(CALL_SERVICE_DENIED_IND);
+               _(CALL_RELEASE_END_REQ);
+               _(CALL_RELEASE_END_RESP);
+               _(CALL_USER_CONNECT_IND);
+               _(CALL_AUDIO_CONNECT_IND);
+               _(CALL_KODIAK_ALLOW_CTRL_REQ);
+               _(CALL_KODIAK_ALLOW_CTRL_RESP);
+               _(CALL_SERVICE_ACTIVATE_IND);
+               _(CALL_SERVICE_ACTIVATE_REQ);
+               _(CALL_SERVICE_ACTIVATE_RESP);
+               _(CALL_SIM_ATK_IND);
+               _(CALL_CONTROL_OPER_IND);
+               _(CALL_TEST_CALL_STATUS_IND);
+               _(CALL_SIM_ATK_INFO_IND);
+               _(CALL_SECURITY_IND);
+               _(CALL_MEDIA_HANDLE_REQ);
+               _(CALL_MEDIA_HANDLE_RESP);
+       }
+       return "CALL_<UNKNOWN>";
+}
+
+char const *call_isi_cause_name(enum call_isi_cause value)
+{
+       switch (value) {
+               _(CALL_CAUSE_NO_CAUSE);
+               _(CALL_CAUSE_NO_CALL);
+               _(CALL_CAUSE_TIMEOUT);
+               _(CALL_CAUSE_RELEASE_BY_USER);
+               _(CALL_CAUSE_BUSY_USER_REQUEST);
+               _(CALL_CAUSE_ERROR_REQUEST);
+               _(CALL_CAUSE_COST_LIMIT_REACHED);
+               _(CALL_CAUSE_CALL_ACTIVE);
+               _(CALL_CAUSE_NO_CALL_ACTIVE);
+               _(CALL_CAUSE_INVALID_CALL_MODE);
+               _(CALL_CAUSE_SIGNALLING_FAILURE);
+               _(CALL_CAUSE_TOO_LONG_ADDRESS);
+               _(CALL_CAUSE_INVALID_ADDRESS);
+               _(CALL_CAUSE_EMERGENCY);
+               _(CALL_CAUSE_NO_TRAFFIC_CHANNEL);
+               _(CALL_CAUSE_NO_COVERAGE);
+               _(CALL_CAUSE_CODE_REQUIRED);
+               _(CALL_CAUSE_NOT_ALLOWED);
+               _(CALL_CAUSE_NO_DTMF);
+               _(CALL_CAUSE_CHANNEL_LOSS);
+               _(CALL_CAUSE_FDN_NOT_OK);
+               _(CALL_CAUSE_USER_TERMINATED);
+               _(CALL_CAUSE_BLACKLIST_BLOCKED);
+               _(CALL_CAUSE_BLACKLIST_DELAYED);
+               _(CALL_CAUSE_NUMBER_NOT_FOUND);
+               _(CALL_CAUSE_NUMBER_CANNOT_REMOVE);
+               _(CALL_CAUSE_EMERGENCY_FAILURE);
+               _(CALL_CAUSE_CS_SUSPENDED);
+               _(CALL_CAUSE_DCM_DRIVE_MODE);
+               _(CALL_CAUSE_MULTIMEDIA_NOT_ALLOWED);
+               _(CALL_CAUSE_SIM_REJECTED);
+               _(CALL_CAUSE_NO_SIM);
+               _(CALL_CAUSE_SIM_LOCK_OPERATIVE);
+               _(CALL_CAUSE_SIMATKCC_REJECTED);
+               _(CALL_CAUSE_SIMATKCC_MODIFIED);
+               _(CALL_CAUSE_DTMF_INVALID_DIGIT);
+               _(CALL_CAUSE_DTMF_SEND_ONGOING);
+               _(CALL_CAUSE_CS_INACTIVE);
+               _(CALL_CAUSE_SECURITY_MODE);
+               _(CALL_CAUSE_TRACFONE_FAILED);
+               _(CALL_CAUSE_TRACFONE_WAIT_FAILED);
+               _(CALL_CAUSE_TRACFONE_CONF_FAILED);
+               _(CALL_CAUSE_TEMPERATURE_LIMIT);
+               _(CALL_CAUSE_KODIAK_POC_FAILED);
+               _(CALL_CAUSE_NOT_REGISTERED);
+               _(CALL_CAUSE_CS_CALLS_ONLY);
+               _(CALL_CAUSE_VOIP_CALLS_ONLY);
+               _(CALL_CAUSE_LIMITED_CALL_ACTIVE);
+               _(CALL_CAUSE_LIMITED_CALL_NOT_ALLOWED);
+               _(CALL_CAUSE_SECURE_CALL_NOT_POSSIBLE);
+               _(CALL_CAUSE_INTERCEPT);
+       }
+       return "CALL_<UNKNOWN>";
+}
+
+char const *call_gsm_cause_name(enum call_gsm_cause value)
+{
+       switch (value) {
+               _(CALL_GSM_CAUSE_UNASSIGNED_NUMBER);
+               _(CALL_GSM_CAUSE_NO_ROUTE);
+               _(CALL_GSM_CAUSE_CH_UNACCEPTABLE);
+               _(CALL_GSM_CAUSE_OPER_BARRING);
+               _(CALL_GSM_CAUSE_NORMAL);
+               _(CALL_GSM_CAUSE_USER_BUSY);
+               _(CALL_GSM_CAUSE_NO_USER_RESPONSE);
+               _(CALL_GSM_CAUSE_ALERT_NO_ANSWER);
+               _(CALL_GSM_CAUSE_CALL_REJECTED);
+               _(CALL_GSM_CAUSE_NUMBER_CHANGED);
+               _(CALL_GSM_CAUSE_NON_SELECT_CLEAR);
+               _(CALL_GSM_CAUSE_DEST_OUT_OF_ORDER);
+               _(CALL_GSM_CAUSE_INVALID_NUMBER);
+               _(CALL_GSM_CAUSE_FACILITY_REJECTED);
+               _(CALL_GSM_CAUSE_RESP_TO_STATUS);
+               _(CALL_GSM_CAUSE_NORMAL_UNSPECIFIED);
+               _(CALL_GSM_CAUSE_NO_CHANNEL);
+               _(CALL_GSM_CAUSE_NETW_OUT_OF_ORDER);
+               _(CALL_GSM_CAUSE_TEMPORARY_FAILURE);
+               _(CALL_GSM_CAUSE_CONGESTION);
+               _(CALL_GSM_CAUSE_ACCESS_INFO_DISC);
+               _(CALL_GSM_CAUSE_CHANNEL_NA);
+               _(CALL_GSM_CAUSE_RESOURCES_NA);
+               _(CALL_GSM_CAUSE_QOS_NA);
+               _(CALL_GSM_CAUSE_FACILITY_UNSUBS);
+               _(CALL_GSM_CAUSE_COMING_BARRED_CUG);
+               _(CALL_GSM_CAUSE_BC_UNAUTHORIZED);
+               _(CALL_GSM_CAUSE_BC_NA);
+               _(CALL_GSM_CAUSE_SERVICE_NA);
+               _(CALL_GSM_CAUSE_BEARER_NOT_IMPL);
+               _(CALL_GSM_CAUSE_ACM_MAX);
+               _(CALL_GSM_CAUSE_FACILITY_NOT_IMPL);
+               _(CALL_GSM_CAUSE_ONLY_RDI_BC);
+               _(CALL_GSM_CAUSE_SERVICE_NOT_IMPL);
+               _(CALL_GSM_CAUSE_INVALID_TI);
+               _(CALL_GSM_CAUSE_NOT_IN_CUG);
+               _(CALL_GSM_CAUSE_INCOMPATIBLE_DEST);
+               _(CALL_GSM_CAUSE_INV_TRANS_NET_SEL);
+               _(CALL_GSM_CAUSE_SEMANTICAL_ERR);
+               _(CALL_GSM_CAUSE_INVALID_MANDATORY);
+               _(CALL_GSM_CAUSE_MSG_TYPE_INEXIST);
+               _(CALL_GSM_CAUSE_MSG_TYPE_INCOMPAT);
+               _(CALL_GSM_CAUSE_IE_NON_EXISTENT);
+               _(CALL_GSM_CAUSE_COND_IE_ERROR);
+               _(CALL_GSM_CAUSE_MSG_INCOMPATIBLE);
+               _(CALL_GSM_CAUSE_TIMER_EXPIRY);
+               _(CALL_GSM_CAUSE_PROTOCOL_ERROR);
+               _(CALL_GSM_CAUSE_INTERWORKING);
+       }
+       return "CALL_<UNKNOWN>";
+}
+
+const char *net_gsm_cause_name(enum net_gsm_cause value)
+{
+       switch (value) {
+               _(NET_GSM_IMSI_UNKNOWN_IN_HLR);
+               _(NET_GSM_ILLEGAL_MS);
+               _(NET_GSM_IMSI_UNKNOWN_IN_VLR);
+               _(NET_GSM_IMEI_NOT_ACCEPTED);
+               _(NET_GSM_ILLEGAL_ME);
+               _(NET_GSM_GPRS_SERVICES_NOT_ALLOWED);
+               _(NET_GSM_GPRS_AND_NON_GPRS_NA);
+               _(NET_GSM_MS_ID_CANNOT_BE_DERIVED);
+               _(NET_GSM_IMPLICITLY_DETACHED);
+               _(NET_GSM_PLMN_NOT_ALLOWED);
+               _(NET_GSM_LA_NOT_ALLOWED);
+               _(NET_GSM_ROAMING_NOT_IN_THIS_LA);
+               _(NET_GSM_GPRS_SERV_NA_IN_THIS_PLMN);
+               _(NET_GSM_NO_SUITABLE_CELLS_IN_LA);
+               _(NET_GSM_MSC_TEMP_NOT_REACHABLE);
+               _(NET_GSM_NETWORK_FAILURE);
+               _(NET_GSM_MAC_FAILURE);
+               _(NET_GSM_SYNCH_FAILURE);
+               _(NET_GSM_CONGESTION);
+               _(NET_GSM_AUTH_UNACCEPTABLE);
+               _(NET_GSM_SERV_OPT_NOT_SUPPORTED);
+               _(NET_GSM_SERV_OPT_NOT_SUBSCRIBED);
+               _(NET_GSM_SERV_TEMP_OUT_OF_ORDER);
+               _(NET_GSM_RETRY_ENTRY_NEW_CELL_LOW);
+               _(NET_GSM_RETRY_ENTRY_NEW_CELL_HIGH);
+               _(NET_GSM_SEMANTICALLY_INCORRECT);
+               _(NET_GSM_INVALID_MANDATORY_INFO);
+               _(NET_GSM_MSG_TYPE_NONEXISTENT);
+               _(NET_GSM_CONDITIONAL_IE_ERROR);
+               _(NET_GSM_MSG_TYPE_WRONG_STATE);
+               _(NET_GSM_PROTOCOL_ERROR_UNSPECIFIED);
+       }
+       return "NET_<UNKNOWN>";
+}
+
+const char *net_isi_cause_name(enum net_isi_cause value)
+{
+       switch (value) {
+               _(NET_CAUSE_OK);
+               _(NET_CAUSE_COMMUNICATION_ERROR);
+               _(NET_CAUSE_INVALID_PARAMETER);
+               _(NET_CAUSE_NO_SIM);
+               _(NET_CAUSE_SIM_NOT_YET_READY);
+               _(NET_CAUSE_NET_NOT_FOUND);
+               _(NET_CAUSE_REQUEST_NOT_ALLOWED);
+               _(NET_CAUSE_CALL_ACTIVE);
+               _(NET_CAUSE_SERVER_BUSY);
+               _(NET_CAUSE_SECURITY_CODE_REQUIRED);
+               _(NET_CAUSE_NOTHING_TO_CANCEL);
+               _(NET_CAUSE_UNABLE_TO_CANCEL);
+               _(NET_CAUSE_NETWORK_FORBIDDEN);
+               _(NET_CAUSE_REQUEST_REJECTED);
+               _(NET_CAUSE_CS_NOT_SUPPORTED);
+               _(NET_CAUSE_PAR_INFO_NOT_AVAILABLE);
+               _(NET_CAUSE_NOT_DONE);
+               _(NET_CAUSE_NO_SELECTED_NETWORK);
+               _(NET_CAUSE_REQUEST_INTERRUPTED);
+               _(NET_CAUSE_TOO_BIG_INDEX);
+               _(NET_CAUSE_MEMORY_FULL);
+               _(NET_CAUSE_SERVICE_NOT_ALLOWED);
+               _(NET_CAUSE_NOT_SUPPORTED_IN_TECH);
+       }
+       return "NET_<UNKNOWN>";
+}
+
+const char *net_status_name(enum net_reg_status value)
+{
+       switch (value) {
+               _(NET_REG_STATUS_HOME);
+               _(NET_REG_STATUS_ROAM);
+               _(NET_REG_STATUS_ROAM_BLINK);
+               _(NET_REG_STATUS_NOSERV);
+               _(NET_REG_STATUS_NOSERV_SEARCHING);
+               _(NET_REG_STATUS_NOSERV_NOTSEARCHING);
+               _(NET_REG_STATUS_NOSERV_NOSIM);
+               _(NET_REG_STATUS_POWER_OFF);
+               _(NET_REG_STATUS_NSPS);
+               _(NET_REG_STATUS_NSPS_NO_COVERAGE);
+               _(NET_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW);
+       }
+       return "NET_<UNKNOWN>";
+}
+
+const char *net_message_id_name(enum net_message_id value)
+{
+       switch (value) {
+               _(NET_MODEM_REG_STATUS_GET_REQ);
+               _(NET_MODEM_REG_STATUS_GET_RESP);
+               _(NET_MODEM_REG_STATUS_IND);
+               _(NET_MODEM_AVAILABLE_GET_REQ);
+               _(NET_MODEM_AVAILABLE_GET_RESP);
+               _(NET_SET_REQ);
+               _(NET_SET_RESP);
+               _(NET_RSSI_GET_REQ);
+               _(NET_RSSI_GET_RESP);
+               _(NET_CS_STATE_IND);
+               _(NET_RSSI_IND);
+               _(NET_CIPHERING_IND);
+               _(NET_TIME_IND);
+               _(NET_CHANNEL_INFO_IND);
+               _(NET_RAT_IND);
+               _(NET_RAT_REQ);
+               _(NET_RAT_RESP);
+               _(NET_CS_STATE_REQ);
+               _(NET_CS_STATE_RESP);
+               _(NET_CELL_INFO_GET_REQ);
+               _(NET_CELL_INFO_GET_RESP);
+               _(NET_CELL_INFO_IND);
+               _(NET_NITZ_NAME_IND);
+               _(NET_NW_ACCESS_CONF_REQ);
+               _(NET_NW_ACCESS_CONF_RESP);
+               _(NET_REG_STATUS_GET_REQ);
+               _(NET_REG_STATUS_GET_RESP);
+               _(NET_REG_STATUS_IND);
+               _(NET_AVAILABLE_GET_REQ);
+               _(NET_AVAILABLE_GET_RESP);
+               _(NET_OPER_NAME_READ_REQ);
+               _(NET_OPER_NAME_READ_RESP);
+               _(NET_OLD_OPER_NAME_READ_REQ);
+               _(NET_OLD_OPER_NAME_READ_RESP);
+       }
+       return "NET_<UNKNOWN>";
+}
+
+const char *net_subblock_name(enum net_subblock value)
+{
+       switch (value) {
+               _(NET_REG_INFO_COMMON);
+               _(NET_MODEM_AVAIL_NETWORK_INFO_COMMON);
+               _(NET_OPERATOR_INFO_COMMON);
+               _(NET_RSSI_CURRENT);
+               _(NET_GSM_REG_INFO);
+               _(NET_DETAILED_NETWORK_INFO);
+               _(NET_GSM_OPERATOR_INFO);
+               _(NET_TIME_INFO);
+               _(NET_GSM_BAND_INFO);
+               _(NET_RAT_INFO);
+               _(NET_GSM_CELL_INFO);
+               _(NET_WCDMA_CELL_INFO);
+               _(NET_FULL_NITZ_NAME);
+               _(NET_SHORT_NITZ_NAME);
+               _(NET_REGISTRATION_CONF_INFO);
+               _(NET_ROAMING_CONF_INFO);
+               _(NET_REGISTRATION_CONF1_INFO);
+               _(NET_ROAMING_CONF1_INFO);
+               _(NET_AVAIL_NETWORK_INFO_COMMON);
+               _(NET_OPER_NAME_INFO);
+       }
+       return "NET_<UNKNOWN>";
+}
+
+const char *gss_message_id_name(enum gss_message_id value)
+{
+       switch (value) {
+               _(GSS_CS_SERVICE_REQ);
+               _(GSS_CS_SERVICE_RESP);
+               _(GSS_CS_SERVICE_FAIL_RESP);
+       }
+       return "GSS_<UNKNOWN>";
+}
+
+const char *gss_subblock_name(enum gss_subblock value)
+{
+       switch (value) {
+               _(GSS_RAT_INFO);
+       }
+       return "GSS_<UNKNOWN>";
+}
+
+const char *gpds_message_id_name(enum gpds_message_id value)
+{
+       switch (value) {
+               _(GPDS_LL_CONFIGURE_REQ);
+               _(GPDS_LL_CONFIGURE_RESP);
+               _(GPDS_CONTEXT_ID_CREATE_REQ);
+               _(GPDS_CONTEXT_ID_CREATE_RESP);
+               _(GPDS_CONTEXT_ID_CREATE_IND);
+               _(GPDS_CONTEXT_ID_DELETE_IND);
+               _(GPDS_CONTEXT_CONFIGURE_REQ);
+               _(GPDS_CONTEXT_CONFIGURE_RESP);
+               _(GPDS_CONTEXT_ACTIVATE_REQ);
+               _(GPDS_CONTEXT_ACTIVATE_RESP);
+               _(GPDS_CONTEXT_ACTIVATE_IND);
+               _(GPDS_CONTEXT_DEACTIVATE_REQ);
+               _(GPDS_CONTEXT_DEACTIVATE_RESP);
+               _(GPDS_CONTEXT_DEACTIVATE_IND);
+               _(GPDS_CONTEXT_MWI_ACT_REQUEST_IND);
+               _(GPDS_CONTEXT_NWI_ACT_REJECT_REQ);
+               _(GPDS_CONTEXT_NWI_ACT_REJECT_RESP);
+               _(GPDS_CONFIGURE_REQ);
+               _(GPDS_CONFIGURE_RESP);
+               _(GPDS_ATTACH_REQ);
+               _(GPDS_ATTACH_RESP);
+               _(GPDS_ATTACH_IND);
+               _(GPDS_DETACH_REQ);
+               _(GPDS_DETACH_RESP);
+               _(GPDS_DETACH_IND);
+               _(GPDS_STATUS_REQ);
+               _(GPDS_STATUS_RESP);
+               _(GPDS_SMS_PDU_SEND_REQ);
+               _(GPDS_SMS_PDU_SEND_RESP);
+               _(GPDS_SMS_PDU_RECEIVE_IND);
+               _(GPDS_TRANSFER_STATUS_IND);
+               _(GPDS_CONTEXT_ACTIVATE_FAIL_IND);
+               _(GPDS_LL_BIND_REQ);
+               _(GPDS_LL_BIND_RESP);
+               _(GPDS_CONTEXT_STATUS_REQ);
+               _(GPDS_CONTEXT_STATUS_RESP);
+               _(GPDS_CONTEXT_STATUS_IND);
+               _(GPDS_CONTEXT_ACTIVATING_IND);
+               _(GPDS_CONTEXT_MODIFY_REQ);
+               _(GPDS_CONTEXT_MODIFY_RESP);
+               _(GPDS_CONTEXT_MODIFY_IND);
+               _(GPDS_ATTACH_FAIL_IND);
+               _(GPDS_CONTEXT_DEACTIVATING_IND);
+               _(GPDS_CONFIGURATION_INFO_REQ);
+               _(GPDS_CONFIGURATION_INFO_RESP);
+               _(GPDS_CONFIGURATION_INFO_IND);
+               _(GPDS_CONTEXT_AUTH_REQ);
+               _(GPDS_CONTEXT_AUTH_RESP);
+               _(GPDS_TEST_MODE_REQ);
+               _(GPDS_TEST_MODE_RESP);
+               _(GPDS_RADIO_ACTIVITY_IND);
+               _(GPDS_FORCED_READY_STATE_REQ);
+               _(GPDS_FORCED_READY_STATE_RESP);
+               _(GPDS_CONTEXTS_CLEAR_REQ);
+               _(GPDS_CONTEXTS_CLEAR_RESP);
+               _(GPDS_MBMS_SERVICE_SELECTION_REQ);
+               _(GPDS_MBMS_SERVICE_SELECTION_RESP);
+               _(GPDS_MBMS_STATUS_IND);
+               _(GPDS_MBMS_CONTEXT_CREATE_REQ);
+               _(GPDS_MBMS_CONTEXT_CREATE_RESP);
+               _(GPDS_MBMS_CONTEXT_ACTIVATE_REQ);
+               _(GPDS_MBMS_CONTEXT_ACTIVATE_RESP);
+               _(GPDS_MBMS_CONTEXT_DELETE_REQ);
+               _(GPDS_MBMS_CONTEXT_DELETE_RESP);
+               _(GPDS_MBMS_CONTEXT_DELETE_IND);
+               _(GPDS_MBMS_SERVICE_SELECTION_IND);
+               _(GPDS_MBMS_SERVICE_AVAILABLE_IND);
+               _(GPDS_TEST_REQ);
+               _(GPDS_TEST_RESP);
+       }
+       return "GPSD_<UNKNOWN>";
+}
+
+const char *gpds_subblock_name(enum gpds_subblock value)
+{
+       switch (value) {
+               _(GPDS_COMP_INFO);
+               _(GPDS_QOS_REQ_INFO);
+               _(GPDS_QOS_MIN_INFO);
+               _(GPDS_QOS_NEG_INFO);
+               _(GPDS_PDP_ADDRESS_INFO);
+               _(GPDS_APN_INFO);
+               _(GPDS_QOS99_REQ_INFO);
+               _(GPDS_QOS99_MIN_INFO);
+               _(GPDS_QOS99_NEG_INFO);
+               _(GPDS_TFT_INFO);
+               _(GPDS_TFT_FILTER_INFO);
+               _(GPDS_USER_NAME_INFO);
+               _(GPDS_PASSWORD_INFO);
+               _(GPDS_PDNS_ADDRESS_INFO);
+               _(GPDS_SDNS_ADDRESS_INFO);
+               _(GPDS_CHALLENGE_INFO);
+               _(GPDS_DNS_ADDRESS_REQ_INFO);
+       }
+       return "GPDS_<UNKNOWN>";
+}
+
+const char *gpds_status_name(enum gpds_status value)
+{
+       switch (value) {
+               _(GPDS_ERROR);
+               _(GPDS_OK);
+               _(GPDS_FAIL);
+       }
+       return "GPDS_<UNKNOWN>";
+}
+
+const char *gpds_isi_cause_name(enum gpds_isi_cause value)
+{
+       switch (value) {
+               _(GPDS_CAUSE_UNKNOWN);
+               _(GPDS_CAUSE_IMSI);
+               _(GPDS_CAUSE_MS_ILLEGAL);
+               _(GPDS_CAUSE_ME_ILLEGAL);
+               _(GPDS_CAUSE_GPRS_NOT_ALLOWED);
+               _(GPDS_NOT_ALLOWED);
+               _(GPDS_CAUSE_MS_IDENTITY);
+               _(GPDS_CAUSE_DETACH);
+               _(GPDS_PLMN_NOT_ALLOWED);
+               _(GPDS_LA_NOT_ALLOWED);
+               _(GPDS_ROAMING_NOT_ALLOWED);
+               _(GPDS_CAUSE_GPRS_NOT_ALLOWED_IN_PLMN);
+               _(GPDS_CAUSE_MSC_NOT_REACH);
+               _(GPDS_CAUSE_PLMN_FAIL);
+               _(GPDS_CAUSE_NETWORK_CONGESTION);
+               _(GPDS_CAUSE_MBMS_BEARER_CAPABILITY_INSUFFICIENT);
+               _(GPDS_CAUSE_LLC_SNDCP_FAILURE);
+               _(GPDS_CAUSE_RESOURCE_INSUFF);
+               _(GPDS_CAUSE_APN);
+               _(GPDS_CAUSE_PDP_UNKNOWN);
+               _(GPDS_CAUSE_AUTHENTICATION);
+               _(GPDS_CAUSE_ACT_REJECT_GGSN);
+               _(GPDS_CAUSE_ACT_REJECT);
+               _(GPDS_CAUSE_SERV_OPT_NOT_SUPPORTED);
+               _(GPDS_CAUSE_SERV_OPT_NOT_SUBSCRIBED);
+               _(GPDS_CAUSE_SERV_OPT_OUT_OF_ORDER);
+               _(GPDS_CAUSE_NSAPI_ALREADY_USED);
+               _(GPDS_CAUSE_DEACT_REGULAR);
+               _(GPDS_CAUSE_QOS);
+               _(GPDS_CAUSE_NETWORK_FAIL);
+               _(GPDS_CAUSE_REACTIVATION_REQ);
+               _(GPDS_CAUSE_FEAT_NOT_SUPPORTED);
+               _(GPDS_CAUSE_TFT_SEMANTIC_ERROR);
+               _(GPDS_CAUSE_TFT_SYNTAX_ERROR);
+               _(GPDS_CAUSE_CONTEXT_UNKNOWN);
+               _(GPDS_CAUSE_FILTER_SEMANTIC_ERROR);
+               _(GPDS_CAUSE_FILTER_SYNTAX_ERROR);
+               _(GPDS_CAUSE_CONT_WITHOUT_TFT);
+               _(GPDS_CAUSE_MULTICAST_MEMBERSHIP_TIMEOUT);
+               _(GPDS_CAUSE_INVALID_MANDATORY_INFO);
+               _(GPDS_CAUSE_MSG_TYPE_NON_EXISTENTOR_NOT_IMPLTD);
+               _(GPDS_CAUSE_MSG_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
+               _(GPDS_CAUSE_IE_NON_EXISTENT_OR_NOT_IMPLEMENTED);
+               _(GPDS_CAUSE_CONDITIONAL_IE_ERROR);
+               _(GPDS_CUASEMSG_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
+               _(GPDS_CAUSE_UNSPECIFIED);
+               _(GPDS_CAUSE_APN_INCOMPATIBLE_WITH_CURR_CTXT);
+               _(GPDS_CAUSE_FDN);
+               _(GPDS_CAUSE_USER_ABORT);
+               _(GPDS_CAUSE_CS_INACTIVE);
+               _(GPDS_CAUSE_CSD_OVERRIDE);
+               _(GPDS_CAUSE_APN_CONTROL);
+               _(GPDS_CAUSE_CALL_CONTROL);
+               _(GPDS_CAUSE_TEMPERATURE_LIMIT);
+               _(GPDS_CAUSE_RETRY_COUNTER_EXPIRED);
+               _(GPDS_CAUSE_NO_CONNECTION);
+               _(GPDS_CAUSE_DETACHED);
+               _(GPDS_CAUSE_NO_SERVICE_POWER_SAVE);
+               _(GPDS_CAUSE_SIM_REMOVED);
+               _(GPDS_CAUSE_POWER_OFF);
+               _(GPDS_CAUSE_LAI_FORBIDDEN_NATIONAL_ROAM_LIST);
+               _(GPDS_CAUSE_LAI_FORBIDDEN_REG_PROVISION_LIST);
+               _(GPDS_CAUSE_ACCESS_BARRED);
+               _(GPDS_CAUSE_FATAL_FAILURE);
+               _(GPDS_CAUSE_AUT_FAILURE);
+       }
+       return "GPDS_<UNKNOWN>";
+}
+
+const char *gpds_transfer_status_name(enum gpds_transfer_status value)
+{
+       switch (value) {
+               _(GPDS_TRANSFER_NOT_AVAIL);
+               _(GPDS_TRANSFER_AVAIL);
+       }
+       return "GPDS_<UNKNOWN>";
+}
+
+const char *gpds_transfer_cause_name(enum gpds_transfer_cause value)
+{
+       switch (value) {
+               _(GPDS_TRANSFER_CAUSE_ATTACHED);
+               _(GPDS_TRANSFER_CAUSE_DETACHED);
+               _(GPDS_TRANSFER_CAUSE_RESUMED);
+               _(GPDS_TRANSFER_CAUSE_SUSPENDED_NO_COVERAGE);
+               _(GPDS_TRANSFER_CAUSE_SUSPENDED_CALL_SMS);
+               _(GPDS_TRANSFER_CAUSE_SUSPENDED_CALL);
+               _(GPDS_TRANSFER_CAUSE_SUSPENDED_RAU);
+               _(GPDS_TRANSFER_CAUSE_SUSPENDED_LU);
+               _(GPDS_TRANSFER_CAUSE_DSAC_RESTRICTION);
+       }
+       return "GPDS_<UNKNOWN>";
+}
+
+#undef _
+
+static void hex_dump(const char *resname, uint8_t res, const char *name,
+                       uint8_t id, uint8_t utid, const uint8_t m[], size_t len)
+{
+       char hex[3 * 16 + 1];
+       char ascii[16 + 1];
+       size_t i, j, k;
+
+       ofono_debug("%s (0x%02X): %s [id=0x%02X utid=0x%02X len=%zu]:",
+                       resname, res, name, id, utid, len);
+
+       strcpy(hex, ""), j = 0;
+       strcpy(ascii, "."), k = 1;
+
+       for (i = 0; i < len; i++) {
+               sprintf(hex + j, " %02X", m[i]), j += 3;
+               ascii[k++] = g_ascii_isgraph(m[i]) ? m[i] : '.';
+
+               if ((j & 48) == 48) {
+                       ofono_debug("    *%-48s : %.*s", hex, (int) k, ascii);
+                       j = 0, k = 0;
+               }
+       }
+
+       if (j)
+               ofono_debug("    *%-48s : %.*s", hex, (int) k, ascii);
+}
+
+static const char *res_to_name(uint8_t res, uint8_t id)
+{
+       if (id == COMMON_MESSAGE)
+               return "COMMON_MESSAGE";
+
+       switch (res) {
+       case PN_MODEM_NETWORK:
+       case PN_NETWORK:
+               return net_message_id_name(id);
+       case PN_PHONE_INFO:
+       case PN_MODEM_INFO:
+       case PN_EPOC_INFO:
+               return info_message_id_name(id);
+       case PN_SS:
+               return ss_message_id_name(id);
+       case PN_MODEM_CALL:
+       case PN_CALL:
+               return call_message_id_name(id);
+       case PN_SECURITY:
+               return sec_message_id_name(id);
+       case PN_SMS:
+               return sms_message_id_name(id);
+       case PN_SIM:
+               return sim_message_id_name(id);
+       case PN_MTC:
+               return mtc_message_id_name(id);
+       case PN_GSS:
+               return gss_message_id_name(id);
+       case PN_GPDS:
+               return gpds_message_id_name(id);
+       case PN_UICC:
+               return uicc_message_id_name(id);
+       }
+       return "UNKNOWN";
+}
+
+void isi_trace(const GIsiMessage *msg, void *data)
+{
+       uint8_t id = g_isi_msg_id(msg);
+       uint8_t res = g_isi_msg_resource(msg);
+       const char *resname = pn_resource_name(res);
+       const char *name = res_to_name(res, id);
+       uint8_t const *dump = g_isi_msg_data(msg);
+
+       hex_dump(resname, res, name, id, g_isi_msg_utid(msg),
+                       dump - 2, g_isi_msg_data_len(msg) + 2);
+}
diff --git a/drivers/isimodem/debug.h b/drivers/isimodem/debug.h
new file mode 100644 (file)
index 0000000..1586abf
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_DEBUG_H
+#define __ISIMODEM_DEBUG_H
+
+#include <gisi/message.h>
+
+#include "ss.h"
+#include "mtc.h"
+#include "sms.h"
+#include "uicc.h"
+#include "sim.h"
+#include "info.h"
+#include "call.h"
+#include "network.h"
+#include "gss.h"
+#include "gpds.h"
+
+const char *ss_message_id_name(enum ss_message_id value);
+const char *ss_subblock_name(enum ss_subblock value);
+const char *ss_ussd_type_name(enum ss_ussd_type value);
+
+const char *mtc_isi_cause_name(enum mtc_isi_cause value);
+const char *mtc_message_id_name(enum mtc_message_id value);
+const char *mtc_modem_state_name(enum mtc_modem_state value);
+const char *mce_rf_state_name(enum mce_rf_state value);
+
+const char *mce_message_id_name(enum mce_message_id value);
+const char *mce_modem_state_name(enum mce_modem_state value);
+const char *mce_status_info(enum mce_status_info value);
+
+const char *uicc_message_id_name(enum uicc_message_id value);
+const char *uicc_subblock_name(uint8_t value);
+const char *uicc_service_type_name(uint8_t value);
+const char *uicc_status_name(uint8_t value);
+const char *uicc_details_name(uint8_t value);
+
+const char *sms_gsm_cause_name(enum sms_gsm_cause value);
+const char *sms_isi_cause_name(enum sms_isi_cause value);
+const char *sms_message_id_name(enum sms_message_id value);
+const char *sms_subblock_name(enum sms_subblock value);
+
+const char *sim_isi_cause_name(enum sim_isi_cause value);
+const char *sim_message_id_name(enum sim_message_id value);
+const char *sim_subblock_name(enum sim_subblock value);
+
+enum ofono_sim_password_type;
+
+const char *sim_password_name(enum ofono_sim_password_type value);
+
+const char *sec_message_id_name(enum sec_message_id value);
+
+const char *info_isi_cause_name(enum info_isi_cause value);
+const char *info_message_id_name(enum info_message_id value);
+const char *info_subblock_name(enum info_subblock value);
+
+const char *call_gsm_cause_name(enum call_gsm_cause value);
+const char *call_isi_cause_name(enum call_isi_cause value);
+const char *call_status_name(enum call_status value);
+const char *call_message_id_name(enum call_message_id value);
+
+const char *net_gsm_cause_name(enum net_gsm_cause value);
+const char *net_isi_cause_name(enum net_isi_cause value);
+const char *net_status_name(enum net_reg_status value);
+const char *net_message_id_name(enum net_message_id value);
+const char *net_subblock_name(enum net_subblock value);
+
+const char *gss_message_id_name(enum gss_message_id value);
+const char *gss_subblock_name(enum gss_subblock value);
+
+const char *gpds_message_id_name(enum gpds_message_id value);
+const char *gpds_subblock_name(enum gpds_subblock value);
+const char *gpds_status_name(enum gpds_status value);
+const char *gpds_isi_cause_name(enum gpds_isi_cause value);
+const char *gpds_transfer_status_name(enum gpds_transfer_status value);
+const char *gpds_transfer_cause_name(enum gpds_transfer_cause value);
+
+void isi_trace(const GIsiMessage *msg, void *data);
+
+const char *pn_resource_name(int value);
+
+#endif /* __ISIMODEM_DEBUG_H */
diff --git a/drivers/isimodem/devinfo.c b/drivers/isimodem/devinfo.c
new file mode 100644 (file)
index 0000000..3a59b35
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/message.h>
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "debug.h"
+#include "info.h"
+
+struct devinfo_data {
+       GIsiClient *client;
+};
+
+static void info_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_devinfo_query_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+       uint8_t msgid;
+       uint8_t status;
+
+       msgid = g_isi_msg_id(msg);
+       if (msgid != INFO_PRODUCT_INFO_READ_RESP &&
+                       msgid != INFO_VERSION_READ_RESP &&
+                       msgid != INFO_SERIAL_NUMBER_READ_RESP)
+               goto error;
+
+       if (g_isi_msg_error(msg) < 0)
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &status))
+               goto error;
+
+       if (status != INFO_OK)
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               uint8_t id = g_isi_sb_iter_get_id(&iter);
+               uint8_t chars;
+               char *info = NULL;
+
+               if (id != INFO_SB_PRODUCT_INFO_MANUFACTURER &&
+                               id != INFO_SB_PRODUCT_INFO_NAME &&
+                               id != INFO_SB_MCUSW_VERSION &&
+                               id != INFO_SB_SN_IMEI_PLAIN)
+                       continue;
+
+               if (g_isi_sb_iter_get_len(&iter) < 5)
+                       goto error;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &chars, 3))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_latin_tag(&iter, &info, chars, 4))
+                       goto error;
+
+               CALLBACK_WITH_SUCCESS(cb, info, cbd->data);
+
+               g_free(info);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", cbd->data);
+}
+
+static void isi_query_manufacturer(struct ofono_devinfo *info,
+                                       ofono_devinfo_query_cb_t cb,
+                                       void *data)
+{
+       struct devinfo_data *dev = ofono_devinfo_get_data(info);
+       struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data);
+
+       const uint8_t msg[] = {
+               INFO_PRODUCT_INFO_READ_REQ,
+               INFO_PRODUCT_MANUFACTURER
+       };
+       size_t len = sizeof(msg);
+
+       if (cbd == NULL || dev == NULL)
+               goto error;
+
+       if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", data);
+       g_free(cbd);
+}
+
+static void isi_query_model(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb,
+                               void *data)
+{
+       struct devinfo_data *dev = ofono_devinfo_get_data(info);
+       struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data);
+
+       const uint8_t msg[] = {
+               INFO_PRODUCT_INFO_READ_REQ,
+               INFO_PRODUCT_NAME
+       };
+       size_t len = sizeof(msg);
+
+       if (cbd == NULL || dev == NULL)
+               goto error;
+
+       if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", data);
+       g_free(cbd);
+}
+
+static void isi_query_revision(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb,
+                               void *data)
+{
+       struct devinfo_data *dev = ofono_devinfo_get_data(info);
+       struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data);
+
+       const uint8_t msg[] = {
+               INFO_VERSION_READ_REQ,
+               0x00, INFO_MCUSW,
+               0x00, 0x00, 0x00, 0x00
+       };
+       size_t len = sizeof(msg);
+
+       if (cbd == NULL || dev == NULL)
+               goto error;
+
+       if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", data);
+       g_free(cbd);
+}
+
+static void isi_query_serial(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb,
+                               void *data)
+{
+       struct devinfo_data *dev = ofono_devinfo_get_data(info);
+       struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data);
+
+       const uint8_t msg[] = {
+               INFO_SERIAL_NUMBER_READ_REQ,
+               INFO_SN_IMEI_PLAIN
+       };
+       size_t len = sizeof(msg);
+
+       if (cbd == NULL || dev == NULL)
+               goto error;
+
+       if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", data);
+       g_free(cbd);
+}
+
+static void reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_devinfo *info = data;
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_devinfo_remove(info);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       ofono_devinfo_register(info);
+}
+
+static int isi_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *idx = user;
+       struct devinfo_data *data = g_try_new0(struct devinfo_data, 1);
+
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->client = g_isi_client_create(idx, PN_PHONE_INFO);
+       if (data->client == NULL) {
+               g_free(data);
+               return -ENOMEM;
+       }
+
+
+       ofono_devinfo_set_data(info, data);
+
+       g_isi_client_set_timeout(data->client, INFO_TIMEOUT);
+       g_isi_client_verify(data->client, reachable_cb, info, NULL);
+
+       return 0;
+}
+
+static void isi_devinfo_remove(struct ofono_devinfo *info)
+{
+       struct devinfo_data *data = ofono_devinfo_get_data(info);
+
+       ofono_devinfo_set_data(info, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_devinfo_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_devinfo_probe,
+       .remove                 = isi_devinfo_remove,
+       .query_manufacturer     = isi_query_manufacturer,
+       .query_model            = isi_query_model,
+       .query_revision         = isi_query_revision,
+       .query_serial           = isi_query_serial
+};
+
+void isi_devinfo_init(void)
+{
+       ofono_devinfo_driver_register(&driver);
+}
+
+void isi_devinfo_exit(void)
+{
+       ofono_devinfo_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/gpds.h b/drivers/isimodem/gpds.h
new file mode 100644 (file)
index 0000000..ce34ddc
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_GPDS_H
+#define __ISIMODEM_GPDS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GPDS_MAX_CONTEXT_COUNT                 11
+#define GPDS_TIMEOUT                           3
+#define GPDS_CTX_ACTIVATE_TIMEOUT              (6 * 30)        /* 6 * T3380 */
+#define GPDS_CTX_DEACTIVATE_TIMEOUT            (6 * 8)         /* 6 * T3390 */
+#define GPDS_ATTACH_TIMEOUT                    (6 * 15)        /* 6 * T3310 */
+#define GPDS_DETACH_TIMEOUT                    (6 * 15)        /* 6 * T3321 */
+
+#define GPDS_MAX_APN_STRING_LENGTH             100
+#define GPDS_MAX_USERNAME_LENGTH               53
+#define GPDS_MAX_PASSWORD_LENGTH               53
+
+#define PN_GPDS                                        0x31
+#define PN_PEP_TYPE_GPRS                       0x04
+#define PN_WRAN                                        0xB4
+
+enum gpds_message_id {
+       GPDS_LL_CONFIGURE_REQ =                 0x00,
+       GPDS_LL_CONFIGURE_RESP =                0x01,
+       GPDS_CONTEXT_ID_CREATE_REQ =            0x02,
+       GPDS_CONTEXT_ID_CREATE_RESP =           0x03,
+       GPDS_CONTEXT_ID_CREATE_IND =            0x04,
+       GPDS_CONTEXT_ID_DELETE_IND =            0x05,
+       GPDS_CONTEXT_CONFIGURE_REQ =            0x06,
+       GPDS_CONTEXT_CONFIGURE_RESP =           0x07,
+       GPDS_CONTEXT_ACTIVATE_REQ =             0x08,
+       GPDS_CONTEXT_ACTIVATE_RESP =            0x09,
+       GPDS_CONTEXT_ACTIVATE_IND =             0x0A,
+       GPDS_CONTEXT_DEACTIVATE_REQ =           0x0B,
+       GPDS_CONTEXT_DEACTIVATE_RESP =          0x0C,
+       GPDS_CONTEXT_DEACTIVATE_IND =           0x0D,
+       GPDS_CONTEXT_MWI_ACT_REQUEST_IND =      0x0E,
+       GPDS_CONTEXT_NWI_ACT_REJECT_REQ =       0x0F,
+       GPDS_CONTEXT_NWI_ACT_REJECT_RESP =      0x10,
+       GPDS_CONFIGURE_REQ =                    0x11,
+       GPDS_CONFIGURE_RESP =                   0x12,
+       GPDS_ATTACH_REQ =                       0x13,
+       GPDS_ATTACH_RESP =                      0x14,
+       GPDS_ATTACH_IND =                       0x15,
+       GPDS_DETACH_REQ =                       0x16,
+       GPDS_DETACH_RESP =                      0x17,
+       GPDS_DETACH_IND =                       0x18,
+       GPDS_STATUS_REQ =                       0x19,
+       GPDS_STATUS_RESP =                      0x1A,
+       GPDS_SMS_PDU_SEND_REQ =                 0x1B,
+       GPDS_SMS_PDU_SEND_RESP =                0x1C,
+       GPDS_SMS_PDU_RECEIVE_IND =              0x1D,
+       GPDS_TRANSFER_STATUS_IND =              0x1E,
+       GPDS_CONTEXT_ACTIVATE_FAIL_IND =        0x1F,
+       GPDS_LL_BIND_REQ =                      0x20,
+       GPDS_LL_BIND_RESP =                     0x21,
+       GPDS_CONTEXT_STATUS_REQ =               0x22,
+       GPDS_CONTEXT_STATUS_RESP =              0x23,
+       GPDS_CONTEXT_STATUS_IND =               0x24,
+       GPDS_CONTEXT_ACTIVATING_IND =           0x25,
+       GPDS_CONTEXT_MODIFY_REQ =               0x2A,
+       GPDS_CONTEXT_MODIFY_RESP =              0x2B,
+       GPDS_CONTEXT_MODIFY_IND =               0x2C,
+       GPDS_ATTACH_FAIL_IND =                  0x2D,
+       GPDS_CONTEXT_DEACTIVATING_IND =         0x2F,
+       GPDS_CONFIGURATION_INFO_REQ =           0x30,
+       GPDS_CONFIGURATION_INFO_RESP =          0x31,
+       GPDS_CONFIGURATION_INFO_IND =           0x32,
+       GPDS_CONTEXT_AUTH_REQ =                 0x33,
+       GPDS_CONTEXT_AUTH_RESP =                0x34,
+       GPDS_TEST_MODE_REQ =                    0x35,
+       GPDS_TEST_MODE_RESP =                   0x36,
+       GPDS_RADIO_ACTIVITY_IND =               0x37,
+       GPDS_FORCED_READY_STATE_REQ =           0x38,
+       GPDS_FORCED_READY_STATE_RESP =          0x39,
+       GPDS_CONTEXTS_CLEAR_REQ =               0x3A,
+       GPDS_CONTEXTS_CLEAR_RESP =              0x3B,
+       GPDS_MBMS_SERVICE_SELECTION_REQ =       0x3C,
+       GPDS_MBMS_SERVICE_SELECTION_RESP =      0x3D,
+       GPDS_MBMS_STATUS_IND =                  0x3E,
+       GPDS_MBMS_CONTEXT_CREATE_REQ =          0x3F,
+       GPDS_MBMS_CONTEXT_CREATE_RESP =         0x40,
+       GPDS_MBMS_CONTEXT_ACTIVATE_REQ =        0x41,
+       GPDS_MBMS_CONTEXT_ACTIVATE_RESP =       0x42,
+       GPDS_MBMS_CONTEXT_DELETE_REQ =          0x43,
+       GPDS_MBMS_CONTEXT_DELETE_RESP =         0x44,
+       GPDS_MBMS_CONTEXT_DELETE_IND =          0x45,
+       GPDS_MBMS_SERVICE_SELECTION_IND =       0x46,
+       GPDS_MBMS_SERVICE_AVAILABLE_IND =       0x47,
+       GPDS_TEST_REQ =                         0x48,
+       GPDS_TEST_RESP =                        0x49
+};
+
+enum gpds_subblock {
+       GPDS_COMP_INFO =                        0x00,
+       GPDS_QOS_REQ_INFO =                     0x01,
+       GPDS_QOS_MIN_INFO =                     0x02,
+       GPDS_QOS_NEG_INFO =                     0x03,
+       GPDS_PDP_ADDRESS_INFO =                 0x04,
+       GPDS_APN_INFO =                         0x05,
+       GPDS_QOS99_REQ_INFO =                   0x06,
+       GPDS_QOS99_MIN_INFO =                   0x07,
+       GPDS_QOS99_NEG_INFO =                   0x08,
+       GPDS_TFT_INFO =                         0x09,
+       GPDS_TFT_FILTER_INFO =                  0x0A,
+       GPDS_USER_NAME_INFO =                   0x0B,
+       GPDS_PASSWORD_INFO =                    0x0C,
+       GPDS_PDNS_ADDRESS_INFO =                0x0D,
+       GPDS_SDNS_ADDRESS_INFO =                0x0E,
+       GPDS_CHALLENGE_INFO =                   0x0F,
+       GPDS_DNS_ADDRESS_REQ_INFO =             0x90,
+};
+
+enum gpds_status {
+       GPDS_ERROR =                            0x00,
+       GPDS_OK =                               0x01,
+       GPDS_FAIL =                             0x02
+};
+
+enum gpds_isi_cause {
+       GPDS_CAUSE_UNKNOWN =                    0x00,
+       GPDS_CAUSE_IMSI =                       0x02,
+       GPDS_CAUSE_MS_ILLEGAL =                 0x03,
+       GPDS_CAUSE_ME_ILLEGAL =                 0x06,
+       GPDS_CAUSE_GPRS_NOT_ALLOWED =           0x07,
+       GPDS_NOT_ALLOWED =                      0x08,
+       GPDS_CAUSE_MS_IDENTITY =                0x09,
+       GPDS_CAUSE_DETACH =                     0x0A,
+       GPDS_PLMN_NOT_ALLOWED =                 0x0B,
+       GPDS_LA_NOT_ALLOWED =                   0x0C,
+       GPDS_ROAMING_NOT_ALLOWED =              0x0D,
+       GPDS_CAUSE_GPRS_NOT_ALLOWED_IN_PLMN =   0x0E,
+       GPDS_CAUSE_MSC_NOT_REACH =              0x10,
+       GPDS_CAUSE_PLMN_FAIL =                  0x11,
+       GPDS_CAUSE_NETWORK_CONGESTION =         0x16,
+       GPDS_CAUSE_MBMS_BEARER_CAPABILITY_INSUFFICIENT = 0x18,
+       GPDS_CAUSE_LLC_SNDCP_FAILURE =          0x19,
+       GPDS_CAUSE_RESOURCE_INSUFF =            0x1A,
+       GPDS_CAUSE_APN =                        0x1B,
+       GPDS_CAUSE_PDP_UNKNOWN =                0x1C,
+       GPDS_CAUSE_AUTHENTICATION =             0x1D,
+       GPDS_CAUSE_ACT_REJECT_GGSN =            0x1E,
+       GPDS_CAUSE_ACT_REJECT =                 0x1F,
+       GPDS_CAUSE_SERV_OPT_NOT_SUPPORTED =     0x20,
+       GPDS_CAUSE_SERV_OPT_NOT_SUBSCRIBED =    0x21,
+       GPDS_CAUSE_SERV_OPT_OUT_OF_ORDER =      0x22,
+       GPDS_CAUSE_NSAPI_ALREADY_USED =         0x23,
+       GPDS_CAUSE_DEACT_REGULAR =              0x24,
+       GPDS_CAUSE_QOS =                        0x25,
+       GPDS_CAUSE_NETWORK_FAIL =               0x26,
+       GPDS_CAUSE_REACTIVATION_REQ =           0x27,
+       GPDS_CAUSE_FEAT_NOT_SUPPORTED =         0x28,
+       GPDS_CAUSE_TFT_SEMANTIC_ERROR =         0x29,
+       GPDS_CAUSE_TFT_SYNTAX_ERROR =           0x2A,
+       GPDS_CAUSE_CONTEXT_UNKNOWN =            0x2B,
+       GPDS_CAUSE_FILTER_SEMANTIC_ERROR =      0x2C,
+       GPDS_CAUSE_FILTER_SYNTAX_ERROR =        0x2D,
+       GPDS_CAUSE_CONT_WITHOUT_TFT =           0x2E,
+       GPDS_CAUSE_MULTICAST_MEMBERSHIP_TIMEOUT = 0x2F,
+       GPDS_CAUSE_INVALID_MANDATORY_INFO =     0x60,
+       GPDS_CAUSE_MSG_TYPE_NON_EXISTENTOR_NOT_IMPLTD = 0x61,
+       GPDS_CAUSE_MSG_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 0x62,
+       GPDS_CAUSE_IE_NON_EXISTENT_OR_NOT_IMPLEMENTED = 0x63,
+       GPDS_CAUSE_CONDITIONAL_IE_ERROR =       0x64,
+       GPDS_CUASEMSG_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 0x65,
+       GPDS_CAUSE_UNSPECIFIED =                0x6F,
+       GPDS_CAUSE_APN_INCOMPATIBLE_WITH_CURR_CTXT = 0x70,
+       GPDS_CAUSE_FDN =                        0xA0,
+       GPDS_CAUSE_USER_ABORT =                 0xA1,
+       GPDS_CAUSE_CS_INACTIVE =                0xA2,
+       GPDS_CAUSE_CSD_OVERRIDE =               0xA3,
+       GPDS_CAUSE_APN_CONTROL =                0xA4,
+       GPDS_CAUSE_CALL_CONTROL =               0xA5,
+       GPDS_CAUSE_TEMPERATURE_LIMIT =          0xA6,
+       GPDS_CAUSE_RETRY_COUNTER_EXPIRED =      0xC8,
+       GPDS_CAUSE_NO_CONNECTION =              0xC9,
+       GPDS_CAUSE_DETACHED =                   0xF5,
+       GPDS_CAUSE_NO_SERVICE_POWER_SAVE =      0xF7,
+       GPDS_CAUSE_SIM_REMOVED =                0xF9,
+       GPDS_CAUSE_POWER_OFF =                  0xFA,
+       GPDS_CAUSE_LAI_FORBIDDEN_NATIONAL_ROAM_LIST = 0xFB,
+       GPDS_CAUSE_LAI_FORBIDDEN_REG_PROVISION_LIST = 0xFC,
+       GPDS_CAUSE_ACCESS_BARRED =              0xFD,
+       GPDS_CAUSE_FATAL_FAILURE =              0xFE,
+       GPDS_CAUSE_AUT_FAILURE =                0xFF,
+};
+
+enum gpds_transfer_status {
+       GPDS_TRANSFER_NOT_AVAIL =               0x00,
+       GPDS_TRANSFER_AVAIL =                   0x01,
+};
+
+enum gpds_transfer_cause {
+       GPDS_TRANSFER_CAUSE_ATTACHED =                  0x02,
+       GPDS_TRANSFER_CAUSE_DETACHED =                  0x03,
+       GPDS_TRANSFER_CAUSE_RESUMED =                   0x04,
+       GPDS_TRANSFER_CAUSE_SUSPENDED_NO_COVERAGE =     0x05,
+       GPDS_TRANSFER_CAUSE_SUSPENDED_CALL_SMS =        0x07,
+       GPDS_TRANSFER_CAUSE_SUSPENDED_CALL =            0x08,
+       GPDS_TRANSFER_CAUSE_SUSPENDED_RAU =             0x09,
+       GPDS_TRANSFER_CAUSE_SUSPENDED_LU =              0x0A,
+       GPDS_TRANSFER_CAUSE_DSAC_RESTRICTION =          0x0B,
+};
+
+enum gpds_context_type {
+       GPDS_CONT_TYPE_NORMAL =                 0x00,
+       GPDS_CONT_TYPE_NWI =                    0x01,
+       GPDS_CONT_TYPE_SEC =                    0x02
+};
+
+enum gpds_ppp_mode {
+       GPDS_LL_FRAMED_PPP =                    0x00,
+       GPDS_LL_NONFRAMED_PPP =                 0x01,
+       GPDS_LL_PLAIN =                         0x02
+};
+
+enum gpds_pdp_type {
+       GPDS_PDP_TYPE_PPP =                     0x01,
+       GPDS_PDP_TYPE_IPV4 =                    0x21,
+       GPDS_PDP_TYPE_IPV6 =                    0x57,
+       GPDS_PDP_TYPE_DEFAULT =                 0xFF
+};
+
+enum gpds_request_mode {
+       GPDS_FOLLOW_OFF =                       0x00,
+       GPDS_FOLLOW_ON =                        0x01
+};
+
+enum gpds_attach_status {
+       GPDS_DETACHED =                         0x00,
+       GPDS_ATTACHED =                         0x01
+};
+
+enum gpds_attach_mode {
+       GPDS_ATTACH_MODE_MANUAL =               0x00,
+       GPDS_ATTACH_MODE_AUTOMATIC =            0x01,
+       GPDS_ATTACH_MODE_DEFAULT =              0xFF
+};
+
+enum gpds_mt_act_mode {
+       GPDS_MT_ACT_MODE_REJECT =               0x00,
+       GPDS_MT_ACT_MODE_ACCEPT =               0x01,
+       GPDS_MT_ACT_MODE_DEFAULT =              0xFF
+};
+
+enum gpds_classc_mode {
+       GPDS_CLASSC_MODE_GPRS =                 0x00,
+       GPDS_CLASSC_MODE_GSM =                  0x01,
+       GPDS_CLASSC_MODE_DEFAULT =              0xFF
+};
+
+enum gpds_aol_context {
+       GPDS_AOL_CTX_NOT_ACTIVE =               0x00,
+       GPDS_AOL_CTX_HPLMN_ACTIVE =             0x01,
+       GPDS_AOL_CTX_VPLMN_ACTIVE =             0x02,
+       GPDS_AOL_CTX_ACTIVE =                   0x03,
+       GPDS_AOL_CTX_DEFAULT =                  0xFF
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !__ISIMODEM_GPDS_H */
diff --git a/drivers/isimodem/gprs-context.c b/drivers/isimodem/gprs-context.c
new file mode 100644 (file)
index 0000000..ce53d02
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <search.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+#include <gisi/client.h>
+#include <gisi/iter.h>
+#include <gisi/pep.h>
+#include <gisi/pipe.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "gpds.h"
+#include "debug.h"
+
+#define STATIC_IP_NETMASK "255.255.255.255"
+
+#define INVALID_ID (0xff)
+# if (INVALID_ID < GPDS_MAX_CONTEXT_COUNT)
+#   error Uho! This should not happen!
+#endif
+
+struct context_data {
+       GIsiClient *client;
+       GIsiModem *idx;
+       uint16_t gpds;  /* GPDS object handle */
+       unsigned cid;   /* oFono core context ID */
+       struct ofono_gprs_context *context;
+       ofono_gprs_context_cb_t cb;
+       void *data;
+
+       GIsiPEP *pep;
+       GIsiPipe *pipe;
+       guint reset;
+
+       char apn[GPDS_MAX_APN_STRING_LENGTH + 1];
+       char username[GPDS_MAX_USERNAME_LENGTH + 1];
+       char password[GPDS_MAX_PASSWORD_LENGTH + 1];
+
+       uint8_t handle; /* GPDS context ID */
+       uint8_t type;
+};
+
+static gboolean client_reset(gpointer data)
+{
+       struct context_data *cd = data;
+
+       g_isi_client_reset(cd->client);
+       cd->reset = 0;
+
+       return FALSE;
+}
+
+static void reset_context(struct context_data *cd)
+{
+       if (cd == NULL)
+               return;
+
+       if (cd->pipe)
+               g_isi_pipe_destroy(cd->pipe);
+
+       if (cd->pep)
+               g_isi_pep_destroy(cd->pep);
+
+       cd->pep = NULL;
+       cd->pipe = NULL;
+       cd->handle = INVALID_ID;
+
+       cd->reset = g_idle_add(client_reset, cd);
+}
+
+typedef void (*ContextFailFunc)(struct context_data *cd);
+
+static void gprs_up_fail(struct context_data *cd)
+{
+       reset_context(cd);
+       CALLBACK_WITH_FAILURE(cd->cb, cd->data);
+}
+
+static void gprs_down_fail(struct context_data *cd)
+{
+       reset_context(cd);
+       CALLBACK_WITH_FAILURE(cd->cb, cd->data);
+}
+
+static gboolean check_resp(const GIsiMessage *msg, uint8_t id, size_t minlen,
+                               struct context_data *cd,
+                               ContextFailFunc fail_cb)
+{
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("ISI message error: %d", g_isi_msg_error(msg));
+               goto error;
+       }
+
+       if (g_isi_msg_id(msg) != id)
+               return FALSE;
+
+       if (g_isi_msg_data_len(msg) < minlen) {
+               DBG("truncated message");
+               goto error;
+       }
+
+       if (cd->handle != INVALID_ID && data[0] != cd->handle)
+               return FALSE;
+
+       if (data[1] != GPDS_OK) {
+               DBG("context error: %s (0x%02"PRIx8")",
+                       gpds_status_name(data[1]), data[1]);
+
+               if (minlen > 2)
+                       DBG("  fail cause: %s (0x%02"PRIx8")",
+                               gpds_isi_cause_name(data[2]), data[2]);
+
+               goto error;
+       }
+
+       return TRUE;
+
+error:
+       if (fail_cb)
+               fail_cb(cd);
+
+       return FALSE;
+}
+
+static gboolean check_ind(const GIsiMessage *msg, size_t minlen,
+                               struct context_data *cd)
+
+{
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("ISI message error: %d", g_isi_msg_error(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_data_len(msg) < minlen) {
+               DBG("truncated message");
+               return FALSE;
+       }
+
+       if (cd->handle != INVALID_ID && data[0] != cd->handle)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void deactivate_ind_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct context_data *cd = opaque;
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (!check_ind(msg, 2, cd))
+               return;
+
+       DBG("context deactivated: %s (0x%02"PRIx8")",
+               gpds_isi_cause_name(data[2]), data[2]);
+
+       ofono_gprs_context_deactivated(cd->context, cd->cid);
+       reset_context(cd);
+}
+
+static void activate_ind_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct context_data *cd = opaque;
+       GIsiSubBlockIter iter;
+       const char *dns[5];
+       int dns_count = 0;
+
+       char ifname[IF_NAMESIZE];
+       char *ip_addr = NULL;
+       char *ipv6_addr = NULL;
+
+       if (!check_ind(msg, 2, cd))
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               uint8_t *addr_value = NULL;
+               uint8_t addr_len = 0;
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+
+               case GPDS_PDP_ADDRESS_INFO:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value,
+                                                       4))
+                               goto error;
+
+                       if (addr_len == 4) {
+                               ip_addr = alloca(INET_ADDRSTRLEN);
+                               inet_ntop(AF_INET, (const void *)addr_value,
+                                               ip_addr, INET_ADDRSTRLEN);
+                       } else if (addr_len == 16) {
+                               ipv6_addr = alloca(INET6_ADDRSTRLEN);
+                               inet_ntop(AF_INET6, (const void *)addr_value,
+                                               ipv6_addr, INET6_ADDRSTRLEN);
+                       }
+                       break;
+
+               case GPDS_PDNS_ADDRESS_INFO:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3))
+                               break;
+
+                       if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value,
+                                                       4))
+                               break;
+
+                       if (addr_len == 4) {
+                               char *addr = alloca(INET_ADDRSTRLEN);
+                               inet_ntop(AF_INET, (const void *)addr_value,
+                                               addr, INET_ADDRSTRLEN);
+                               dns[dns_count++] = addr;
+                       } else if (addr_len == 16) {
+                               char *addr = alloca(INET6_ADDRSTRLEN);
+                               inet_ntop(AF_INET6, (const void *)addr_value,
+                                               addr, INET6_ADDRSTRLEN);
+                               dns[dns_count++] = addr;
+                       }
+                       break;
+
+               case GPDS_SDNS_ADDRESS_INFO:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3))
+                               break;
+
+                       if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value,
+                                                       4))
+                               break;
+
+                       if (addr_len == 4) {
+                               char *addr = alloca(INET_ADDRSTRLEN);
+                               inet_ntop(AF_INET, (const void *)addr_value,
+                                               addr, INET_ADDRSTRLEN);
+                               dns[dns_count++] = addr;
+                       } else if (addr_len == 16) {
+                               char *addr = alloca(INET6_ADDRSTRLEN);
+                               inet_ntop(AF_INET6, (const void *)addr_value,
+                                               addr, INET6_ADDRSTRLEN);
+                               dns[dns_count++] = addr;
+                       }
+                       break;
+
+               default:
+                       DBG("skipped sub-block: %s (%zu bytes)",
+                               gpds_subblock_name(g_isi_sb_iter_get_id(&iter)),
+                               g_isi_sb_iter_get_len(&iter));
+               }
+       }
+
+       if (!g_isi_pep_get_ifname(cd->pep, ifname))
+               goto error;
+
+       dns[dns_count] = 0;
+
+       ofono_gprs_context_set_interface(cd->context, ifname);
+
+       if (ip_addr != NULL) {
+               ofono_gprs_context_set_ipv4_address(cd->context, ip_addr, TRUE);
+               ofono_gprs_context_set_ipv4_netmask(cd->context,
+                                                       STATIC_IP_NETMASK);
+               ofono_gprs_context_set_ipv4_dns_servers(cd->context, dns);
+       } else if (ipv6_addr != NULL) {
+               ofono_gprs_context_set_ipv6_address(cd->context, ipv6_addr);
+               ofono_gprs_context_set_ipv6_dns_servers(cd->context, dns);
+       }
+
+       CALLBACK_WITH_SUCCESS(cd->cb, cd->data);
+       return;
+
+error:
+       gprs_up_fail(cd);
+}
+
+static void context_activate_cb(const GIsiMessage *msg, void *cd)
+{
+       check_resp(msg, GPDS_CONTEXT_ACTIVATE_RESP, 6, cd, gprs_up_fail);
+}
+
+static void send_context_activate(GIsiClient *client, void *opaque)
+{
+       struct context_data *cd = opaque;
+
+       const unsigned char msg[] = {
+               GPDS_CONTEXT_ACTIVATE_REQ,
+               cd->handle,     /* context ID */
+               0,              /* sub blocks */
+       };
+
+       g_isi_client_ind_subscribe(client, GPDS_CONTEXT_ACTIVATE_IND,
+                               activate_ind_cb, cd);
+       g_isi_client_ind_subscribe(client, GPDS_CONTEXT_DEACTIVATE_IND,
+                               deactivate_ind_cb, cd);
+
+       if (g_isi_client_send_with_timeout(client, msg, sizeof(msg),
+                               GPDS_CTX_ACTIVATE_TIMEOUT,
+                               context_activate_cb, cd, NULL))
+               g_isi_pipe_start(cd->pipe);
+       else
+               gprs_up_fail(cd);
+}
+
+static void context_auth_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct context_data *cd = opaque;
+
+       if (!check_resp(msg, GPDS_CONTEXT_AUTH_RESP, 2, cd, gprs_up_fail))
+               return;
+
+       send_context_activate(cd->client, cd);
+}
+
+static void send_context_authenticate(GIsiClient *client, void *opaque)
+{
+       struct context_data *cd = opaque;
+       size_t username_len = strlen(cd->username);
+       size_t password_len = strlen(cd->password);
+
+       /* Pad the fields to the next 32bit boundary */
+       size_t sb_userinfo_len = ALIGN4(3 + username_len);
+       size_t userinfo_pad_len = sb_userinfo_len - (3 + username_len);
+
+       size_t sb_password_info_len = ALIGN4(3 + password_len);
+       size_t password_pad_len = sb_password_info_len - (3 + password_len);
+
+       const uint8_t padding[4] = { 0 };
+
+       const uint8_t top[] = {
+               GPDS_CONTEXT_AUTH_REQ,
+               cd->handle,
+               2,      /* sub blocks */
+               GPDS_USER_NAME_INFO,
+               sb_userinfo_len,
+               username_len,
+               /* Username goes here */
+               /* Possible padding goes here */
+       };
+
+       const uint8_t bottom[] = {
+               GPDS_PASSWORD_INFO,
+               sb_password_info_len,
+               password_len,
+               /* Password goes here */
+               /* Possible padding goes here */
+       };
+
+       const struct iovec iov[6] = {
+               { (uint8_t *) top, sizeof(top) },
+               { cd->username, username_len },
+               { (uint8_t *) padding, userinfo_pad_len },
+               { (uint8_t *) bottom, sizeof(bottom) },
+               { cd->password, password_len },
+               { (uint8_t *) padding, password_pad_len },
+       };
+
+       if (!g_isi_client_vsend(client, iov, 6, context_auth_cb, cd, NULL))
+               gprs_up_fail(cd);
+}
+
+static void context_conf_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct context_data *cd = opaque;
+
+       if (!check_resp(msg, GPDS_CONTEXT_CONFIGURE_RESP, 2, cd, gprs_up_fail))
+               return;
+
+       if (cd->username[0] != '\0')
+               send_context_authenticate(cd->client, cd);
+       else
+               send_context_activate(cd->client, cd);
+}
+
+static void link_conf_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct context_data *cd = opaque;
+       size_t apn_len = strlen(cd->apn);
+       size_t sb_apn_info_len = ALIGN4(3 + apn_len);
+       size_t apn_pad_len = sb_apn_info_len - (3 + apn_len);
+
+       const uint8_t padding[4] = { 0 };
+
+       const uint8_t req[] = {
+               GPDS_CONTEXT_CONFIGURE_REQ,
+               cd->handle,     /* context ID */
+               cd->type,       /* PDP type */
+               GPDS_CONT_TYPE_NORMAL,
+               cd->handle,     /* primary context ID */
+               0x00,           /* filler */
+               2,              /* sub blocks */
+               GPDS_DNS_ADDRESS_REQ_INFO,
+               4,              /* subblock length */
+               0, 0,           /* padding */
+               GPDS_APN_INFO,
+               sb_apn_info_len,
+               apn_len,
+               /* Possible padding goes here */
+       };
+
+       const struct iovec iov[3] = {
+               { (uint8_t *) req, sizeof(req) },
+               { cd->apn, apn_len },
+               { (uint8_t *) padding, apn_pad_len },
+       };
+
+       if (!check_resp(msg, GPDS_LL_CONFIGURE_RESP, 2, cd, gprs_up_fail))
+               return;
+
+       if (!g_isi_client_vsend(cd->client, iov, 3, context_conf_cb, cd, NULL))
+               gprs_up_fail(cd);
+}
+
+static void create_context_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct context_data *cd = opaque;
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       uint8_t req[] = {
+               GPDS_LL_CONFIGURE_REQ,
+               0x00,           /* GPDS context ID, added later */
+               g_isi_pipe_get_handle(cd->pipe),
+               GPDS_LL_PLAIN,  /* link type */
+       };
+
+       if (!check_resp(msg, GPDS_CONTEXT_ID_CREATE_RESP, 2, cd, gprs_up_fail))
+               return;
+
+       cd->handle = req[1] = data[0];
+
+       if (!g_isi_client_send(cd->client, req, sizeof(req), link_conf_cb,
+                               cd, NULL))
+               gprs_up_fail(cd);
+}
+
+static void create_pipe_cb(GIsiPipe *pipe)
+{
+       struct context_data *cd = g_isi_pipe_get_userdata(pipe);
+
+       const uint8_t msg[] = {
+               GPDS_CONTEXT_ID_CREATE_REQ,
+       };
+
+       if (!g_isi_client_send(cd->client, msg, sizeof(msg), create_context_cb,
+                               cd, NULL))
+               gprs_up_fail(cd);
+}
+
+static void isi_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct context_data *cd = ofono_gprs_context_get_data(gc);
+
+       DBG("activate: gpds = 0x%04x", cd->gpds);
+
+       if (cd == NULL || !cd->gpds) {
+               /* GPDS is not reachable */
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       if (cd->reset) {
+               g_isi_client_reset(cd->client);
+               g_source_remove(cd->reset);
+               cd->reset = 0;
+       }
+
+       cd->cid = ctx->cid;
+       cd->cb = cb;
+       cd->data = data;
+       cd->pep = NULL;
+       cd->pipe = NULL;
+       cd->handle = INVALID_ID;
+
+       switch (ctx->proto) {
+       case OFONO_GPRS_PROTO_IP:
+               cd->type = GPDS_PDP_TYPE_IPV4;
+               break;
+
+       case OFONO_GPRS_PROTO_IPV6:
+               cd->type = GPDS_PDP_TYPE_IPV6;
+               break;
+
+       case OFONO_GPRS_PROTO_IPV4V6:
+               /* Not supported by modem */
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       if (strlen(ctx->apn) >= GPDS_MAX_APN_STRING_LENGTH
+                       || strlen(ctx->username) >= GPDS_MAX_USERNAME_LENGTH
+                       || strlen(ctx->password) >= GPDS_MAX_PASSWORD_LENGTH)
+               goto error;
+
+       strncpy(cd->apn, ctx->apn, GPDS_MAX_APN_STRING_LENGTH);
+       cd->apn[GPDS_MAX_APN_STRING_LENGTH] = '\0';
+
+       strncpy(cd->username, ctx->username, GPDS_MAX_USERNAME_LENGTH);
+       cd->username[GPDS_MAX_USERNAME_LENGTH] = '\0';
+
+       strncpy(cd->password, ctx->password, GPDS_MAX_PASSWORD_LENGTH);
+       cd->username[GPDS_MAX_PASSWORD_LENGTH] = '\0';
+
+       cd->pep = g_isi_pep_create(cd->idx, NULL, NULL);
+       if (cd->pep == NULL)
+               goto error;
+
+       cd->pipe = g_isi_pipe_create(cd->idx, create_pipe_cb,
+                                       g_isi_pep_get_object(cd->pep),
+                                       cd->gpds, PN_PEP_TYPE_GPRS,
+                                       PN_PEP_TYPE_GPRS);
+       if (cd->pipe == NULL)
+               goto error;
+
+       g_isi_pipe_set_userdata(cd->pipe, cd);
+       return;
+
+error:
+       gprs_up_fail(cd);
+}
+
+static void context_deactivate_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct context_data *cd = opaque;
+
+       if (!check_resp(msg, GPDS_CONTEXT_DEACTIVATE_RESP, 2, cd,
+                       gprs_down_fail))
+               return;
+
+       CALLBACK_WITH_SUCCESS(cd->cb, cd->data);
+       reset_context(cd);
+}
+
+static void isi_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int cid,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct context_data *cd = ofono_gprs_context_get_data(gc);
+
+       unsigned char msg[] = {
+               GPDS_CONTEXT_DEACTIVATE_REQ,
+               0x00,   /* GPDS context ID, added later */
+       };
+
+       if (cd == NULL)
+               return;
+
+       cd->cb = cb;
+       cd->data = data;
+
+       msg[1] = cd->handle;
+
+       if (!g_isi_client_send_with_timeout(cd->client, msg, sizeof(msg),
+                               GPDS_CTX_DEACTIVATE_TIMEOUT,
+                               context_deactivate_cb, cd, NULL))
+               gprs_down_fail(cd);
+}
+
+static void gpds_ctx_reachable_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_gprs_context *gc = opaque;
+       struct context_data *cd = ofono_gprs_context_get_data(gc);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("unable to bootstrap gprs context driver");
+               ofono_gprs_context_remove(gc);
+               return;
+       }
+
+       cd->gpds = g_isi_msg_object(msg);
+}
+
+static int isi_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *user)
+{
+       GIsiModem *idx = user;
+       struct context_data *cd = g_try_new0(struct context_data, 1);
+
+       if (cd == NULL)
+               return -ENOMEM;
+
+       cd->client = g_isi_client_create(idx, PN_GPDS);
+       if (cd->client == NULL) {
+               g_free(cd);
+               return -ENOMEM;
+       }
+
+       cd->idx = idx;
+       cd->context = gc;
+       ofono_gprs_context_set_data(gc, cd);
+
+       g_isi_client_verify(cd->client, gpds_ctx_reachable_cb, gc, NULL);
+
+       return 0;
+}
+
+static void isi_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct context_data *cd = ofono_gprs_context_get_data(gc);
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       if (cd == NULL)
+               return;
+
+       if (cd->reset)
+               g_source_remove(cd->reset);
+
+       if (cd->pipe != NULL)
+               g_isi_pipe_destroy(cd->pipe);
+
+       if (cd->pep != NULL)
+               g_isi_pep_destroy(cd->pep);
+
+       g_isi_client_destroy(cd->client);
+       g_free(cd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_gprs_context_probe,
+       .remove                 = isi_gprs_context_remove,
+       .activate_primary       = isi_gprs_activate_primary,
+       .deactivate_primary     = isi_gprs_deactivate_primary,
+};
+
+void isi_gprs_context_init(void)
+{
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void isi_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/gprs.c b/drivers/isimodem/gprs.c
new file mode 100644 (file)
index 0000000..2d64bf1
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "gpds.h"
+#include "info.h"
+#include "debug.h"
+
+/* 27.007 Section 10.1.20 <stat> */
+enum network_registration_status {
+       GPRS_STAT_NOT_REGISTERED =      0,
+       GPRS_STAT_REGISTERED =          1,
+       GPRS_STAT_SEARCHING =           2,
+       GPRS_STAT_DENIED =              3,
+       GPRS_STAT_UNKNOWN =             4,
+       GPRS_STAT_ROAMING =             5,
+};
+
+struct gprs_data {
+       GIsiClient *client;
+       GIsiClient *info_client;
+};
+
+static void configure_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("ISI message error: %d", g_isi_msg_error(msg));
+               return;
+       }
+
+       if (g_isi_msg_id(msg) != GPDS_CONFIGURE_RESP)
+               return;
+
+       if (g_isi_msg_data_len(msg) < 1)
+               return;
+
+       if (data[0] != GPDS_OK)
+               DBG("GPDS configure failed: %s", gpds_status_name(data[0]));
+}
+
+static void set_attach_mode(struct ofono_gprs *gprs, int attached)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+
+       const unsigned char msg[] = {
+               GPDS_CONFIGURE_REQ,
+               attached ? GPDS_ATTACH_MODE_AUTOMATIC : GPDS_ATTACH_MODE_MANUAL,
+               GPDS_MT_ACT_MODE_REJECT,
+               GPDS_CLASSC_MODE_DEFAULT,
+               GPDS_AOL_CTX_DEFAULT,
+               0x00,
+               0x00
+       };
+
+       g_isi_client_send(gd->client, msg, sizeof(msg),
+                               configure_resp_cb, gprs, NULL);
+}
+
+static void detach_ind_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_gprs *gprs = opaque;
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0)
+               return;
+
+       if (g_isi_msg_id(msg) != GPDS_DETACH_IND)
+               return;
+
+       if (g_isi_msg_data_len(msg) < 2)
+               return;
+
+       DBG("detached: %s (0x%02"PRIx8")",
+               gpds_isi_cause_name(data[0]), data[0]);
+
+       set_attach_mode(gprs, FALSE);
+
+       ofono_gprs_detached_notify(gprs);
+}
+
+static void suspend_notify(struct ofono_gprs *gprs, uint8_t suspend_status,
+                       uint8_t suspend_cause)
+{
+       int cause;
+
+       DBG("transfer status: %s (0x%02"PRIx8") cause %s (0x%02"PRIx8")",
+               gpds_transfer_status_name(suspend_status), suspend_status,
+               gpds_transfer_cause_name(suspend_cause), suspend_cause);
+
+       if (suspend_status == GPDS_TRANSFER_AVAIL) {
+               ofono_gprs_resume_notify(gprs);
+               return;
+       }
+
+       switch (suspend_cause) {
+       case GPDS_TRANSFER_CAUSE_SUSPENDED_NO_COVERAGE:
+               cause = GPRS_SUSPENDED_NO_COVERAGE;
+               break;
+
+       case GPDS_TRANSFER_CAUSE_SUSPENDED_CALL:
+               cause = GPRS_SUSPENDED_CALL;
+               break;
+
+       case GPDS_TRANSFER_CAUSE_SUSPENDED_CALL_SMS:
+       case GPDS_TRANSFER_CAUSE_SUSPENDED_RAU:
+       case GPDS_TRANSFER_CAUSE_SUSPENDED_LU:
+               cause = GPRS_SUSPENDED_SIGNALLING;
+               break;
+
+       default:
+               return;
+       }
+
+       ofono_gprs_suspend_notify(gprs, cause);
+}
+
+static void transfer_status_ind_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_gprs *gprs = opaque;
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0)
+               return;
+
+       if (g_isi_msg_id(msg) != GPDS_TRANSFER_STATUS_IND)
+               return;
+
+       if (g_isi_msg_data_len(msg) < 2)
+               return;
+
+       suspend_notify(gprs, data[0], data[1]);
+}
+
+static void create_contexts(struct ofono_gprs *gprs, int count)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       GIsiModem *modem = g_isi_client_modem(gd->client);
+       struct ofono_modem *omodem = g_isi_modem_get_userdata(modem);
+       struct ofono_gprs_context *gc;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               gc = ofono_gprs_context_create(omodem, 0, "isimodem", modem);
+               if (gc == NULL)
+                       break;
+
+               ofono_gprs_add_context(gprs, gc);
+       }
+
+       ofono_gprs_set_cid_range(gprs, 1, i);
+
+       DBG("%d GPRS contexts created", count);
+}
+
+static void info_pp_read_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_gprs *gprs = opaque;
+       uint8_t count = GPDS_MAX_CONTEXT_COUNT;
+       GIsiSubBlockIter iter;
+
+       if (g_isi_msg_error(msg) == -ESHUTDOWN)
+               return;
+
+       if (g_isi_msg_error(msg) < 0)
+               goto out;
+
+       if (g_isi_msg_id(msg) != INFO_PP_READ_RESP)
+               goto out;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case INFO_SB_PP: {
+                       guint16 fea;
+                       guint8 n;
+                       unsigned pp;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &n, 1))
+                               goto out;
+
+                       for (pp = 4; n--; pp += 2) {
+
+                               if (!g_isi_sb_iter_get_word(&iter, &fea, pp))
+                                       goto out;
+
+                               if ((fea >> 8) != INFO_PP_MAX_PDP_CONTEXTS)
+                                       goto out;
+
+                               count = fea & 0xff;
+                               break;
+                       }
+                       break;
+               }
+
+               default:
+                       break;
+               }
+       }
+
+out:
+       create_contexts(gprs, count);
+}
+
+static void gpds_reachable_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_gprs *gprs = opaque;
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       GIsiModem *modem = g_isi_client_modem(gd->client);
+
+       const unsigned char req[] = {
+               INFO_PP_READ_REQ,
+               0,                              /* filler */
+               1,                              /* subblocks */
+               INFO_SB_PP,
+               8,                              /* subblock length */
+               0,
+               1,                              /* N */
+               INFO_PP_MAX_PDP_CONTEXTS,       /* PP feature */
+               0,                              /* PP value */
+               0,                              /* filler */
+               0                               /* filler */
+       };
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("unable to bootstrap gprs driver");
+               ofono_gprs_remove(gprs);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_ind_subscribe(gd->client, GPDS_DETACH_IND,
+                                       detach_ind_cb, gprs);
+       g_isi_client_ind_subscribe(gd->client, GPDS_TRANSFER_STATUS_IND,
+                                       transfer_status_ind_cb, gprs);
+
+       ofono_gprs_register(gprs);
+
+       gd->info_client = g_isi_client_create(modem, PN_PHONE_INFO);
+       if (gd->info_client == NULL) {
+               create_contexts(gprs, GPDS_MAX_CONTEXT_COUNT);
+               return;
+       }
+
+       g_isi_client_send(gd->info_client, req, sizeof(req),
+                               info_pp_read_resp_cb, gprs, NULL);
+}
+
+static int isi_gprs_probe(struct ofono_gprs *gprs,
+                               unsigned int vendor, void *user)
+{
+       GIsiModem *modem = user;
+       struct gprs_data *gd = g_try_new0(struct gprs_data, 1);
+
+       if (gd == NULL)
+               return -ENOMEM;
+
+       gd->client = g_isi_client_create(modem, PN_GPDS);
+       if (gd->client == NULL) {
+               g_free(gd);
+               return -ENOMEM;
+       }
+
+       ofono_gprs_set_data(gprs, gd);
+
+       g_isi_client_set_timeout(gd->client, GPDS_TIMEOUT);
+       g_isi_client_verify(gd->client, gpds_reachable_cb, gprs, NULL);
+
+       return 0;
+}
+
+static void isi_gprs_remove(struct ofono_gprs *gprs)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+
+       ofono_gprs_set_data(gprs, NULL);
+
+       if (gd == NULL)
+               return;
+
+       g_isi_client_destroy(gd->client);
+       g_isi_client_destroy(gd->info_client);
+       g_free(gd);
+}
+
+static void attach_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_gprs_cb_t cb = cbd->cb;
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("ISI message error: %d", g_isi_msg_error(msg));
+               goto error;
+       }
+
+       if (g_isi_msg_id(msg) != GPDS_ATTACH_RESP)
+               return;
+
+       if (g_isi_msg_data_len(msg) < 2)
+               goto error;
+
+       if (data[0] != GPDS_OK) {
+               DBG("attach failed: %s", gpds_status_name(data[0]));
+               goto error;
+       }
+
+       set_attach_mode(cbd->user, TRUE);
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void detach_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_gprs_cb_t cb = cbd->cb;
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("ISI client error: %d", g_isi_msg_error(msg));
+               goto error;
+       }
+
+       if (g_isi_msg_id(msg) != GPDS_DETACH_RESP)
+               return;
+
+       if (g_isi_msg_data_len(msg) < 2)
+               goto error;
+
+       if (data[0] != GPDS_OK) {
+               DBG("detach failed: %s", gpds_status_name(data[0]));
+               goto error;
+       }
+
+       set_attach_mode(cbd->user, FALSE);
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_gprs_set_attached(struct ofono_gprs *gprs, int attached,
+                                       ofono_gprs_cb_t cb, void *data)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       struct isi_cb_data *cbd = isi_cb_data_new(gprs, cb, data);
+
+       if (cbd == NULL || gd == NULL)
+               goto error;
+
+       if (attached) {
+               const unsigned char msg[] = {
+                       GPDS_ATTACH_REQ,
+                       GPDS_FOLLOW_OFF
+               };
+
+               if (g_isi_client_send_with_timeout(gd->client,
+                               msg, sizeof(msg),
+                               GPDS_ATTACH_TIMEOUT, attach_resp_cb,
+                               cbd, g_free))
+                       return;
+       } else {
+               const unsigned char msg[] = {
+                       GPDS_DETACH_REQ,
+                       0x00, /* filler */
+                       0x00  /* sub-blocks */
+               };
+
+               if (g_isi_client_send_with_timeout(gd->client,
+                               msg, sizeof(msg),
+                               GPDS_DETACH_TIMEOUT, detach_resp_cb,
+                               cbd, g_free))
+                       return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void status_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_gprs_status_cb_t cb = cbd->cb;
+       struct ofono_gprs *gprs = cbd->data;
+       int status;
+       const uint8_t *data = g_isi_msg_data(msg);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("ISI message error: %d", g_isi_msg_error(msg));
+               goto error;
+       }
+
+       if (g_isi_msg_id(msg) != GPDS_STATUS_RESP)
+               return;
+
+       if (g_isi_msg_data_len(msg) < 12)
+               goto error;
+
+       /* FIXME: the core still expects reg status, and not a boolean
+        * attached status here.*/
+       switch (data[0]) {
+       case GPDS_ATTACHED:
+               status = GPRS_STAT_REGISTERED;
+               break;
+       case GPDS_DETACHED:
+               status = GPRS_STAT_NOT_REGISTERED;
+               break;
+       default:
+               status = GPRS_STAT_UNKNOWN;
+       }
+
+       suspend_notify(gprs, data[10], data[11]);
+
+       CALLBACK_WITH_SUCCESS(cb, status, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void isi_gprs_attached_status(struct ofono_gprs *gprs,
+                                               ofono_gprs_status_cb_t cb,
+                                               void *data)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       struct isi_cb_data *cbd = isi_cb_data_new(NULL, cb, data);
+
+       const unsigned char msg[] = {
+               GPDS_STATUS_REQ,
+       };
+
+       if (cbd == NULL || gd == NULL)
+               goto error;
+
+       if (g_isi_client_send(gd->client, msg, sizeof(msg),
+                               status_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+       g_free(cbd);
+}
+
+static struct ofono_gprs_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_gprs_probe,
+       .remove                 = isi_gprs_remove,
+       .set_attached           = isi_gprs_set_attached,
+       .attached_status        = isi_gprs_attached_status,
+};
+
+void isi_gprs_init(void)
+{
+       ofono_gprs_driver_register(&driver);
+}
+
+void isi_gprs_exit(void)
+{
+       ofono_gprs_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/gss.h b/drivers/isimodem/gss.h
new file mode 100644 (file)
index 0000000..a479b82
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_GSS_H
+#define __ISIMODEM_GSS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_GSS                         0x32
+#define GSS_TIMEOUT                    5
+
+enum gss_message_id {
+       GSS_CS_SERVICE_REQ =            0x00,
+       GSS_CS_SERVICE_RESP =           0x01,
+       GSS_CS_SERVICE_FAIL_RESP =      0x02,
+};
+
+enum gss_subblock {
+       GSS_RAT_INFO =                  0x0B,
+};
+
+enum gss_selection_mode {
+       GSS_DUAL_RAT =                  0x00,
+       GSS_GSM_RAT =                   0x01,
+       GSS_UMTS_RAT =                  0x02,
+};
+
+enum gss_operation {
+       GSS_SELECTED_RAT_WRITE =        0x0E,
+       GSS_SELECTED_RAT_READ =         0x9C,
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !__ISIMODEM_GSS_H */
diff --git a/drivers/isimodem/info.h b/drivers/isimodem/info.h
new file mode 100644 (file)
index 0000000..3fee12b
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_INFO_H
+#define __ISIMODEM_INFO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_PHONE_INFO                          0x1B
+#define PN_MODEM_INFO                          0xC5
+
+#define PN_EPOC_INFO                           0x62
+#define INFO_TIMEOUT                           5
+
+enum info_isi_cause {
+       INFO_OK =                               0x00,
+       INFO_FAIL =                             0x01,
+       INFO_NO_NUMBER =                        0x02,
+       INFO_NOT_SUPPORTED =                    0x03,
+};
+
+enum info_message_id {
+       INFO_SERIAL_NUMBER_READ_REQ =           0x00,
+       INFO_SERIAL_NUMBER_READ_RESP =          0x01,
+       INFO_PP_READ_REQ =                      0x02,
+       INFO_PP_READ_RESP =                     0x03,
+       INFO_VERSION_READ_REQ =                 0x07,
+       INFO_VERSION_READ_RESP =                0x08,
+       INFO_PRODUCT_INFO_READ_REQ =            0x15,
+       INFO_PRODUCT_INFO_READ_RESP =           0x16,
+};
+
+enum info_subblock {
+       INFO_SB_MODEMSW_VERSION =               0x00,
+       INFO_SB_PRODUCT_INFO_NAME =             0x01,
+       INFO_SB_PRODUCT_INFO_MANUFACTURER =     0x07,
+       INFO_SB_SN_IMEI_PLAIN =                 0x41,
+       INFO_SB_SN_IMEI_SV_TO_NET =             0x43,
+       INFO_SB_PP =                            0x47,
+       INFO_SB_MCUSW_VERSION =                 0x48,
+};
+
+enum info_product_info_type {
+       INFO_PRODUCT_NAME =                     0x01,
+       INFO_PRODUCT_MANUFACTURER =             0x07,
+};
+
+enum info_serial_number_type {
+       INFO_SN_IMEI_PLAIN =                    0x41,
+};
+
+enum info_version_type {
+       INFO_MCUSW =                            0x01,
+};
+
+enum info_pp_feature {
+       INFO_PP_MAX_PDP_CONTEXTS =              0xCA
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !__ISIMODEM_INFO_H */
diff --git a/drivers/isimodem/infoserver.c b/drivers/isimodem/infoserver.c
new file mode 100644 (file)
index 0000000..defc264
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gisi/modem.h>
+#include <gisi/server.h>
+#include <gisi/message.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/log.h>
+#include <ofono/modem.h>
+
+#include "info.h"
+#include "infoserver.h"
+
+struct isi_infoserver {
+       GIsiServer *server;
+       unsigned sv;    /* Software version in 0..98 */
+};
+
+static GIsiVersion isiversion = {
+       .major = 0,
+       .minor = 0,
+};
+
+static void send_error(GIsiServer *server, const GIsiMessage *req, uint8_t code)
+{
+       const uint8_t error[] = {
+               INFO_SERIAL_NUMBER_READ_RESP,
+               code,
+               0
+       };
+
+       g_isi_server_send(server, req, error, sizeof(error));
+}
+
+static void send_response(GIsiServer *server, const GIsiMessage *req,
+                               unsigned sv)
+{
+       const uint8_t resp[] = {
+               INFO_SERIAL_NUMBER_READ_RESP, INFO_OK, 1,
+               INFO_SB_SN_IMEI_SV_TO_NET, 16,
+               /* Mobile Identity IE, TS 24.008 section 10.5.1.4 */
+               0, 9,
+               /* F in place of IMEI digits and filler */
+               0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+               0x0f | ((sv / 10) << 4),
+               0xf0 | ((sv % 10) & 0x0f),
+               /* Subblock filler */
+               0, 0, 0,
+       };
+
+       g_isi_server_send(server, req, resp, sizeof(resp));
+}
+
+static void serial_number_read_req(const GIsiMessage *msg, void *data)
+{
+       struct isi_infoserver *self = data;
+       uint8_t target;
+
+       if (g_isi_msg_id(msg) != INFO_SERIAL_NUMBER_READ_REQ)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &target)) {
+               send_error(self->server, msg, INFO_FAIL);
+               return;
+       }
+
+       if (target == INFO_SB_SN_IMEI_SV_TO_NET) {
+               /* IMEISV defined in 3GPP TS 23.003 section 6.2.2 */
+               send_response(self->server, msg, self->sv);
+               return;
+       }
+
+       DBG("Unknown query target 0x%02X", target);
+       send_error(self->server, msg, INFO_NOT_SUPPORTED);
+}
+
+struct isi_infoserver *isi_infoserver_create(struct ofono_modem *modem,
+                                               void *data)
+{
+       struct isi_infoserver *self;
+       GIsiModem *isimodem = data;
+
+       if (isimodem == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       self = g_try_new0(struct isi_infoserver, 1);
+       if (self == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       self->server = g_isi_server_create(isimodem, PN_EPOC_INFO, &isiversion);
+       if (self->server == NULL) {
+               g_free(self);
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       g_isi_server_handle(self->server,
+                               INFO_SERIAL_NUMBER_READ_REQ,
+                               serial_number_read_req,
+                               self);
+
+       return self;
+}
+
+void isi_infoserver_destroy(struct isi_infoserver *self)
+{
+       if (self == NULL)
+               return;
+
+       g_isi_server_destroy(self->server);
+       g_free(self);
+}
diff --git a/drivers/isimodem/infoserver.h b/drivers/isimodem/infoserver.h
new file mode 100644 (file)
index 0000000..0b37a36
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_ISI_INFOSERVER_H
+#define __OFONO_ISI_INFOSERVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct isi_infoserver;
+
+struct isi_infoserver *isi_infoserver_create(struct ofono_modem *modem,
+                                               void *data);
+
+void isi_infoserver_destroy(struct isi_infoserver *self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_ISI_INFOSERVER_H */
diff --git a/drivers/isimodem/isimodem.c b/drivers/isimodem/isimodem.c
new file mode 100644 (file)
index 0000000..2d083ce
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+
+#include "isimodem.h"
+
+static int isimodem_init(void)
+{
+       isi_devinfo_init();
+       isi_phonebook_init();
+       isi_netreg_init();
+       isi_voicecall_init();
+       isi_sms_init();
+       isi_cbs_init();
+       isi_sim_init();
+       isi_ussd_init();
+       isi_call_forwarding_init();
+       isi_call_settings_init();
+       isi_call_barring_init();
+       isi_call_meter_init();
+       isi_radio_settings_init();
+       isi_gprs_init();
+       isi_gprs_context_init();
+       isi_audio_settings_init();
+       isi_uicc_init();
+
+       return 0;
+}
+
+static void isimodem_exit(void)
+{
+       isi_devinfo_exit();
+       isi_phonebook_exit();
+       isi_netreg_exit();
+       isi_voicecall_exit();
+       isi_sms_exit();
+       isi_cbs_exit();
+       isi_sim_exit();
+       isi_ussd_exit();
+       isi_call_forwarding_exit();
+       isi_call_settings_exit();
+       isi_call_barring_exit();
+       isi_call_meter_exit();
+       isi_radio_settings_exit();
+       isi_gprs_exit();
+       isi_gprs_context_exit();
+       isi_audio_settings_exit();
+       isi_uicc_exit();
+}
+
+OFONO_PLUGIN_DEFINE(isimodem, "PhoNet / ISI modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, isimodem_init, isimodem_exit)
diff --git a/drivers/isimodem/isimodem.h b/drivers/isimodem/isimodem.h
new file mode 100644 (file)
index 0000000..ce25604
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+extern void isi_phonebook_init(void);
+extern void isi_phonebook_exit(void);
+
+extern void isi_devinfo_init(void);
+extern void isi_devinfo_exit(void);
+
+extern void isi_netreg_init(void);
+extern void isi_netreg_exit(void);
+
+extern void isi_voicecall_init(void);
+extern void isi_voicecall_exit(void);
+
+extern void isi_sms_init(void);
+extern void isi_sms_exit(void);
+
+extern void isi_cbs_init(void);
+extern void isi_cbs_exit(void);
+
+extern void isi_sim_init(void);
+extern void isi_sim_exit(void);
+
+extern void isi_ussd_init(void);
+extern void isi_ussd_exit(void);
+
+extern void isi_call_forwarding_init(void);
+extern void isi_call_forwarding_exit(void);
+
+extern void isi_call_settings_init(void);
+extern void isi_call_settings_exit(void);
+
+extern void isi_call_barring_init(void);
+extern void isi_call_barring_exit(void);
+
+extern void isi_call_meter_init(void);
+extern void isi_call_meter_exit(void);
+
+extern void isi_radio_settings_init(void);
+extern void isi_radio_settings_exit(void);
+
+extern void isi_gprs_init(void);
+extern void isi_gprs_exit(void);
+
+extern void isi_gprs_context_init(void);
+extern void isi_gprs_context_exit(void);
+
+extern void isi_audio_settings_init(void);
+extern void isi_audio_settings_exit(void);
+
+extern void isi_uicc_init(void);
+extern void isi_uicc_exit(void);
diff --git a/drivers/isimodem/isiutil.h b/drivers/isimodem/isiutil.h
new file mode 100644 (file)
index 0000000..981f9ee
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_UTIL_H
+#define __ISIMODEM_UTIL_H
+
+struct isi_cb_data {
+       void *cb;
+       void *data;
+       void *user;
+};
+
+static inline struct isi_cb_data *isi_cb_data_new(void *user, void *cb,
+                                                       void *data)
+{
+       struct isi_cb_data *ret;
+
+       ret = g_try_new0(struct isi_cb_data, 1);
+       if (ret) {
+               ret->cb = cb;
+               ret->data = data;
+               ret->user = user;
+       }
+       return ret;
+}
+
+#define CALLBACK_WITH_FAILURE(f, args...)              \
+       do {                                            \
+               struct ofono_error e;                   \
+               e.type = OFONO_ERROR_TYPE_FAILURE;      \
+               e.error = 0;                            \
+               f(&e, ##args);                          \
+       } while (0)
+
+#define CALLBACK_WITH_SUCCESS(f, args...)              \
+       do {                                            \
+               struct ofono_error e;                   \
+               e.type = OFONO_ERROR_TYPE_NO_ERROR;     \
+               e.error = 0;                            \
+               f(&e, ##args);                          \
+       } while (0)
+
+#define ISI_RESOURCE_DBG(msg)                                  \
+       DBG("QSO: %s [0x%02X] v%03d.%03d",                      \
+               pn_resource_name(g_isi_msg_resource((msg))),    \
+               g_isi_msg_resource((msg)),                      \
+               g_isi_msg_version_major((msg)),                 \
+               g_isi_msg_version_minor((msg)));
+
+#define ISI_VERSION_AT_LEAST(ver,maj,min)                      \
+       ((ver) != NULL && ((ver)->major > (maj) ||              \
+               ((ver)->major == (maj) &&                       \
+                       (ver)->minor >= (min))))
+
+#define ALIGN4(val) (((val) + 3) & ~3)
+
+#define ISI_16BIT(val)                                         \
+       (((val) >> 8) & 0xFF), ((val) & 0xFF)
+
+#define ISI_32BIT(val)                                         \
+       (((val) >> 24) & 0xFF), (((val) >> 16) & 0xFF),         \
+       (((val) >> 8) & 0xFF), ((val) & 0xFF)
+
+#endif /* !__ISIMODEM_UTIL_H */
diff --git a/drivers/isimodem/mtc.h b/drivers/isimodem/mtc.h
new file mode 100644 (file)
index 0000000..f6d7fdd
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_MTC_H
+#define __ISIMODEM_MTC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_MTC                         0x15
+#define PN_MODEM_MCE                   0xC2
+#define MTC_TIMEOUT                    5
+#define MTC_STATE_REQ_TIMEOUT          (6 + 5)
+
+enum mce_message_id {
+       MCE_MODEM_STATE_IND =           0x00,
+       MCE_MODEM_STATE_QUERY_REQ =     0x01,
+       MCE_MODEM_STATE_QUERY_RESP =    0x02,
+       MCE_RF_STATE_REQ =              0x03,
+       MCE_RF_STATE_RESP =             0x04,
+       MCE_RF_STATE_IND =              0x05,
+       MCE_RF_STATE_QUERY_REQ =        0x06,
+       MCE_RF_STATE_QUERY_RESP =       0x07,
+       MCE_POWER_OFF_REQ =             0x08,
+       MCE_POWER_OFF_RESP =            0x09
+};
+
+enum mce_rf_state {
+       MCE_RF_OFF =                    0x00,
+       MCE_RF_ON =                     0x01
+};
+
+enum mce_status_info {
+       MCE_OK =                        0x00,
+       MCE_FAIL =                      0x01,
+       MCE_ALREADY_ACTIVE =            0x06,
+       MCE_TRANSITION_ONGOING =        0x16
+};
+
+enum mce_modem_state {
+       MCE_NORMAL =                    0x00,
+       MCE_LOCAL =                     0x01,
+       MCE_SW_RESET =                  0x80,
+       MCE_POWER_OFF =                 0x81
+};
+
+enum mce_isi_action {
+       MCE_START =                     0x03,
+       MCE_READY =                     0x04
+};
+
+enum mtc_isi_cause {
+       MTC_OK =                        0x00,
+       MTC_FAIL =                      0x01,
+       MTC_NOT_ALLOWED =               0x02,
+       MTC_STATE_TRANSITION_GOING_ON = 0x05,
+       MTC_ALREADY_ACTIVE =            0x06,
+       MTC_SERVICE_DISABLED =          0x10,
+       MTC_NOT_READY_YET =             0x13,
+       MTC_NOT_SUPPORTED =             0x14,
+       MTC_TRANSITION_ONGOING =        0x16,
+       MTC_RESET_REQUIRED =            0x17,
+};
+
+enum mtc_isi_action {
+       MTC_START =                     0x03,
+       MTC_READY =                     0x04,
+       MTC_NOS_READY =                 0x0C,
+       MTC_SOS_START =                 0x11,
+       MTC_SOS_READY =                 0x12,
+};
+
+enum mtc_message_id {
+       MTC_STATE_REQ =                 0x01,
+       MTC_STATE_QUERY_REQ =           0x02,
+       MTC_POWER_OFF_REQ =             0x03,
+       MTC_POWER_ON_REQ =              0x04,
+       MTC_STARTUP_SYNQ_REQ =          0x0B,
+       MTC_SHUTDOWN_SYNC_REQ =         0x12,
+       MTC_STATE_RESP =                0x64,
+       MTC_STATE_QUERY_RESP =          0x65,
+       MTC_POWER_OFF_RESP =            0x66,
+       MTC_POWER_ON_RESP =             0x67,
+       MTC_STARTUP_SYNQ_RESP =         0x6E,
+       MTC_SHUTDOWN_SYNC_RESP =        0x75,
+       MTC_STATE_INFO_IND =            0xC0,
+};
+
+enum mtc_modem_state {
+       MTC_STATE_NONE =                -1,     /* Used only internally */
+       MTC_POWER_OFF =                 0x00,
+       MTC_NORMAL =                    0x01,
+       MTC_CHARGING =                  0x02,
+       MTC_ALARM =                     0x03,
+       MTC_TEST =                      0x04,
+       MTC_LOCAL =                     0x05,
+       MTC_WARRANTY =                  0x06,
+       MTC_RELIABILITY =               0x07,
+       MTC_SELFTEST_FAIL =             0x08,
+       MTC_SWDL =                      0x09,
+       MTC_RF_INACTIVE =               0x0A,
+       MTC_ID_WRITE =                  0x0B,
+       MTC_DISCHARGING =               0x0C,
+       MTC_DISK_WIPE =                 0x0D,
+       MTC_SW_RESET =                  0x0E,
+       MTC_CMT_ONLY_MODE =             0xFF,
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __ISIMODEM_MTC_H */
diff --git a/drivers/isimodem/network-registration.c b/drivers/isimodem/network-registration.c
new file mode 100644 (file)
index 0000000..922eb9e
--- /dev/null
@@ -0,0 +1,1188 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/message.h>
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/netreg.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "network.h"
+#include "debug.h"
+
+struct reg_info {
+       uint8_t status;
+       uint8_t mode;
+};
+
+struct gsm_info {
+       uint16_t lac;
+       uint32_t ci;
+       uint8_t egprs;
+       uint8_t hsdpa;
+       uint8_t hsupa;
+};
+
+struct rat_info {
+       uint8_t rat;
+       uint8_t compact;
+};
+
+struct network_time {
+       uint8_t year;
+       uint8_t mon;
+       uint8_t mday;
+       uint8_t hour;
+       uint8_t min;
+       uint8_t sec;
+       uint8_t utc;
+       uint8_t dst;
+};
+
+struct netreg_data {
+       GIsiClient *client;
+       GIsiClient *pn_network;
+       GIsiClient *pn_modem_network;
+       struct reg_info reg;
+       struct gsm_info gsm;
+       struct rat_info rat;
+       GIsiVersion version;
+       char nitz_name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1];
+};
+
+static inline guint8 *mccmnc_to_bcd(const char *mcc, const char *mnc,
+                                               guint8 *bcd)
+{
+       bcd[0] = (mcc[0] - '0') | (mcc[1] - '0') << 4;
+       bcd[1] = (mcc[2] - '0');
+       bcd[1] |= (mnc[2] == '\0' ? 0x0f : (mnc[2] - '0')) << 4;
+       bcd[2] = (mnc[0] - '0') | (mnc[1] - '0') << 4;
+       return bcd;
+}
+
+static inline int isi_status_to_at_status(struct reg_info *reg)
+{
+       switch (reg->status) {
+       case NET_REG_STATUS_NOSERV:
+       case NET_REG_STATUS_NOSERV_NOTSEARCHING:
+       case NET_REG_STATUS_NOSERV_NOSIM:
+       case NET_REG_STATUS_POWER_OFF:
+       case NET_REG_STATUS_NSPS:
+       case NET_REG_STATUS_NSPS_NO_COVERAGE:
+               return 0;
+
+       case NET_REG_STATUS_HOME:
+               return 1;
+
+       case NET_REG_STATUS_NOSERV_SEARCHING:
+               return 2;
+
+       case NET_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW:
+               return 3;
+
+       case NET_REG_STATUS_ROAM_BLINK:
+       case NET_REG_STATUS_ROAM:
+               return 5;
+       }
+       return 4;
+}
+
+static inline int isi_to_at_tech(struct rat_info *rat, struct gsm_info *gsm)
+{
+       int tech = -1;
+
+       if (rat == NULL || gsm == NULL)
+               return -1;
+
+       if (rat->rat == NET_GSM_RAT)
+               tech = 0;
+
+       if (rat->compact)
+               tech = 1;
+
+       if (rat->rat == NET_UMTS_RAT)
+               tech = 2;
+
+       if (gsm->egprs)
+               tech = 3;
+
+       if (gsm->hsdpa)
+               tech = 4;
+
+       if (gsm->hsupa)
+               tech = 5;
+
+       if (gsm->hsdpa && gsm->hsupa)
+               tech = 6;
+
+       return tech;
+}
+
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
+{
+       uint8_t cause;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid)
+               return FALSE;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &cause))
+               return FALSE;
+
+       if (cause != NET_CAUSE_OK) {
+               DBG("Request failed: %s", net_isi_cause_name(cause));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static gboolean parse_common_info(GIsiSubBlockIter *iter, struct reg_info *reg)
+{
+       return reg && g_isi_sb_iter_get_byte(iter, &reg->status, 2) &&
+               g_isi_sb_iter_get_byte(iter, &reg->mode, 3);
+}
+
+static gboolean parse_gsm_info(GIsiSubBlockIter *iter, struct gsm_info *gsm)
+{
+       return gsm && g_isi_sb_iter_get_word(iter, &gsm->lac, 2) &&
+                       g_isi_sb_iter_get_dword(iter, &gsm->ci, 4) &&
+                       g_isi_sb_iter_get_byte(iter, &gsm->egprs, 17) &&
+                       g_isi_sb_iter_get_byte(iter, &gsm->hsdpa, 20) &&
+                       g_isi_sb_iter_get_byte(iter, &gsm->hsupa, 21);
+}
+
+static void reg_status_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GIsiSubBlockIter iter;
+
+       if (netreg == NULL || nd == NULL)
+               return;
+
+       if (g_isi_msg_id(msg) != NET_REG_STATUS_IND &&
+                       g_isi_msg_id(msg) != NET_MODEM_REG_STATUS_IND)
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case NET_REG_INFO_COMMON:
+
+                       if (!parse_common_info(&iter, &nd->reg))
+                               return;
+                       break;
+
+               case NET_GSM_REG_INFO:
+
+                       if (!parse_gsm_info(&iter, &nd->gsm))
+                               return;
+                       break;
+               }
+       }
+
+       ofono_netreg_status_notify(netreg, isi_status_to_at_status(&nd->reg),
+                                       nd->gsm.lac, nd->gsm.ci,
+                                       isi_to_at_tech(&nd->rat, &nd->gsm));
+}
+
+static gboolean parse_rat_info(GIsiSubBlockIter *iter, struct rat_info *rat)
+{
+       uint8_t len;
+
+       if (!g_isi_sb_iter_get_byte(iter, &rat->rat, 2))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &len, 3))
+               return FALSE;
+
+       if (len != 0 && !g_isi_sb_iter_get_byte(iter, &rat->compact, 4))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void rat_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       GIsiSubBlockIter iter;
+
+       if (nd == NULL || g_isi_msg_id(msg) != NET_RAT_IND)
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != NET_RAT_INFO)
+                       continue;
+
+               if (!parse_rat_info(&iter, &nd->rat))
+                       return;
+       }
+
+       ofono_netreg_status_notify(netreg, isi_status_to_at_status(&nd->reg),
+                                       nd->gsm.lac, nd->gsm.ci,
+                                       isi_to_at_tech(&nd->rat, &nd->gsm));
+}
+
+static void reg_status_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_netreg *netreg = cbd->user;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       ofono_netreg_status_cb_t cb = cbd->cb;
+
+       GIsiSubBlockIter iter;
+
+       if (!check_response_status(msg, NET_MODEM_REG_STATUS_GET_RESP) &&
+                       !check_response_status(msg, NET_REG_STATUS_GET_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case NET_REG_INFO_COMMON:
+
+                       if (!parse_common_info(&iter, &nd->reg))
+                               goto error;
+                       break;
+
+               case NET_GSM_REG_INFO:
+
+                       if (!parse_gsm_info(&iter, &nd->gsm))
+                               goto error;
+                       break;
+               }
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, isi_status_to_at_status(&nd->reg),
+                               nd->gsm.lac, nd->gsm.ci,
+                               isi_to_at_tech(&nd->rat, &nd->gsm),
+                               cbd->data);
+       g_free(cbd);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data);
+       g_free(cbd);
+}
+
+static void rat_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_netreg *netreg = cbd->user;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       ofono_netreg_status_cb_t cb = cbd->cb;
+
+       uint8_t req[] = {
+               NET_REG_STATUS_GET_REQ,
+       };
+       GIsiSubBlockIter iter;
+
+       if (cbd == NULL || nd == NULL)
+               goto error;
+
+       if (!check_response_status(msg, NET_RAT_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != NET_RAT_INFO)
+                       continue;
+
+               if (!parse_rat_info(&iter, &nd->rat))
+                       goto error;
+       }
+
+       if (g_isi_client_resource(nd->client) == PN_MODEM_NETWORK ||
+                       nd->version.major < 14)
+               req[0] = NET_MODEM_REG_STATUS_GET_REQ;
+
+       if (g_isi_client_send(nd->client, req, sizeof(req),
+                               reg_status_resp_cb, cbd, NULL))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
+       g_free(cbd);
+}
+
+static void isi_registration_status(struct ofono_netreg *netreg,
+                                       ofono_netreg_status_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data);
+
+       /*
+        * Current technology depends on the current RAT as well as
+        * the services reported by the current cell.  Therefore we
+        * need a pair of queries to deduce the full registration
+        * status: first query for the RAT then the actual
+        * registration status.
+        */
+       const uint8_t rat[] = {
+               NET_RAT_REQ,
+               NET_CURRENT_RAT
+       };
+
+       if (nd == NULL || cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(nd->client, rat, sizeof(rat),
+                               rat_resp_cb, cbd, NULL))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
+       g_free(cbd);
+}
+
+static void cell_info_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_netreg_operator_cb_t cb = cbd->cb;
+       struct ofono_netreg *netreg = cbd->user;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct ofono_network_operator op;
+       GIsiSubBlockIter iter;
+
+       memset(&op, 0, sizeof(struct ofono_network_operator));
+
+       if (!check_response_status(msg, NET_CELL_INFO_GET_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case NET_GSM_CELL_INFO:
+
+                       if (!g_isi_sb_iter_get_oper_code(&iter, op.mcc, op.mnc,
+                                                               12))
+                               goto error;
+
+                       op.tech = 0;
+                       break;
+
+               case NET_WCDMA_CELL_INFO:
+
+                       if (!g_isi_sb_iter_get_oper_code(&iter, op.mcc, op.mnc,
+                                                               12))
+                               goto error;
+
+                       op.tech = 2;
+                       break;
+               }
+       }
+
+       if (nd->nitz_name[0] != '\0')
+               strcpy(op.name, nd->nitz_name);
+
+       CALLBACK_WITH_SUCCESS(cb, &op, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void create_cell_info_get_req(struct ofono_netreg *netreg,
+                                       ofono_netreg_operator_cb_t cb,
+                                       void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data);
+
+       const uint8_t msg[] = {
+               NET_CELL_INFO_GET_REQ,
+       };
+
+       if (cbd == NULL || nd == NULL)
+               goto error;
+
+       if (g_isi_client_send(nd->client, msg, sizeof(msg), cell_info_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+       g_free(cbd);
+}
+
+static void name_get_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_netreg_operator_cb_t cb = cbd->cb;
+       struct ofono_network_operator op;
+
+       GIsiSubBlockIter iter;
+       uint8_t len = 0;
+       char *tag = NULL;
+
+       memset(&op, 0, sizeof(struct ofono_network_operator));
+
+       if (!check_response_status(msg, NET_OLD_OPER_NAME_READ_RESP) &&
+                       !check_response_status(msg, NET_OPER_NAME_READ_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 6);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case NET_GSM_OPERATOR_INFO:
+
+                       if (!g_isi_sb_iter_get_oper_code(&iter, op.mcc,
+                                                               op.mnc, 2))
+                               goto error;
+                       break;
+
+               case NET_OPER_NAME_INFO:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &len, 3))
+                               goto error;
+
+                       /* Name is UCS-2 encoded */
+                       len *= 2;
+
+                       if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag, len, 4))
+                               goto error;
+
+                       strncpy(op.name, tag, OFONO_MAX_OPERATOR_NAME_LENGTH);
+                       op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+                       g_free(tag);
+                       break;
+               }
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, &op, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void create_name_get_req(struct ofono_netreg *netreg,
+                                       ofono_netreg_operator_cb_t cb,
+                                       void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data);
+
+       uint8_t msg[] = {
+               NET_OPER_NAME_READ_REQ,
+               NET_HARDCODED_LATIN_OPER_NAME,
+               OFONO_MAX_OPERATOR_NAME_LENGTH,
+               0x00, 0x00,     /* Index not used */
+               0x00,           /* Filler */
+               0x00,           /* No sub-blocks */
+       };
+
+       if (cbd == NULL || nd == NULL)
+               goto error;
+
+       if (nd->version.major < 14)
+               msg[0] = NET_OLD_OPER_NAME_READ_REQ;
+
+       if (g_isi_client_send(nd->client, msg, sizeof(msg), name_get_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+       g_free(cbd);
+}
+
+static void isi_current_operator(struct ofono_netreg *netreg,
+                                       ofono_netreg_operator_cb_t cb,
+                                       void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (g_isi_client_resource(nd->client) == PN_MODEM_NETWORK)
+               create_cell_info_get_req(netreg, cb, data);
+       else
+               create_name_get_req(netreg, cb, data);
+}
+
+static void available_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_netreg_operator_list_cb_t cb = cbd->cb;
+       struct ofono_network_operator *list = NULL;
+
+       GIsiSubBlockIter iter;
+       uint8_t sb_count;
+
+       int total = 0;
+       int common = 0;
+       int detail = 0;
+
+       if (!check_response_status(msg, NET_MODEM_AVAILABLE_GET_RESP) &&
+                       !check_response_status(msg, NET_AVAILABLE_GET_RESP))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &sb_count))
+               goto error;
+
+       /* Each description of an operator has a pair of sub-blocks */
+       total = sb_count / 2;
+       list = alloca(total * sizeof(struct ofono_network_operator));
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               struct ofono_network_operator *op;
+               char *tag = NULL;
+               uint8_t taglen = 0;
+               uint8_t status = 0;
+               uint8_t umts = 0;
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case NET_MODEM_AVAIL_NETWORK_INFO_COMMON:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &status, 2))
+                               goto error;
+
+                       op = list + common++;
+                       op->status = status;
+
+                       /*
+                        * FIXME: PN_MODEM_NETWORK provides no name
+                        * tags. We need access to the GSMA name list
+                        * here, or alternatively, core has to fill in
+                        * the blanks.
+                        */
+                       op->name[0] = '\0';
+                       break;
+
+               case NET_AVAIL_NETWORK_INFO_COMMON:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &status, 2))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &taglen, 5))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag,
+                                                               taglen * 2, 6))
+                               goto error;
+
+                       op = list + common++;
+                       op->status = status;
+
+                       strncpy(op->name, tag, OFONO_MAX_OPERATOR_NAME_LENGTH);
+                       op->name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+                       g_free(tag);
+                       break;
+
+               /* case NET_MODEM_DETAILED_NETWORK_INFO: */
+               case NET_DETAILED_NETWORK_INFO:
+                       op = list + detail++;
+
+                       if (!g_isi_sb_iter_get_oper_code(&iter, op->mcc,
+                                                               op->mnc, 2))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &umts, 7))
+                               goto error;
+
+                       op->tech = umts ? 2 : 3;
+                       break;
+               }
+       }
+
+       if (common == detail && detail == total) {
+               CALLBACK_WITH_SUCCESS(cb, total, list, cbd->data);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
+}
+
+static void isi_list_operators(struct ofono_netreg *netreg,
+                               ofono_netreg_operator_list_cb_t cb,
+                               void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data);
+
+       uint8_t msg[] = {
+               NET_AVAILABLE_GET_REQ,
+               NET_MANUAL_SEARCH,
+               0x01,  /* Sub-block count */
+               NET_GSM_BAND_INFO,
+               0x04,  /* Sub-block length */
+               NET_GSM_BAND_ALL_SUPPORTED_BANDS,
+               0x00
+       };
+
+       if (cbd == NULL || nd == NULL)
+               goto error;
+
+       if (g_isi_client_resource(nd->client) == PN_MODEM_NETWORK ||
+                       nd->version.major < 14)
+               msg[0] = NET_MODEM_AVAILABLE_GET_REQ;
+
+       if (g_isi_client_send_with_timeout(nd->client, msg, sizeof(msg),
+                               NETWORK_SCAN_TIMEOUT, available_resp_cb, cbd,
+                               g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
+       g_free(cbd);
+}
+
+static void set_auto_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct netreg_data *nd = cbd->user;
+       ofono_netreg_register_cb_t cb = cbd->cb;
+
+       if (!check_response_status(msg, NET_SET_RESP)) {
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+               return;
+       }
+
+       nd->reg.mode = NET_SELECT_MODE_AUTOMATIC;
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static void isi_register_auto(struct ofono_netreg *netreg,
+                               ofono_netreg_register_cb_t cb,
+                               void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data);
+
+       const unsigned char msg[] = {
+               NET_SET_REQ,
+               0x00,  /* Registered in another protocol? */
+               0x01,  /* Sub-block count */
+               NET_OPERATOR_INFO_COMMON,
+               0x04,  /* Sub-block length */
+               nd->reg.mode == NET_SELECT_MODE_AUTOMATIC
+                       ? NET_SELECT_MODE_USER_RESELECTION
+                       : NET_SELECT_MODE_AUTOMATIC,
+               0x00  /* Index not used */
+       };
+
+       if (nd == NULL || cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send_with_timeout(nd->client, msg, sizeof(msg),
+                               NETWORK_SET_TIMEOUT,
+                               set_auto_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void set_manual_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_netreg *netreg = cbd->user;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       ofono_netreg_register_cb_t cb = cbd->cb;
+
+       if (!check_response_status(msg, NET_SET_RESP)) {
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+               return;
+       }
+
+       nd->reg.mode = NET_SELECT_MODE_MANUAL;
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static void isi_register_manual(struct ofono_netreg *netreg,
+                               const char *mcc, const char *mnc,
+                               ofono_netreg_register_cb_t cb, void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data);
+
+       guint8 buffer[3] = { 0 };
+       guint8 *bcd = mccmnc_to_bcd(mcc, mnc, buffer);
+
+       const unsigned char msg[] = {
+               NET_SET_REQ,
+               0x00,  /* Registered in another protocol? */
+               0x02,  /* Sub-block count */
+               NET_OPERATOR_INFO_COMMON,
+               0x04,  /* Sub-block length */
+               NET_SELECT_MODE_MANUAL,
+               0x00,  /* Index not used */
+               NET_GSM_OPERATOR_INFO,
+               0x08,  /* Sub-block length */
+               bcd[0], bcd[1], bcd[2],
+               NET_GSM_BAND_INFO_NOT_AVAIL,  /* Pick any supported band */
+               0x00, 0x00  /* Filler */
+       };
+
+       if (cbd == NULL || nd == NULL)
+               goto error;
+
+       if (g_isi_client_send_with_timeout(nd->client, msg, sizeof(msg),
+                               NETWORK_SET_TIMEOUT,
+                               set_manual_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void rssi_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       uint8_t rssi;
+
+       if (g_isi_msg_id(msg) != NET_RSSI_IND)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &rssi))
+               return;
+
+       ofono_netreg_strength_notify(netreg, rssi ? rssi : -1);
+}
+
+static gboolean parse_nettime(GIsiSubBlockIter *iter,
+                               struct ofono_network_time *info)
+{
+       struct network_time *time;
+       size_t len = sizeof(struct network_time);
+
+       if (!g_isi_sb_iter_get_struct(iter, (void **) &time, len, 2))
+               return FALSE;
+
+       /* Value is years since last turn of century */
+       info->year = time->year != NET_INVALID_TIME ? time->year : -1;
+       info->year += 2000;
+
+       info->mon = time->mon != NET_INVALID_TIME ? time->mon : -1;
+       info->mday = time->mday != NET_INVALID_TIME ? time->mday : -1;
+       info->hour = time->hour != NET_INVALID_TIME ? time->hour : -1;
+       info->min = time->min != NET_INVALID_TIME ? time->min : -1;
+       info->sec = time->sec != NET_INVALID_TIME ? time->sec : -1;
+
+       /*
+        * Most significant bit set indicates negative offset. The
+        * second most significant bit is 'reserved'. The value is the
+        * offset from UTCin a count of 15min intervals, possibly
+        * including the current DST adjustment.
+        */
+       info->utcoff = (time->utc & 0x3F) * 15 * 60;
+       if (time->utc & 0x80)
+               info->utcoff *= -1;
+
+       info->dst = time->dst != NET_INVALID_TIME ? time->dst : -1;
+       return TRUE;
+}
+
+static void time_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       struct ofono_network_time info;
+       GIsiSubBlockIter iter;
+
+       if (g_isi_msg_id(msg) != NET_TIME_IND)
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != NET_TIME_INFO)
+                       continue;
+
+               if (!parse_nettime(&iter, &info))
+                       return;
+
+               ofono_netreg_time_notify(netreg, &info);
+               return;
+       }
+}
+
+static void name_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       GIsiSubBlockIter iter;
+       char *tag;
+       uint8_t taglen;
+
+       if (g_isi_msg_id(msg) != NET_NITZ_NAME_IND)
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+               uint8_t id;
+
+               id = g_isi_sb_iter_get_id(&iter);
+               if (id != NET_FULL_NITZ_NAME && id != NET_SHORT_NITZ_NAME)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &taglen, 5))
+                       return;
+
+               if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag, taglen * 2, 7))
+                       return;
+
+               strncpy(nd->nitz_name, tag, OFONO_MAX_OPERATOR_NAME_LENGTH);
+               nd->nitz_name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+               g_free(tag);
+       }
+}
+
+static void rssi_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_netreg_strength_cb_t cb = cbd->cb;
+       uint8_t rssi;
+
+       GIsiSubBlockIter iter;
+
+       if (!check_response_status(msg, NET_RSSI_GET_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != NET_RSSI_CURRENT)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &rssi, 2))
+                       break;
+
+               CALLBACK_WITH_SUCCESS(cb, rssi ? rssi : -1, cbd->data);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void isi_strength(struct ofono_netreg *netreg,
+                               ofono_netreg_strength_cb_t cb,
+                               void *data)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data);
+
+       const uint8_t msg[] = {
+               NET_RSSI_GET_REQ,
+               NET_CS_GSM,
+               NET_CURRENT_CELL_RSSI,
+               0, 0, 0, 0,
+       };
+       size_t len = sizeof(msg);
+
+       if (nd == NULL || cbd == NULL)
+               goto error;
+
+       /* Filler is only required by PN_MODEM_NETWORK */
+       if (g_isi_client_resource(nd->client) != PN_MODEM_NETWORK)
+               len -= 4;
+
+       if (g_isi_client_send(nd->client, msg, len, rssi_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+       g_free(cbd);
+}
+
+static void cs_access_config_resp_cb(const GIsiMessage *msg, void *data)
+{
+       GIsiSubBlockIter iter;
+
+       DBG("");
+
+       if (g_isi_msg_id(msg) != NET_NW_ACCESS_CONF_RESP)
+               return;
+
+       /*
+        * TODO: Check that roaming and registration
+        * are now enabled.
+        */
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+               uint8_t id = g_isi_sb_iter_get_id(&iter);
+               uint8_t mode;
+
+               DBG("SB=%02X", id);
+
+               switch (id) {
+               case NET_REGISTRATION_CONF_INFO:
+               case NET_REGISTRATION_CONF1_INFO:
+                       g_isi_sb_iter_get_byte(&iter, &mode, 2);
+                       DBG("Reg %X", mode);
+                       break;
+
+               case NET_ROAMING_CONF_INFO:
+               case NET_ROAMING_CONF1_INFO:
+                       g_isi_sb_iter_get_byte(&iter, &mode, 2);
+                       DBG("Roam %X", mode);
+                       break;
+
+               default:
+                       DBG("Unknown subblock");
+               }
+       }
+}
+
+static void enable_registration(struct ofono_netreg *netreg)
+{
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       const uint8_t req[] = {
+               NET_NW_ACCESS_CONF_REQ, 0, 2,
+               /* Subblock 1 */
+               0x59, 4, 1, 0,
+               /* Subblock 2 */
+               0x5A, 4, 1, 0,
+       };
+
+       DBG("");
+       g_isi_client_send(nd->client, req, sizeof(req),
+                               cs_access_config_resp_cb, netreg, NULL);
+}
+
+static void activate_cs_and_enable_registration(struct ofono_netreg *netreg)
+{
+       DBG("not implemented");
+}
+
+static void cs_state_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       uint8_t code;
+
+       DBG("");
+
+       if (g_isi_msg_id(msg) != NET_CS_STATE_RESP)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &code))
+               return;
+
+       if (code != NET_CAUSE_OK) {
+               DBG("Failed with cause=%X", code);
+               return;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &code))
+               return;
+
+       DBG("CS STATE=%X", code);
+
+       if (code == NET_CS_INACTIVE)
+               activate_cs_and_enable_registration(netreg);
+       else
+               enable_registration(netreg);
+}
+
+static void subscribe_indications(GIsiClient *cl, void *data)
+{
+       g_isi_client_ind_subscribe(cl, NET_RSSI_IND, rssi_ind_cb, data);
+       g_isi_client_ind_subscribe(cl, NET_NITZ_NAME_IND, name_ind_cb, data);
+       g_isi_client_ind_subscribe(cl, NET_RAT_IND, rat_ind_cb, data);
+       g_isi_client_ind_subscribe(cl, NET_TIME_IND, time_ind_cb, data);
+       g_isi_client_ind_subscribe(cl, NET_REG_STATUS_IND, reg_status_ind_cb,
+                                       data);
+       g_isi_client_ind_subscribe(cl, NET_MODEM_REG_STATUS_IND,reg_status_ind_cb,
+                                       data);
+}
+
+static void pn_network_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("PN_NETWORK not reachable, removing client");
+               g_isi_client_destroy(nd->pn_network);
+               nd->pn_network = NULL;
+
+               if (nd->pn_modem_network == NULL)
+                       ofono_netreg_remove(netreg);
+
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       if (nd == NULL || nd->client != NULL)
+               return;
+
+       nd->client = nd->pn_network;
+
+       nd->version.major = g_isi_msg_version_major(msg);
+       nd->version.minor = g_isi_msg_version_minor(msg);
+
+       subscribe_indications(nd->client, netreg);
+
+       ofono_netreg_register(netreg);
+}
+
+static void pn_modem_network_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       const uint8_t req[] = {
+               NET_CS_STATE_REQ,
+       };
+
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("PN_MODEM_NETWORK not reachable, removing client");
+               g_isi_client_destroy(nd->pn_modem_network);
+               nd->pn_modem_network = NULL;
+
+               if (nd->pn_network == NULL)
+                       ofono_netreg_remove(netreg);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       if (nd == NULL || nd->client != NULL)
+               return;
+
+       nd->client = nd->pn_modem_network;
+
+       nd->version.major = g_isi_msg_version_major(msg);
+       nd->version.minor = g_isi_msg_version_minor(msg);
+
+       subscribe_indications(nd->client, netreg);
+
+       ofono_netreg_register(netreg);
+
+       g_isi_client_send(nd->client, req, sizeof(req), cs_state_resp_cb,
+                               netreg, NULL);
+}
+
+static int isi_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *modem = user;
+       struct netreg_data *nd;
+
+       nd = g_try_new0(struct netreg_data, 1);
+       if (nd == NULL)
+               return -ENOMEM;
+
+       nd->pn_network = g_isi_client_create(modem, PN_NETWORK);
+       if (nd->pn_network == NULL) {
+               g_free(nd);
+               return -ENOMEM;
+       }
+
+       nd->pn_modem_network = g_isi_client_create(modem, PN_MODEM_NETWORK);
+       if (nd->pn_modem_network == NULL) {
+               g_isi_client_destroy(nd->pn_network);
+               g_free(nd);
+               return -ENOMEM;
+       }
+
+       ofono_netreg_set_data(netreg, nd);
+
+       g_isi_client_verify(nd->pn_network, pn_network_reachable_cb,
+                               netreg, NULL);
+       g_isi_client_verify(nd->pn_modem_network, pn_modem_network_reachable_cb,
+                               netreg, NULL);
+
+       return 0;
+}
+
+static void isi_netreg_remove(struct ofono_netreg *netreg)
+{
+       struct netreg_data *data = ofono_netreg_get_data(netreg);
+
+       ofono_netreg_set_data(netreg, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->pn_modem_network);
+       g_isi_client_destroy(data->pn_network);
+       g_free(data);
+}
+
+static struct ofono_netreg_driver isimodem = {
+       .name                   = "isimodem",
+       .probe                  = isi_netreg_probe,
+       .remove                 = isi_netreg_remove,
+       .registration_status    = isi_registration_status,
+       .current_operator       = isi_current_operator,
+       .list_operators         = isi_list_operators,
+       .register_auto          = isi_register_auto,
+       .register_manual        = isi_register_manual,
+       .strength               = isi_strength,
+};
+
+void isi_netreg_init(void)
+{
+       ofono_netreg_driver_register(&isimodem);
+}
+
+void isi_netreg_exit(void)
+{
+       ofono_netreg_driver_unregister(&isimodem);
+}
diff --git a/drivers/isimodem/network.h b/drivers/isimodem/network.h
new file mode 100644 (file)
index 0000000..7449a1d
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_NETWORK_H
+#define __ISIMODEM_NETWORK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_NETWORK                                     0x0A
+#define PN_MODEM_NETWORK                               0xC8
+#define NETWORK_SCAN_TIMEOUT                           180
+#define NETWORK_SET_TIMEOUT                            240
+#define NET_INVALID_TIME                               0x64
+
+enum net_message_id {
+       NET_MODEM_REG_STATUS_GET_REQ =                  0x00,
+       NET_MODEM_REG_STATUS_GET_RESP =                 0x01,
+       NET_MODEM_REG_STATUS_IND =                      0x02,
+       NET_MODEM_AVAILABLE_GET_REQ =                   0x03,
+       NET_MODEM_AVAILABLE_GET_RESP =                  0x04,
+       NET_SET_REQ =                                   0x07,
+       NET_SET_RESP =                                  0x08,
+       NET_RSSI_GET_REQ =                              0x0B,
+       NET_RSSI_GET_RESP =                             0x0C,
+       NET_CS_STATE_IND =                              0x19,
+       NET_RSSI_IND =                                  0x1E,
+       NET_CIPHERING_IND =                             0x20,
+       NET_TIME_IND =                                  0x27,
+       NET_OLD_OPER_NAME_READ_REQ =                    0x28,
+       NET_OLD_OPER_NAME_READ_RESP =                   0x29,
+       NET_CHANNEL_INFO_IND =                          0x2C,
+       NET_RAT_IND =                                   0x35,
+       NET_RAT_REQ =                                   0x36,
+       NET_RAT_RESP =                                  0x37,
+       NET_CS_STATE_REQ =                              0x3A,
+       NET_CS_STATE_RESP =                             0x3B,
+       NET_CELL_INFO_GET_REQ =                         0x40,
+       NET_CELL_INFO_GET_RESP =                        0x41,
+       NET_CELL_INFO_IND =                             0x42,
+       NET_NITZ_NAME_IND =                             0x43,
+       NET_NW_ACCESS_CONF_REQ =                        0x48,
+       NET_NW_ACCESS_CONF_RESP =                       0x49,
+       NET_REG_STATUS_GET_REQ =                        0xE0,
+       NET_REG_STATUS_GET_RESP =                       0xE1,
+       NET_REG_STATUS_IND =                            0xE2,
+       NET_AVAILABLE_GET_REQ =                         0xE3,
+       NET_AVAILABLE_GET_RESP =                        0xE4,
+       NET_OPER_NAME_READ_REQ =                        0xE5,
+       NET_OPER_NAME_READ_RESP =                       0xE6,
+};
+
+enum net_subblock {
+       NET_REG_INFO_COMMON =                           0x00,
+       NET_MODEM_AVAIL_NETWORK_INFO_COMMON =           0x01,
+       NET_OPERATOR_INFO_COMMON =                      0x02,
+       NET_RSSI_CURRENT =                              0x04,
+       NET_GSM_REG_INFO =                              0x09,
+       NET_DETAILED_NETWORK_INFO =                     0x0B,
+       NET_MODEM_DETAILED_NETWORK_INFO =               0x0B,
+       NET_GSM_OPERATOR_INFO =                         0x0C,
+       NET_TIME_INFO =                                 0x10,
+       NET_GSM_BAND_INFO =                             0x11,
+       NET_RAT_INFO =                                  0x2C,
+       NET_GSM_CELL_INFO =                             0x46,
+       NET_WCDMA_CELL_INFO =                           0x47,
+       NET_FULL_NITZ_NAME =                            0x48,
+       NET_SHORT_NITZ_NAME =                           0x49,
+       NET_REGISTRATION_CONF_INFO =                    0x55,
+       NET_ROAMING_CONF_INFO =                         0x56,
+       NET_REGISTRATION_CONF1_INFO =                   0x59,
+       NET_ROAMING_CONF1_INFO =                        0x5A,
+       NET_AVAIL_NETWORK_INFO_COMMON =                 0xE1,
+       NET_OPER_NAME_INFO =                            0xE7,
+};
+
+enum net_reg_status {
+       NET_REG_STATUS_HOME =                           0x00,
+       NET_REG_STATUS_ROAM =                           0x01,
+       NET_REG_STATUS_ROAM_BLINK =                     0x02,
+       NET_REG_STATUS_NOSERV =                         0x03,
+       NET_REG_STATUS_NOSERV_SEARCHING =               0x04,
+       NET_REG_STATUS_NOSERV_NOTSEARCHING =            0x05,
+       NET_REG_STATUS_NOSERV_NOSIM =                   0x06,
+       NET_REG_STATUS_POWER_OFF =                      0x08,
+       NET_REG_STATUS_NSPS =                           0x09,
+       NET_REG_STATUS_NSPS_NO_COVERAGE =               0x0A,
+       NET_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW =      0x0B,
+};
+
+enum net_network_status {
+       NET_OPER_STATUS_UNKNOWN =                       0x00,
+       NET_OPER_STATUS_AVAILABLE =                     0x01,
+       NET_OPER_STATUS_CURRENT =                       0x02,
+       NET_OPER_STATUS_FORBIDDEN =                     0x03,
+};
+
+enum net_network_pref {
+       NET_GSM_HOME_PLMN =                             0x00,
+       NET_GSM_PREFERRED_PLMN =                        0x01,
+       NET_GSM_FORBIDDEN_PLMN =                        0x02,
+       NET_GSM_OTHER_PLMN =                            0x03,
+       NET_GSM_NO_PLMN_AVAIL =                         0x04,
+};
+
+enum net_umts_available {
+       NET_UMTS_NOT_AVAILABLE =                        0x00,
+       NET_UMTS_AVAILABLE =                            0x01,
+};
+
+enum net_band_info {
+       NET_GSM_BAND_900_1800 =                         0x00,
+       NET_GSM_BAND_850_1900 =                         0x01,
+       NET_GSM_BAND_INFO_NOT_AVAIL =                   0x02,
+       NET_GSM_BAND_ALL_SUPPORTED_BANDS =              0x03,
+       NET_GSM_BAND_850_LOCKED =                       0xB0,
+       NET_GSM_BAND_900_LOCKED =                       0xA0,
+       NET_GSM_BAND_1800_LOCKED =                      0xA1,
+       NET_GSM_BAND_1900_LOCKED =                      0xB1,
+};
+
+enum net_gsm_cause {
+       NET_GSM_IMSI_UNKNOWN_IN_HLR =                   0x02,
+       NET_GSM_ILLEGAL_MS =                            0x03,
+       NET_GSM_IMSI_UNKNOWN_IN_VLR =                   0x04,
+       NET_GSM_IMEI_NOT_ACCEPTED =                     0x05,
+       NET_GSM_ILLEGAL_ME =                            0x06,
+       NET_GSM_GPRS_SERVICES_NOT_ALLOWED =             0x07,
+       NET_GSM_GPRS_AND_NON_GPRS_NA =                  0x08,
+       NET_GSM_MS_ID_CANNOT_BE_DERIVED =               0x09,
+       NET_GSM_IMPLICITLY_DETACHED =                   0x0A,
+       NET_GSM_PLMN_NOT_ALLOWED =                      0x0B,
+       NET_GSM_LA_NOT_ALLOWED =                        0x0C,
+       NET_GSM_ROAMING_NOT_IN_THIS_LA =                0x0D,
+       NET_GSM_GPRS_SERV_NA_IN_THIS_PLMN =             0x0E,
+       NET_GSM_NO_SUITABLE_CELLS_IN_LA =               0x0F,
+       NET_GSM_MSC_TEMP_NOT_REACHABLE =                0x10,
+       NET_GSM_NETWORK_FAILURE =                       0x11,
+       NET_GSM_MAC_FAILURE =                           0x14,
+       NET_GSM_SYNCH_FAILURE =                         0x15,
+       NET_GSM_CONGESTION =                            0x16,
+       NET_GSM_AUTH_UNACCEPTABLE =                     0x17,
+       NET_GSM_SERV_OPT_NOT_SUPPORTED =                0x20,
+       NET_GSM_SERV_OPT_NOT_SUBSCRIBED =               0x21,
+       NET_GSM_SERV_TEMP_OUT_OF_ORDER =                0x22,
+       NET_GSM_RETRY_ENTRY_NEW_CELL_LOW =              0x30,
+       NET_GSM_RETRY_ENTRY_NEW_CELL_HIGH =             0x3F,
+       NET_GSM_SEMANTICALLY_INCORRECT =                0x5F,
+       NET_GSM_INVALID_MANDATORY_INFO =                0x60,
+       NET_GSM_MSG_TYPE_NONEXISTENT =                  0x61,
+       NET_GSM_CONDITIONAL_IE_ERROR =                  0x64,
+       NET_GSM_MSG_TYPE_WRONG_STATE =                  0x65,
+       NET_GSM_PROTOCOL_ERROR_UNSPECIFIED =            0x6F,
+};
+
+enum net_cs_type {
+       NET_CS_GSM =                                    0x00,
+};
+
+enum net_rat_name {
+       NET_GSM_RAT =                                   0x01,
+       NET_UMTS_RAT =                                  0x02
+};
+
+enum net_rat_type {
+       NET_CURRENT_RAT =                               0x00,
+       NET_SUPPORTED_RATS =                            0x01,
+};
+
+enum net_measurement_type {
+       NET_CURRENT_CELL_RSSI =                         0x02,
+};
+
+enum net_search_mode {
+       NET_MANUAL_SEARCH =                             0x00,
+};
+
+enum net_oper_name_type {
+       NET_HARDCODED_LATIN_OPER_NAME =                 0x00,
+};
+
+enum net_select_mode {
+       NET_SELECT_MODE_UNKNOWN =                       0x00,
+       NET_SELECT_MODE_MANUAL =                        0x01,
+       NET_SELECT_MODE_AUTOMATIC =                     0x02,
+       NET_SELECT_MODE_USER_RESELECTION =              0x03,
+       NET_SELECT_MODE_NO_SELECTION =                  0x04,
+};
+
+enum net_cs_states {
+       NET_CS_INACTIVE =               0x00,
+       NET_CS_ACTIVE =         0x01,
+};
+
+enum net_isi_cause {
+       NET_CAUSE_OK =                                  0x00,
+       NET_CAUSE_COMMUNICATION_ERROR =                 0x01,
+       NET_CAUSE_INVALID_PARAMETER =                   0x02,
+       NET_CAUSE_NO_SIM =                              0x03,
+       NET_CAUSE_SIM_NOT_YET_READY =                   0x04,
+       NET_CAUSE_NET_NOT_FOUND =                       0x05,
+       NET_CAUSE_REQUEST_NOT_ALLOWED =                 0x06,
+       NET_CAUSE_CALL_ACTIVE =                         0x07,
+       NET_CAUSE_SERVER_BUSY =                         0x08,
+       NET_CAUSE_SECURITY_CODE_REQUIRED =              0x09,
+       NET_CAUSE_NOTHING_TO_CANCEL =                   0x0A,
+       NET_CAUSE_UNABLE_TO_CANCEL =                    0x0B,
+       NET_CAUSE_NETWORK_FORBIDDEN =                   0x0C,
+       NET_CAUSE_REQUEST_REJECTED =                    0x0D,
+       NET_CAUSE_CS_NOT_SUPPORTED =                    0x0E,
+       NET_CAUSE_PAR_INFO_NOT_AVAILABLE =              0x0F,
+       NET_CAUSE_NOT_DONE =                            0x10,
+       NET_CAUSE_NO_SELECTED_NETWORK =                 0x11,
+       NET_CAUSE_REQUEST_INTERRUPTED =                 0x12,
+       NET_CAUSE_TOO_BIG_INDEX =                       0x14,
+       NET_CAUSE_MEMORY_FULL =                         0x15,
+       NET_CAUSE_SERVICE_NOT_ALLOWED =                 0x16,
+       NET_CAUSE_NOT_SUPPORTED_IN_TECH =               0x17,
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !__ISIMODEM_NETWORK_H */
diff --git a/drivers/isimodem/phonebook.c b/drivers/isimodem/phonebook.c
new file mode 100644 (file)
index 0000000..47b34a8
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gisi/client.h>
+#include <gisi/message.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/phonebook.h>
+#include "util.h"
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "sim.h"
+#include "debug.h"
+
+struct pb_data {
+       GIsiClient *client;
+};
+
+struct read_resp {
+       uint8_t service_type;
+       uint8_t sb_count;
+       uint8_t data[];
+};
+
+static gboolean parse_adn(GIsiSubBlockIter *iter, uint16_t *location,
+                               char **name, char **number)
+{
+       uint8_t namelen;
+       uint8_t numlen;
+
+       if (!g_isi_sb_iter_get_word(iter, location, 4) ||
+                       !g_isi_sb_iter_get_byte(iter, &namelen, 6) ||
+                       !g_isi_sb_iter_get_byte(iter, &numlen, 7))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_alpha_tag(iter, name, namelen * 2, 8))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_alpha_tag(iter, number, numlen * 2,
+                       8 + namelen * 2)) {
+               g_free(*name);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static gboolean parse_sne(GIsiSubBlockIter *iter, char **sne)
+{
+       uint8_t len;
+
+       if (!g_isi_sb_iter_get_byte(iter, &len, 6))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_alpha_tag(iter, sne, len * 2, 8))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean parse_anr(GIsiSubBlockIter *iter, char **anr)
+{
+       uint8_t len;
+
+       if (!g_isi_sb_iter_get_byte(iter, &len, 6))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_alpha_tag(iter, anr, len * 2, 8))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean parse_email(GIsiSubBlockIter *iter, char **email)
+{
+       uint8_t len;
+
+       if (!g_isi_sb_iter_get_byte(iter, &len, 6))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_alpha_tag(iter, email, len * 2, 8))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean decode_response(const GIsiMessage *msg, uint16_t *location,
+                               void *data)
+{
+       struct ofono_phonebook *pb = data;
+       const struct read_resp *resp = g_isi_msg_data(msg);
+       size_t len = g_isi_msg_data_len(msg);
+
+       GIsiSubBlockIter iter;
+
+       char *name = NULL;
+       char *number = NULL;
+       char *sne = NULL;
+       char *anr = NULL;
+       char *email = NULL;
+
+       uint8_t status = 0;
+       gboolean success = FALSE;
+
+       if (g_isi_msg_id(msg) != SIM_PB_RESP_SIM_PB_READ ||
+                       resp == NULL || len < sizeof(struct read_resp) ||
+                       resp->service_type != SIM_PB_READ)
+               return FALSE;
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 2, TRUE, resp->sb_count);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case SIM_PB_ADN:
+
+                       if (!parse_adn(&iter, location, &name, &number))
+                               goto error;
+                       success = TRUE;
+                       break;
+
+               case SIM_PB_SNE:
+
+                       if (!parse_sne(&iter, &sne))
+                               goto error;
+                       break;
+
+               case SIM_PB_ANR:
+
+                       if (!parse_anr(&iter, &anr))
+                               goto error;
+                       break;
+
+               case SIM_PB_EMAIL:
+
+                       if (!parse_email(&iter, &email))
+                               goto error;
+                       break;
+
+               case SIM_PB_STATUS:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &status, 4))
+                               goto error;
+                       break;
+
+               default:
+                       DBG("Skipping sub-block: %s (%zd bytes)",
+                               sim_subblock_name(g_isi_sb_iter_get_id(&iter)),
+                               g_isi_sb_iter_get_len(&iter));
+                       break;
+               }
+       }
+
+       if (status == SIM_SERV_OK)
+               ofono_phonebook_entry(pb, -1, number, -1, name, -1, NULL,
+                                       anr, -1, sne, email, NULL, NULL);
+error:
+       g_free(name);
+       g_free(number);
+       g_free(sne);
+       g_free(anr);
+       g_free(email);
+
+       return success;
+}
+
+static void read_next_entry(GIsiClient *client, uint16_t location,
+                               GIsiNotifyFunc notify, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_phonebook_cb_t cb = cbd->cb;
+       const uint8_t msg[] = {
+               SIM_PB_REQ_SIM_PB_READ,
+               SIM_PB_READ,
+               2,                              /* number of subblocks */
+               0, SIM_PB_LOCATION_SEARCH,      /* subblock id */
+               0, 8,                           /* subblock size */
+               0, SIM_PB_ADN,
+               location >> 8, location & 0xFF, /* read next entry after
+                                                * specified by location */
+               0, SIM_PB_INFO_REQUEST,         /* subblock id */
+               0, 16,                          /* subblock size */
+               4,                              /* number of tags */
+               0,                              /* filler */
+               0, SIM_PB_ADN,                  /* tags */
+               0, SIM_PB_SNE,
+               0, SIM_PB_ANR,
+               0, SIM_PB_EMAIL,
+               0, 0                            /* filler */
+       };
+
+       if (cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(client, msg, sizeof(msg), notify, cbd, NULL))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+       g_free(cbd);
+}
+
+static void read_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_phonebook *pb = cbd->user;
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+       ofono_phonebook_cb_t cb = cbd->cb;
+       uint16_t location;
+
+       if (g_isi_msg_error(msg) < 0) {
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+               g_free(cbd);
+               return;
+       }
+
+       if (decode_response(msg, &location, cbd->user)) {
+               read_next_entry(pbd->client, location, read_resp_cb, cbd);
+               return;
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       g_free(cbd);
+}
+
+static void isi_export_entries(struct ofono_phonebook *pb, const char *storage,
+                               ofono_phonebook_cb_t cb, void *data)
+{
+       struct pb_data *pbd = ofono_phonebook_get_data(pb);
+       struct isi_cb_data *cbd = isi_cb_data_new(pb, cb, data);
+       const uint8_t msg[] = {
+               SIM_PB_REQ_SIM_PB_READ,
+               SIM_PB_READ,
+               2,                              /* number of subblocks */
+               0, SIM_PB_LOCATION,             /* subblock id */
+               0, 8,                           /* subblock size */
+               0, SIM_PB_ADN,
+               0xFF, 0xFF,                     /* read first entry in pb */
+               0, SIM_PB_INFO_REQUEST,         /* subblock id */
+               0, 16,                          /* subblock size */
+               4,                              /* number of tags */
+               0,                              /* filler */
+               0, SIM_PB_ADN,                  /* tags */
+               0, SIM_PB_SNE,
+               0, SIM_PB_ANR,
+               0, SIM_PB_EMAIL,
+               0, 0                            /* filler */
+       };
+       size_t len = sizeof(msg);
+
+       if (cbd == NULL || pbd == NULL || strcmp(storage, "SM") != 0)
+               goto error;
+
+       if (g_isi_client_send(pbd->client, msg, len, read_resp_cb, cbd, NULL))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_phonebook *pb = data;
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_phonebook_remove(pb);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       ofono_phonebook_register(pb);
+}
+
+static int isi_phonebook_probe(struct ofono_phonebook *pb, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *modem = user;
+       struct pb_data *data;
+
+       data = g_try_new0(struct pb_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->client = g_isi_client_create(modem, PN_SIM);
+       if (data->client == NULL) {
+               g_free(data);
+               return -ENOMEM;
+       }
+
+       ofono_phonebook_set_data(pb, data);
+
+       g_isi_client_verify(data->client, reachable_cb, pb, NULL);
+
+       return 0;
+}
+
+static void isi_phonebook_remove(struct ofono_phonebook *pb)
+{
+       struct pb_data *data = ofono_phonebook_get_data(pb);
+
+       ofono_phonebook_set_data(pb, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_phonebook_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_phonebook_probe,
+       .remove                 = isi_phonebook_remove,
+       .export_entries         = isi_export_entries
+};
+
+void isi_phonebook_init(void)
+{
+       ofono_phonebook_driver_register(&driver);
+}
+
+void isi_phonebook_exit(void)
+{
+       ofono_phonebook_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/radio-settings.c b/drivers/isimodem/radio-settings.c
new file mode 100644 (file)
index 0000000..2c4989c
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "debug.h"
+#include "gpds.h"
+#include "gss.h"
+#include "network.h"
+
+struct radio_data {
+       GIsiClient *gss_client;
+       GIsiClient *gpds_client;
+       GIsiClient *wran_client;
+       uint16_t wran_object;
+       uint16_t quick_release:1;
+};
+
+static enum ofono_radio_access_mode isi_mode_to_ofono_mode(guint8 mode)
+{
+       switch (mode) {
+       case GSS_DUAL_RAT:
+               return OFONO_RADIO_ACCESS_MODE_ANY;
+       case GSS_GSM_RAT:
+               return OFONO_RADIO_ACCESS_MODE_GSM;
+       case GSS_UMTS_RAT:
+               return OFONO_RADIO_ACCESS_MODE_UMTS;
+       default:
+               return -1;
+       }
+}
+
+static int ofono_mode_to_isi_mode(enum ofono_radio_access_mode mode)
+{
+       switch (mode) {
+       case OFONO_RADIO_ACCESS_MODE_ANY:
+               return GSS_DUAL_RAT;
+       case OFONO_RADIO_ACCESS_MODE_GSM:
+               return GSS_GSM_RAT;
+       case OFONO_RADIO_ACCESS_MODE_UMTS:
+               return GSS_UMTS_RAT;
+       default:
+               return -1;
+       }
+}
+
+static void rat_mode_read_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+       int mode = -1;
+       GIsiSubBlockIter iter;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("message error");
+               goto error;
+       }
+
+       if (g_isi_msg_id(msg) == GSS_CS_SERVICE_FAIL_RESP)
+               goto error;
+
+       if (g_isi_msg_id(msg) != GSS_CS_SERVICE_RESP)
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+               g_isi_sb_iter_is_valid(&iter);
+               g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+
+               case GSS_RAT_INFO: {
+                       guint8 info;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &info, 2))
+                               goto error;
+
+                       mode = isi_mode_to_ofono_mode(info);
+
+                       break;
+               }
+               default:
+                       DBG("Skipping sub-block: %s (%zu bytes)",
+                               gss_subblock_name(
+                                       g_isi_sb_iter_get_id(&iter)),
+                               g_isi_sb_iter_get_len(&iter));
+                       break;
+               }
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, mode, cbd->data);
+       g_free(cbd);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+       g_free(cbd);
+       return;
+}
+
+static void isi_query_rat_mode(struct ofono_radio_settings *rs,
+                               ofono_radio_settings_rat_mode_query_cb_t cb,
+                               void *data)
+{
+       struct radio_data *rd = ofono_radio_settings_get_data(rs);
+       struct isi_cb_data *cbd = isi_cb_data_new(rd, cb, data);
+
+       const unsigned char msg[] = {
+               GSS_CS_SERVICE_REQ,
+               GSS_SELECTED_RAT_READ,
+               0x00 /* subblock count */
+       };
+
+       if (cbd == NULL || rd == NULL)
+               goto error;
+
+       if (g_isi_client_send(rd->gss_client, msg, sizeof(msg),
+                               rat_mode_read_resp_cb, cbd, NULL))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+       g_free(cbd);
+}
+
+static void mode_write_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("message error");
+               goto error;
+       }
+
+       if (g_isi_msg_id(msg) == GSS_CS_SERVICE_FAIL_RESP)
+               goto error;
+
+       if (g_isi_msg_id(msg) != GSS_CS_SERVICE_RESP)
+               return;
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       g_free(cbd);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+       g_free(cbd);
+       return;
+}
+
+static void isi_set_rat_mode(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode,
+                               ofono_radio_settings_rat_mode_set_cb_t cb,
+                               void *data)
+{
+       struct radio_data *rd = ofono_radio_settings_get_data(rs);
+       struct isi_cb_data *cbd = isi_cb_data_new(rd, cb, data);
+       int isi_mode = ofono_mode_to_isi_mode(mode);
+
+       const unsigned char msg[] = {
+               GSS_CS_SERVICE_REQ,
+               GSS_SELECTED_RAT_WRITE,
+               0x01, /* subblock count */
+               GSS_RAT_INFO,
+               0x04, /* subblock length */
+               isi_mode,
+               0x00 /* filler */
+       };
+
+       if (cbd == NULL || rd == NULL)
+               goto error;
+
+       if (isi_mode == -1)
+               goto error;
+
+       if (g_isi_client_send(rd->gss_client, msg, sizeof(msg),
+                               mode_write_resp_cb, cbd, NULL))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void update_fast_dormancy(struct radio_data *rd)
+{
+       GIsiModem *modem;
+
+       struct sockaddr_pn dst = {
+               .spn_family = AF_PHONET,
+               .spn_resource = 0x3a,
+               .spn_dev = rd->wran_object >> 8,
+               .spn_obj = rd->wran_object & 0xff,
+       };
+
+       if (!rd->wran_object)
+               return;
+
+       modem = g_isi_client_modem(rd->wran_client);
+
+       if (rd->quick_release) {
+               const unsigned char msg[] = {
+                       0x00, 0x1f, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+                       0x00, 0x00, 0x00, 0x00
+               };
+
+               g_isi_modem_sendto(modem, &dst, msg, sizeof(msg));
+       } else {
+               const unsigned char msg[] = {
+                       0x00, 0x1f, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02,
+                       0x00, 0x0a, 0x00, 0x00
+               };
+
+               g_isi_modem_sendto(modem, &dst, msg, sizeof(msg));
+       }
+
+       DBG("3G PS quick release %s",
+               rd->quick_release ? "enabled" : "disabled");
+}
+
+static void gpds_context_activating_ind_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct radio_data *rd = opaque;
+       update_fast_dormancy(rd);
+}
+
+static void isi_query_fast_dormancy(struct ofono_radio_settings *rs,
+                       ofono_radio_settings_fast_dormancy_query_cb_t cb,
+                       void *data)
+{
+       struct radio_data *rd = ofono_radio_settings_get_data(rs);
+       CALLBACK_WITH_SUCCESS(cb, rd->quick_release, data);
+}
+
+static void isi_set_fast_dormancy(struct ofono_radio_settings *rs,
+                               ofono_bool_t enable,
+                               ofono_radio_settings_fast_dormancy_set_cb_t cb,
+                               void *data)
+{
+       struct radio_data *rd = ofono_radio_settings_get_data(rs);
+       rd->quick_release = enable;
+       update_fast_dormancy(rd);
+       CALLBACK_WITH_SUCCESS(cb, data);
+}
+
+static void wran_reachable_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct radio_data *rd = opaque;
+
+       if (g_isi_msg_error(msg) < 0)
+               return;
+
+       ISI_RESOURCE_DBG(msg);
+
+       rd->wran_object = g_isi_msg_object(msg);
+
+       DBG("PN_WRAN object = 0x%04x", rd->wran_object);
+
+       update_fast_dormancy(rd);
+
+       g_isi_client_ind_subscribe(rd->gpds_client,
+                                       GPDS_CONTEXT_ACTIVATING_IND,
+                                       gpds_context_activating_ind_cb, rd);
+}
+
+static void gss_reachable_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_radio_settings *rs = opaque;
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_radio_settings_remove(rs);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       ofono_radio_settings_register(rs);
+}
+
+static int isi_radio_settings_probe(struct ofono_radio_settings *rs,
+                                       unsigned int vendor,
+                                       void *user)
+{
+       GIsiModem *modem = user;
+       struct radio_data *rd = g_try_new0(struct radio_data, 1);
+
+       if (rd == NULL)
+               return -ENOMEM;
+
+       rd->gss_client = g_isi_client_create(modem, PN_GSS);
+       if (rd->gss_client == NULL)
+               goto nomem;
+
+       rd->gpds_client = g_isi_client_create(modem, PN_GPDS);
+       if (rd->gpds_client == NULL)
+               goto nomem;
+
+       rd->wran_client = g_isi_client_create(modem, PN_WRAN);
+       if (rd->wran_client == NULL)
+               goto nomem;
+
+       ofono_radio_settings_set_data(rs, rd);
+
+       g_isi_client_verify(rd->gss_client, gss_reachable_cb, rs, NULL);
+       g_isi_client_verify(rd->wran_client, wran_reachable_cb, rd, NULL);
+
+       return 0;
+nomem:
+       g_isi_client_destroy(rd->gss_client);
+       g_isi_client_destroy(rd->wran_client);
+       g_isi_client_destroy(rd->gpds_client);
+       g_free(rd);
+       return -ENOMEM;
+}
+
+static void isi_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+       struct radio_data *rd = ofono_radio_settings_get_data(rs);
+
+       ofono_radio_settings_set_data(rs, NULL);
+
+       if (rd == NULL)
+               return;
+
+       g_isi_client_destroy(rd->gss_client);
+       g_isi_client_destroy(rd->wran_client);
+       g_isi_client_destroy(rd->gpds_client);
+       g_free(rd);
+}
+
+static struct ofono_radio_settings_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_radio_settings_probe,
+       .remove                 = isi_radio_settings_remove,
+       .query_rat_mode         = isi_query_rat_mode,
+       .set_rat_mode           = isi_set_rat_mode,
+       .query_fast_dormancy    = isi_query_fast_dormancy,
+       .set_fast_dormancy      = isi_set_fast_dormancy,
+};
+
+void isi_radio_settings_init(void)
+{
+       ofono_radio_settings_driver_register(&driver);
+}
+
+void isi_radio_settings_exit(void)
+{
+       ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/sim.c b/drivers/isimodem/sim.c
new file mode 100644 (file)
index 0000000..b316391
--- /dev/null
@@ -0,0 +1,963 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/message.h>
+#include <gisi/client.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sim.h>
+
+#include "ofono.h"
+#include "simutil.h"
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "sim.h"
+#include "debug.h"
+
+#define SIM_MAX_SPN_LENGTH     16
+
+struct sim_data {
+       GIsiClient *client;
+       GIsiClient *sec_client;
+       enum ofono_sim_password_type passwd_state;
+       ofono_bool_t ready;
+       ofono_bool_t notify_ready;
+};
+
+struct sim_imsi {
+       uint8_t length;
+       uint8_t imsi[8];
+};
+
+struct sim_iccid {
+       uint8_t id[10];
+};
+
+struct sim_spn {
+       uint16_t name[SIM_MAX_SPN_LENGTH + 1];
+       uint8_t disp_home;
+       uint8_t disp_roam;
+};
+
+struct file_info {
+       int fileid;
+       int length;
+       int structure;
+       int record_length;
+       uint8_t access[3];
+       uint8_t file_status;
+};
+
+static int sim_resp_status(const GIsiMessage *msg, uint8_t msgid,
+                               uint8_t service)
+{
+       uint8_t type = 0;
+       uint8_t status;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return -1;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       sim_message_id_name(g_isi_msg_id(msg)));
+               return -1;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &status) ||
+                       !g_isi_msg_data_get_byte(msg, 0, &type)) {
+               DBG("Runt msg: %s", sim_message_id_name(msgid));
+               return -1;
+       }
+
+       if (status != SIM_SERV_OK)
+               DBG("Request failed: %s", sim_isi_cause_name(status));
+
+       if (type != service) {
+               DBG("Unexpected service: 0x%02X", type);
+               return -1;
+       }
+
+       return status;
+}
+
+/* Returns file info */
+static gboolean fake_file_info(gpointer user)
+{
+       struct isi_cb_data *cbd = user;
+       ofono_sim_file_info_cb_t cb = cbd->cb;
+       struct file_info const *fi = cbd->user;
+
+       DBG("Returning static file info for %04X", fi->fileid);
+       CALLBACK_WITH_SUCCESS(cb, fi->length, fi->structure, fi->record_length,
+                               fi->access, fi->file_status, cbd->data);
+       g_free(cbd);
+       return FALSE;
+}
+
+static void isi_read_file_info(struct ofono_sim *sim, int fileid,
+                               ofono_sim_file_info_cb_t cb, void *data)
+{
+       int i;
+       static struct file_info const info[] = {
+               { SIM_EFSPN_FILEID, 17, 0, 0, { 0x0f, 0xff, 0xff }, 1 },
+               { SIM_EF_ICCID_FILEID, 10, 0, 0, { 0x0f, 0xff, 0xff }, 1 },
+       };
+       int N = sizeof(info) / sizeof(info[0]);
+       struct isi_cb_data *cbd;
+
+       for (i = 0; i < N; i++) {
+               if (fileid == info[i].fileid) {
+                       cbd = isi_cb_data_new((void *) &info[i], cb, data);
+                       g_idle_add(fake_file_info, cbd);
+                       return;
+               }
+       }
+
+       DBG("Fileid %04X not implemented", fileid);
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, data);
+}
+
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid,
+                                       uint8_t service)
+{
+       return sim_resp_status(msg, msgid, service) == SIM_SERV_OK;
+}
+
+static void spn_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sim_read_cb_t cb = cbd->cb;
+
+       const struct sim_spn *resp = NULL;
+       size_t len = sizeof(struct sim_spn);
+
+       uint8_t spn[SIM_MAX_SPN_LENGTH + 1];
+       int i;
+
+       if (!check_response_status(msg, SIM_SERV_PROV_NAME_RESP,
+                                       SIM_ST_READ_SERV_PROV_NAME))
+               goto error;
+
+       if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &resp, len))
+               goto error;
+
+       /* Set display condition bits */
+       spn[0] = (resp->disp_home & 0x01) | ((resp->disp_roam & 0x01) << 1);
+
+       /* Convert from a NULL-terminated UCS-2 string to ASCII */
+       for (i = 0; i < SIM_MAX_SPN_LENGTH; i++) {
+               uint16_t c = resp->name[i] >> 8 | resp->name[i] << 8;
+
+               if (c == 0)
+                       c = 0xFF;
+               else if (!g_ascii_isprint(c))
+                       c = '?';
+
+               spn[i + 1] = c;
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, spn, sizeof(spn), cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
+}
+
+static gboolean isi_read_spn(struct ofono_sim *sim, struct isi_cb_data *cbd)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       const uint8_t msg[] = {
+               SIM_SERV_PROV_NAME_REQ,
+               SIM_ST_READ_SERV_PROV_NAME,
+               0
+       };
+
+       return g_isi_client_send(sd->client, msg, sizeof(msg),
+                                       spn_resp_cb, cbd, g_free);
+}
+
+static void read_iccid_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sim_read_cb_t cb = cbd->cb;
+       struct sim_iccid *icc;
+       size_t len = sizeof(struct sim_iccid);
+
+       if (!check_response_status(msg, SIM_READ_FIELD_RESP, ICC))
+               goto error;
+
+       if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &icc, len))
+               goto error;
+
+       CALLBACK_WITH_SUCCESS(cb, icc->id, 10, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
+}
+
+static gboolean isi_read_iccid(struct ofono_sim *sim, struct isi_cb_data *cbd)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       const uint8_t req[] = {
+               SIM_READ_FIELD_REQ,
+               ICC,
+       };
+
+       return g_isi_client_send(sd->client, req, sizeof(req),
+                                       read_iccid_resp_cb, cbd, g_free);
+}
+
+static void isi_read_file_transparent(struct ofono_sim *sim, int fileid,
+                                       int start, int length,
+                                       ofono_sim_read_cb_t cb, void *data)
+{
+       struct isi_cb_data *cbd;
+       gboolean done;
+
+       cbd = isi_cb_data_new(sim, cb, data);
+       if (cbd == NULL)
+               goto error;
+
+       switch (fileid) {
+       case SIM_EFSPN_FILEID:
+               done = isi_read_spn(sim, cbd);
+               break;
+
+       case SIM_EF_ICCID_FILEID:
+               done = isi_read_iccid(sim, cbd);
+               break;
+
+       default:
+               done = FALSE;
+       }
+
+       if (done)
+               return;
+
+       DBG("Fileid %04X not implemented", fileid);
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+       g_free(cbd);
+}
+
+static void isi_read_file_linear(struct ofono_sim *sim, int fileid,
+                                       int record, int length,
+                                       ofono_sim_read_cb_t cb, void *data)
+{
+       DBG("Fileid %04X not implemented", fileid);
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void isi_read_file_cyclic(struct ofono_sim *sim, int fileid,
+                                       int record, int length,
+                                       ofono_sim_read_cb_t cb, void *data)
+{
+       DBG("Fileid %04X not implemented", fileid);
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void isi_write_file_transparent(struct ofono_sim *sim, int fileid,
+                                       int start, int length,
+                                       const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       DBG("Fileid %04X not implemented", fileid);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void isi_write_file_linear(struct ofono_sim *sim, int fileid,
+                                       int record, int length,
+                                       const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       DBG("Fileid %04X not implemented", fileid);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void isi_write_file_cyclic(struct ofono_sim *sim, int fileid,
+                                       int length, const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       DBG("Fileid %04X not implemented", fileid);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void imsi_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sim_imsi_cb_t cb = cbd->cb;
+
+       const struct sim_imsi *resp;
+       size_t len = sizeof(struct sim_imsi);
+
+       char imsi[SIM_MAX_IMSI_LENGTH + 1];
+       size_t i, j;
+
+       if (!check_response_status(msg, SIM_IMSI_RESP_READ_IMSI, READ_IMSI))
+               goto error;
+
+       if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &resp, len))
+               goto error;
+
+       /* Ignore the low-order semi-octet of the first byte */
+       imsi[0] = ((resp->imsi[0] & 0xF0) >> 4) + '0';
+
+       for (i = 1, j = 1; i < resp->length && j < SIM_MAX_IMSI_LENGTH; i++) {
+               char nibble;
+
+               imsi[j++] = (resp->imsi[i] & 0x0F) + '0';
+               nibble = (resp->imsi[i] & 0xF0) >> 4;
+               if (nibble != 0x0F)
+                       imsi[j++] = nibble + '0';
+       }
+
+       imsi[j] = '\0';
+       CALLBACK_WITH_SUCCESS(cb, imsi, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void isi_read_imsi(struct ofono_sim *sim,
+                               ofono_sim_imsi_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+
+       const uint8_t msg[] = {
+               SIM_IMSI_REQ_READ_IMSI,
+               READ_IMSI
+       };
+       size_t len = sizeof(msg);
+
+       if (cbd == NULL || sd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, len, imsi_resp_cb, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+       g_free(cbd);
+}
+
+static void isi_query_passwd_state(struct ofono_sim *sim,
+                                       ofono_sim_passwd_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       DBG("passwd_state %u", sd->passwd_state);
+
+       sd->notify_ready = TRUE;
+
+       switch (sd->passwd_state) {
+       case OFONO_SIM_PASSWORD_NONE:
+               if (sd->ready)
+                       CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data);
+               else
+                       CALLBACK_WITH_FAILURE(cb, -1, data);
+               break;
+
+       case OFONO_SIM_PASSWORD_INVALID:
+               CALLBACK_WITH_FAILURE(cb, -1, data);
+               break;
+
+       default:
+               CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data);
+       }
+}
+
+static void sim_set_passwd_state(struct ofono_sim *sim,
+                                       enum ofono_sim_password_type pin_type)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       int inserted;
+       int previous;
+
+       if (pin_type == sd->passwd_state)
+               return;
+
+       DBG("new state \"%s\"", sim_password_name(pin_type));
+
+       inserted = pin_type != OFONO_SIM_PASSWORD_INVALID;
+       previous = sd->passwd_state != OFONO_SIM_PASSWORD_INVALID;
+
+       sd->passwd_state = pin_type;
+
+       if (pin_type != OFONO_SIM_PASSWORD_NONE) {
+               sd->ready = FALSE;
+               sd->notify_ready = FALSE;
+       }
+
+       if (inserted != previous)
+               ofono_sim_inserted_notify(sim, inserted);
+}
+
+static void check_sec_response(const GIsiMessage *msg, void *opaque,
+                                       uint8_t success, uint8_t failure)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+       struct ofono_sim *sim = cbd->user;
+       uint8_t id;
+       uint8_t cause;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               goto failure;
+       }
+
+       id = g_isi_msg_id(msg);
+
+       if (id == success) {
+               DBG("%s", sec_message_id_name(id));
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE);
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+               return;
+       }
+
+       if (id == failure && g_isi_msg_data_get_byte(msg, 0, &cause)) {
+               DBG("%s(cause=%02x)", sec_message_id_name(id), cause);
+
+               if (cause == SEC_CAUSE_CODE_BLOCKED)
+                       sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PUK);
+       } else
+               DBG("Error msg: %s", sec_message_id_name(id));
+
+failure:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void sec_code_verify_resp(const GIsiMessage *msg, void *opaque)
+{
+       check_sec_response(msg, opaque, SEC_CODE_VERIFY_OK_RESP,
+                               SEC_CODE_VERIFY_FAIL_RESP);
+}
+
+static void isi_send_passwd(struct ofono_sim *sim, const char *passwd,
+                               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+       unsigned char msg[2 + SEC_CODE_MAX_LENGTH + 1] = {
+               SEC_CODE_VERIFY_REQ,
+               SEC_CODE_PIN,
+       };
+       int len = 2 + strlen(passwd) + 1;
+
+       DBG("");
+
+       if (!cbd)
+               goto error;
+
+       strcpy((char *) msg + 2, passwd);
+
+       if (g_isi_client_send(sd->sec_client, msg, len,
+                               sec_code_verify_resp, cbd, g_free))
+               return;
+
+error:
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void isi_reset_passwd(struct ofono_sim *sim,
+                               const char *puk, const char *passwd,
+                               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+       enum ofono_sim_password_type passwd_type = OFONO_SIM_PASSWORD_SIM_PIN;
+       unsigned char msg[2 + 2 * (SEC_CODE_MAX_LENGTH + 1)] = {
+               SEC_CODE_VERIFY_REQ,
+       };
+       size_t len = sizeof(msg);
+
+       DBG("");
+
+       if (!cbd)
+               goto error;
+
+       if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN)
+               msg[1] = SEC_CODE_PUK;
+       else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2)
+               msg[1] = SEC_CODE_PUK2;
+       else
+               goto error;
+
+       strcpy((char *) &msg[2], puk);
+       strcpy((char *) &msg[2 + SEC_CODE_MAX_LENGTH + 1], passwd);
+
+       if (g_isi_client_send(sd->sec_client, msg, len,
+                       sec_code_verify_resp, cbd, g_free))
+               return;
+
+error:
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+
+/* ISI callback: Enable/disable PIN */
+static void pin_enable_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       check_sec_response(msg, opaque,
+                       SEC_CODE_STATE_OK_RESP, SEC_CODE_STATE_FAIL_RESP);
+}
+
+static void isi_lock(struct ofono_sim *sim,
+               enum ofono_sim_password_type passwd_type,
+               int enable, const char *passwd,
+               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+
+       unsigned char req[3 + SEC_CODE_MAX_LENGTH + 1] = {
+               SEC_CODE_STATE_REQ,
+       };
+
+       if (!cbd)
+               goto error;
+
+       DBG("enable %d pintype %d pass %s", enable, passwd_type, passwd);
+
+       if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN)
+               req[1] = SEC_CODE_PIN;
+       else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2)
+               req[1] = SEC_CODE_PIN2;
+       else
+               goto error;
+
+       if (enable)
+               req[2] = SEC_CODE_ENABLE;
+       else
+               req[2] = SEC_CODE_DISABLE;
+
+       strcpy((char *) &req[3], passwd);
+
+       if (g_isi_client_send(sd->sec_client, req, sizeof(req),
+                       pin_enable_resp_cb, cbd, g_free))
+               return;
+
+error:
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+
+/* ISI callback: PIN state (enabled/disabled) query */
+static void sec_code_change_resp(const GIsiMessage *msg, void *opaque)
+{
+       check_sec_response(msg, opaque,
+                       SEC_CODE_CHANGE_OK_RESP, SEC_CODE_CHANGE_FAIL_RESP);
+}
+
+
+static void isi_change_passwd(struct ofono_sim *sim,
+                               enum ofono_sim_password_type passwd_type,
+                               const char *old, const char *new,
+                               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+       unsigned char msg[2 + 2 * (SEC_CODE_MAX_LENGTH + 1)] = {
+               SEC_CODE_CHANGE_REQ,
+       };
+
+       DBG("passwd_type %d", passwd_type);
+
+       if (!cbd)
+               goto error;
+
+       if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN)
+               msg[1] = SEC_CODE_PIN;
+       else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2)
+               msg[1] = SEC_CODE_PIN2;
+       else
+               goto error;
+
+       strcpy((char *) &msg[2], old);
+       strcpy((char *) &msg[2 + SEC_CODE_MAX_LENGTH + 1], new);
+
+       if (g_isi_client_send(sd->sec_client, msg, sizeof(msg),
+                       sec_code_change_resp, cbd, g_free))
+               return;
+
+error:
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+
+/* ISI callback: PIN state (enabled/disabled) query */
+static void sec_code_state_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       check_sec_response(msg, opaque, SEC_CODE_STATE_OK_RESP,
+                               SEC_CODE_STATE_FAIL_RESP);
+}
+
+static void isi_query_locked(struct ofono_sim *sim,
+                               enum ofono_sim_password_type passwd_type,
+                               ofono_sim_locked_cb_t cb, void *data)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+
+       unsigned char msg[] = {
+               SEC_CODE_STATE_REQ,
+               0,
+               SEC_CODE_STATE_QUERY
+       };
+
+       DBG("");
+
+       if (!cbd)
+               goto error;
+
+       if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN)
+               msg[1] = SEC_CODE_PIN;
+       else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2)
+               msg[1] = SEC_CODE_PIN2;
+       else
+               goto error;
+
+       if (g_isi_client_send(sd->sec_client, msg, sizeof(msg),
+                       sec_code_state_resp_cb, cbd, g_free))
+               return;
+
+error:
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void sim_ind_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_sim *sim = opaque;
+       uint8_t service;
+       uint8_t status;
+
+       DBG("");
+
+       if (g_isi_msg_id(msg) != SIM_IND ||
+                       !g_isi_msg_data_get_byte(msg, 0, &service) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &status))
+               return;
+
+       if (status == SIM_SERV_PIN_VERIFY_REQUIRED && service == SIM_ST_PIN)
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN);
+       else if (status == SIM_SERV_SIM_BLOCKED)
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PUK);
+       else if (status == SIM_SERV_INIT_OK && service == SIM_ST_INFO)
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE);
+       else if (status == SIM_SERV_SIM_DISCONNECTED)
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_INVALID);
+}
+
+static void sim_server_ready_ind_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_sim *sim = opaque;
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       DBG("");
+
+       if (sd == NULL || g_isi_msg_id(msg) != SIM_SERVER_READY_IND)
+               return;
+
+       sd->ready = TRUE;
+
+       if (sd->notify_ready)
+               __ofono_sim_recheck_pin(sim);
+}
+
+static void read_dyn_flags_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_sim *sim = opaque;
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       int status;
+
+       status = sim_resp_status(msg, SIM_DYNAMIC_FLAGS_RESP, READ_DYN_FLAGS);
+
+       if (status < 0 || status == SIM_SERV_NOTREADY)
+               return;
+
+       sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE);
+
+       sd->ready = TRUE;
+
+       if (sd->notify_ready)
+               __ofono_sim_recheck_pin(sim);
+}
+
+static void read_dyn_flags_req(struct ofono_sim *sim)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       unsigned char req[] = {
+               SIM_DYNAMIC_FLAGS_REQ,
+               READ_DYN_FLAGS,
+               0
+       };
+
+       g_isi_client_send(sd->client, req, sizeof(req),
+                               read_dyn_flags_cb, sim, NULL);
+}
+
+static void sec_state_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_sim *sim = opaque;
+       uint8_t msgid;
+       uint8_t cause;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return;
+       }
+
+       msgid = g_isi_msg_id(msg);
+
+       if (msgid != SEC_STATE_RESP) {
+               DBG("Unexpected msg: %s", sec_message_id_name(msgid));
+               return;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &cause)) {
+               DBG("Runt msg: %s", sec_message_id_name(msgid));
+               return;
+       }
+
+       DBG("%s(cause=0x%0x)", sec_message_id_name(msgid), cause);
+
+       switch (cause) {
+       case SEC_STARTUP_OK:
+               DBG("SEC_STARTUP_OK");
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE);
+               /* Check if SIM server is already ready */
+               read_dyn_flags_req(sim);
+               break;
+
+       case SEC_CAUSE_PIN_REQUIRED:
+               DBG("SEC_CAUSE_PIN_REQUIRED");
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN);
+               break;
+
+       case SEC_CAUSE_PUK_REQUIRED:
+               DBG("SEC_CAUSE_PUK_REQUIRED");
+               sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN);
+               break;
+
+       case SEC_CAUSE_NO_SIM:
+               DBG("SEC_CAUSE_NO_SIM");
+               break;
+
+       case SEC_CAUSE_INVALID_SIM:
+               DBG("SEC_CAUSE_INVALID_SIM");
+               break;
+
+       case SEC_CAUSE_SIM_REJECTED:
+               DBG("SEC_CAUSE_SIM_REJECTED");
+               break;
+
+       default:
+               break;
+       }
+}
+
+static void isi_sec_state_req(struct ofono_sim *sim)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       unsigned char req[] = {
+               SEC_STATE_REQ,
+               0,
+               0
+       };
+
+       g_isi_client_send(sd->sec_client, req, sizeof(req),
+                       sec_state_resp_cb, sim, NULL);
+}
+
+static void sim_status_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_sim *sim = opaque;
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       int status = sim_resp_status(msg, SIM_STATUS_RESP, SIM_ST_CARD_STATUS);
+
+       if (status < 0 || status == SIM_SERV_SIM_DISCONNECTED)
+               return;
+
+       /* We probably have a SIM. */
+       if (sd->sec_client)
+               isi_sec_state_req(sim);
+       else
+               read_dyn_flags_req(sim);
+}
+
+static void isi_sim_status_req(struct ofono_sim *sim)
+{
+       struct sim_data *sd = ofono_sim_get_data(sim);
+       const unsigned char req[] = {
+               SIM_STATUS_REQ,
+               SIM_ST_CARD_STATUS
+       };
+
+       g_isi_client_send(sd->client, req, sizeof(req),
+                       sim_status_resp_cb, sim, NULL);
+}
+
+static void sec_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("PN_SECURITY: %s", strerror(-g_isi_msg_error(msg)));
+               DBG("PIN code handling not available");
+               g_isi_client_destroy(sd->sec_client);
+               sd->sec_client = NULL;
+       }
+
+       g_isi_client_ind_subscribe(sd->client, SIM_IND, sim_ind_cb, sim);
+       g_isi_client_ind_subscribe(sd->client, SIM_SERVER_READY_IND,
+                                       sim_server_ready_ind_cb, sim);
+       /* Check if we have a SIM */
+       isi_sim_status_req(sim);
+
+       ofono_sim_register(sim);
+}
+
+static void sim_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct sim_data *sd = ofono_sim_get_data(sim);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("PN_SIM: %s", strerror(-g_isi_msg_error(msg)));
+               ofono_sim_remove(sim);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_verify(sd->sec_client, sec_reachable_cb, sim, NULL);
+}
+
+static int isi_sim_probe(struct ofono_sim *sim, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *modem = user;
+       struct sim_data *sd;
+
+       sd = g_try_new0(struct sim_data, 1);
+       if (sd == NULL)
+               return -ENOMEM;
+
+       sd->passwd_state = OFONO_SIM_PASSWORD_INVALID;
+
+       sd->client = g_isi_client_create(modem, PN_SIM);
+       if (sd->client == NULL)
+               goto error;
+
+       sd->sec_client = g_isi_client_create(modem, PN_SECURITY);
+       if (sd->sec_client == NULL)
+               goto error;
+
+       g_isi_client_set_timeout(sd->client, SIM_TIMEOUT);
+       g_isi_client_set_timeout(sd->sec_client, SIM_TIMEOUT);
+
+       ofono_sim_set_data(sim, sd);
+
+       g_isi_client_ind_subscribe(sd->client, SIM_IND, sim_ind_cb, sim);
+       g_isi_client_verify(sd->client, sim_reachable_cb, sim, NULL);
+
+       return 0;
+
+error:
+       g_isi_client_destroy(sd->client);
+       g_isi_client_destroy(sd->sec_client);
+
+       return -ENOMEM;
+}
+
+static void isi_sim_remove(struct ofono_sim *sim)
+{
+       struct sim_data *data = ofono_sim_get_data(sim);
+
+       ofono_sim_set_data(sim, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_isi_client_destroy(data->sec_client);
+       g_free(data);
+}
+
+static struct ofono_sim_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_sim_probe,
+       .remove                 = isi_sim_remove,
+       .read_file_info         = isi_read_file_info,
+       .read_file_transparent  = isi_read_file_transparent,
+       .read_file_linear       = isi_read_file_linear,
+       .read_file_cyclic       = isi_read_file_cyclic,
+       .write_file_transparent = isi_write_file_transparent,
+       .write_file_linear      = isi_write_file_linear,
+       .write_file_cyclic      = isi_write_file_cyclic,
+       .read_imsi              = isi_read_imsi,
+       .query_passwd_state     = isi_query_passwd_state,
+       .send_passwd            = isi_send_passwd,
+       .reset_passwd           = isi_reset_passwd,
+       .lock                   = isi_lock,
+       .change_passwd          = isi_change_passwd,
+       .query_locked           = isi_query_locked,
+};
+
+void isi_sim_init(void)
+{
+       ofono_sim_driver_register(&driver);
+}
+
+void isi_sim_exit(void)
+{
+       ofono_sim_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/sim.h b/drivers/isimodem/sim.h
new file mode 100644 (file)
index 0000000..c370a68
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_SIM_H
+#define __ISIMODEM_SIM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_SIM                                                 0x09
+#define SIM_TIMEOUT                                            5
+#define PN_SECURITY                                            0x08
+#define SIM_MAX_IMSI_LENGTH                                    15
+
+enum sim_isi_cause {
+       SIM_SERV_NOT_AVAIL =                                    0x00,
+       SIM_SERV_OK =                                           0x01,
+       SIM_SERV_PIN_VERIFY_REQUIRED =                          0x02,
+       SIM_SERV_PIN_REQUIRED =                                 0x03,
+       SIM_SERV_SIM_BLOCKED =                                  0x04,
+       SIM_SERV_SIM_PERMANENTLY_BLOCKED =                      0x05,
+       SIM_SERV_SIM_DISCONNECTED =                             0x06,
+       SIM_SERV_SIM_REJECTED =                                 0x07,
+       SIM_SERV_LOCK_ACTIVE =                                  0x08,
+       SIM_SERV_AUTOLOCK_CLOSED =                              0x09,
+       SIM_SERV_AUTOLOCK_ERROR =                               0x0A,
+       SIM_SERV_INIT_OK =                                      0x0B,
+       SIM_SERV_INIT_NOT_OK =                                  0x0C,
+       SIM_SERV_WRONG_OLD_PIN =                                0x0D,
+       SIM_SERV_PIN_DISABLED =                                 0x0E,
+       SIM_SERV_COMMUNICATION_ERROR =                          0x0F,
+       SIM_SERV_UPDATE_IMPOSSIBLE =                            0x10,
+       SIM_SERV_NO_SECRET_CODE_IN_SIM =                        0x11,
+       SIM_SERV_PIN_ENABLE_OK =                                0x12,
+       SIM_SERV_PIN_DISABLE_OK =                               0x13,
+       SIM_SERV_WRONG_UNBLOCKING_KEY =                         0x15,
+       SIM_SERV_ILLEGAL_NUMBER =                               0x2E,
+       SIM_SERV_NOT_OK =                                       0x1C,
+       SIM_SERV_PN_LIST_ENABLE_OK =                            0x1E,
+       SIM_SERV_PN_LIST_DISABLE_OK =                           0x1F,
+       SIM_SERV_NO_PIN =                                       0x20,
+       SIM_SERV_PIN_VERIFY_OK =                                0x21,
+       SIM_SERV_PIN_BLOCKED =                                  0x22,
+       SIM_SERV_PIN_PERM_BLOCKED =                             0x23,
+       SIM_SERV_DATA_NOT_AVAIL =                               0x24,
+       SIM_SERV_IN_HOME_ZONE =                                 0x25,
+       SIM_SERV_STATE_CHANGED =                                0x27,
+       SIM_SERV_INF_NBR_READ_OK =                              0x28,
+       SIM_SERV_INF_NBR_READ_NOT_OK =                          0x29,
+       SIM_SERV_IMSI_EQUAL =                                   0x2A,
+       SIM_SERV_IMSI_NOT_EQUAL =                               0x2B,
+       SIM_SERV_INVALID_LOCATION =                             0x2C,
+       SIM_SERV_STA_SIM_REMOVED =                              0x35,
+       SIM_SERV_SECOND_SIM_REMOVED_CS =                        0x36,
+       SIM_SERV_CONNECTED_INDICATION_CS =                      0x37,
+       SIM_SERV_SECOND_SIM_CONNECTED_CS =                      0x38,
+       SIM_SERV_PIN_RIGHTS_LOST_IND_CS =                       0x39,
+       SIM_SERV_PIN_RIGHTS_GRANTED_IND_CS =                    0x3A,
+       SIM_SERV_INIT_OK_CS =                                   0x3B,
+       SIM_SERV_INIT_NOT_OK_CS =                               0x3C,
+       SIM_FDN_ENABLED =                                       0x19,
+       SIM_FDN_DISABLED =                                      0x1A,
+       SIM_SERV_INVALID_FILE =                                 0x45,
+       SIM_SERV_DATA_AVAIL =                                   0x4F,
+       SIM_SERV_ICC_EQUAL =                                    0x49,
+       SIM_SERV_ICC_NOT_EQUAL =                                0x4A,
+       SIM_SERV_SIM_NOT_INITIALISED =                          0x4B,
+       SIM_SERV_SERVICE_NOT_AVAIL =                            0x50,
+       SIM_SERV_FDN_STATUS_ERROR =                             0x57,
+       SIM_SERV_FDN_CHECK_PASSED =                             0x58,
+       SIM_SERV_FDN_CHECK_FAILED =                             0x59,
+       SIM_SERV_FDN_CHECK_DISABLED =                           0x5A,
+       SIM_SERV_FDN_CHECK_NO_FDN_SIM =                         0x5B,
+       SIM_STA_ISIM_AVAILEBLE_PIN_REQUIRED =                   0x5C,
+       SIM_STA_ISIM_AVAILEBLE =                                0x5D,
+       SIM_STA_USIM_AVAILEBLE =                                0x5E,
+       SIM_STA_SIM_AVAILEBLE =                                 0x5F,
+       SIM_STA_ISIM_NOT_INITIALIZED =                          0x60,
+       SIM_STA_IMS_READY =                                     0x61,
+       SIM_STA_APP_DATA_READ_OK =                              0x96,
+       SIM_STA_APP_ACTIVATE_OK =                               0x97,
+       SIM_STA_APP_ACTIVATE_NOT_OK =                           0x98,
+       SIM_SERV_NOT_DEFINED =                                  0xF9,
+       SIM_SERV_NOSERVICE =                                    0xFA,
+       SIM_SERV_NOTREADY =                                     0xFB,
+       SIM_SERV_ERROR =                                        0xFC,
+       SIM_SERV_CIPHERING_INDICATOR_DISPLAY_REQUIRED =         0x30,
+       SIM_SERV_CIPHERING_INDICATOR_DISPLAY_NOT_REQUIRED =     0x31,
+       SIM_SERV_FILE_NOT_AVAILABLE =                           0x4D
+};
+
+enum sim_subblock {
+       SIM_PB_INFO_REQUEST =                                   0xE4,
+       SIM_PB_STATUS =                                         0xFB,
+       SIM_PB_LOCATION =                                       0xFE,
+       SIM_PB_LOCATION_SEARCH =                                0xFF,
+};
+
+enum sim_pb_type {
+       SIM_PB_ADN =                                            0xC8,
+};
+
+enum sim_pb_tag {
+       SIM_PB_ANR =                                            0xCA,
+       SIM_PB_EMAIL =                                          0xDD,
+       SIM_PB_SNE =                                            0xF7,
+};
+
+enum sim_message_id {
+       SIM_NETWORK_INFO_REQ =                                  0x19,
+       SIM_NETWORK_INFO_RESP =                                 0x1A,
+       SIM_IMSI_REQ_READ_IMSI =                                0x1D,
+       SIM_IMSI_RESP_READ_IMSI =                               0x1E,
+       SIM_SERV_PROV_NAME_REQ =                                0x21,
+       SIM_SERV_PROV_NAME_RESP =                               0x22,
+       SIM_DYNAMIC_FLAGS_REQ =                                 0x29,
+       SIM_DYNAMIC_FLAGS_RESP =                                0x2A,
+       SIM_READ_FIELD_REQ =                                    0xBA,
+       SIM_READ_FIELD_RESP =                                   0xBB,
+       SIM_SMS_REQ =                                           0xBC,
+       SIM_SMS_RESP =                                          0xBD,
+       SIM_STATUS_REQ =                                        0xC0,
+       SIM_STATUS_RESP =                                       0xC1,
+       SIM_PB_REQ_SIM_PB_READ =                                0xDC,
+       SIM_PB_RESP_SIM_PB_READ =                               0xDD,
+       SIM_SERVER_READY_IND =                                  0xED,
+       SIM_IND =                                               0xEF,
+};
+
+enum sim_service_type {
+       SIM_ST_CARD_STATUS =                                    0x00,
+       SIM_ST_PIN =                                            0x01,
+       SIM_ST_ALL_SERVICES =                                   0x05,
+       SIM_ST_INFO =                                           0x0D,
+       SIM_PB_READ =                                           0x0F,
+       SIM_ST_CAT_SUPPORT_ENABLE =                             0x15,
+       SIM_ST_CAT_SUPPORT_DISABLE =                            0x16,
+       SIM_ST_READ_SERV_PROV_NAME =                            0x2C,
+       READ_IMSI =                                             0x2D,
+       READ_HPLMN =                                            0x2F,
+       READ_DYN_FLAGS =                                        0x35,
+       READ_PARAMETER =                                        0x52,
+       UPDATE_PARAMETER =                                      0x53,
+       ICC =                                                   0x66,
+};
+
+#define SEC_CODE_MAX_LENGTH            0x0A
+
+enum sec_message_id {
+       SEC_CODE_STATE_REQ =            0x01,
+       SEC_CODE_STATE_OK_RESP =        0x02,
+       SEC_CODE_STATE_FAIL_RESP =      0x03,
+       SEC_CODE_CHANGE_REQ =           0x04,
+       SEC_CODE_CHANGE_OK_RESP =       0x05,
+       SEC_CODE_CHANGE_FAIL_RESP =     0x06,
+       SEC_CODE_VERIFY_REQ =           0x07,
+       SEC_CODE_VERIFY_OK_RESP =       0x08,
+       SEC_CODE_VERIFY_FAIL_RESP =     0x09,
+       SEC_STATE_REQ =                 0x11,
+       SEC_STATE_RESP =                0x12,
+};
+
+enum sec_code_id_info {
+       SEC_CODE_PIN =                  0x02,
+       SEC_CODE_PUK =                  0x03,
+       SEC_CODE_PIN2 =                 0x04,
+       SEC_CODE_PUK2 =                 0x05,
+};
+
+enum sec_code_state_info {
+       SEC_CODE_DISABLE =              0x00,
+       SEC_CODE_ENABLE =               0x01,
+       SEC_CODE_STATE_QUERY =          0x04,
+};
+
+enum sec_state_cause_info {
+       SEC_CAUSE_PIN_REQUIRED =        0x02,
+       SEC_CAUSE_PUK_REQUIRED =        0x03,
+       SEC_STARTUP_OK =                0x05,
+       SEC_STARTUP_ONGOING =           0x07,
+       SEC_CAUSE_CODE_BLOCKED =        0x08,
+       SEC_CAUSE_NO_SIM =              0x16,
+       SEC_CAUSE_SIM_REJECTED =        0x1A,
+       SEC_CAUSE_INVALID_SIM =         0x1E,
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __ISIMODEM_SIM_H */
diff --git a/drivers/isimodem/sms.c b/drivers/isimodem/sms.c
new file mode 100644 (file)
index 0000000..8d14eeb
--- /dev/null
@@ -0,0 +1,1143 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include <gisi/message.h>
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sms.h>
+
+#include "smsutil.h"
+#include "isimodem.h"
+#include "isiutil.h"
+#include "sms.h"
+#include "sim.h"
+#include "debug.h"
+
+/* This is a straightforward copy of the EF_smsp structure */
+struct sim_efsmsp{
+       uint8_t absent;
+       uint8_t tp_pid;
+       uint8_t tp_dcs;
+       uint8_t tp_vp;
+       uint8_t dst[12];
+       uint8_t sca[12];
+       uint8_t alphalen;
+       uint8_t filler[3];
+       uint16_t alpha[17];
+};
+
+/* Sub-block used by PN_SMS */
+struct sms_params {
+       uint8_t location;
+       uint8_t absent;
+       uint8_t tp_pid;
+       uint8_t tp_dcs;
+       uint8_t dst[12];
+       uint8_t sca[12];
+       uint8_t tp_vp;
+       uint8_t alphalen;
+       uint8_t filler[2];
+       uint16_t alpha[17];
+};
+
+struct sms_report {
+       uint8_t type;
+       uint8_t cause;
+       uint8_t ref;
+};
+
+struct sms_status {
+       uint8_t status;
+       uint8_t ref;
+       uint8_t route;
+       uint8_t cseg;   /* Current segment */
+       uint8_t tseg;   /* Total segments */
+};
+
+struct sms_addr {
+       uint8_t type;
+       uint8_t len;
+       uint8_t *data;
+};
+
+struct sms_common {
+       uint8_t len;
+       uint8_t *data;
+};
+
+struct sms_data {
+       GIsiClient *client;
+       GIsiClient *sim;
+       GIsiVersion version;
+       struct sim_efsmsp params;
+};
+
+static uint8_t bearer_to_cs_pref(int bearer)
+{
+       switch (bearer) {
+       case 0:
+               return SMS_ROUTE_NOT_AVAILABLE;
+       case 1:
+               return SMS_ROUTE_PRIORITY_1;
+       case 2:
+               return SMS_ROUTE_PRIORITY_2;
+       case 3:
+               return SMS_ROUTE_PRIORITY_1;
+       }
+
+       return SMS_ROUTE_NOT_AVAILABLE;
+}
+
+static uint8_t bearer_to_ps_pref(int bearer)
+{
+       switch (bearer) {
+       case 0:
+               return SMS_ROUTE_PRIORITY_1;
+       case 1:
+               return SMS_ROUTE_NOT_AVAILABLE;
+       case 2:
+               return SMS_ROUTE_PRIORITY_1;
+       case 3:
+               return SMS_ROUTE_PRIORITY_2;
+       }
+
+       return SMS_ROUTE_NOT_AVAILABLE;
+}
+
+static int cs_ps_pref_to_bearer(uint8_t cs, uint8_t ps)
+{
+       if (cs == SMS_ROUTE_NOT_AVAILABLE && ps == SMS_ROUTE_PRIORITY_1)
+               return 0;
+
+       if (cs == SMS_ROUTE_PRIORITY_1 && ps == SMS_ROUTE_NOT_AVAILABLE)
+               return 1;
+
+       if (cs == SMS_ROUTE_PRIORITY_2 && ps == SMS_ROUTE_PRIORITY_1)
+               return 2;
+
+       if (cs == SMS_ROUTE_PRIORITY_1 && ps == SMS_ROUTE_PRIORITY_2)
+               return 3;
+
+       return 0;
+}
+
+static gboolean check_sim(const GIsiMessage *msg, uint8_t msgid, uint8_t service)
+{
+       uint8_t type;
+       uint8_t cause;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s", sms_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &type))
+               return FALSE;
+
+       if (type != service) {
+               DBG("Unexpected service type: 0x%02X", type);
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &cause))
+               return FALSE;
+
+       if (cause != SIM_SERV_OK) {
+               DBG("Request failed: %s", sim_isi_cause_name(cause));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean check_sms(const GIsiMessage *msg, uint8_t msgid, int expect)
+{
+       uint8_t cause;
+       int pos;
+
+       /*
+       * Quirk for the cause code position in the response. More
+       * recent versions of the API use 16bit subblock IDs, causing
+       * the cause to be bumped forward by one byte.
+       */
+       if (ISI_VERSION_AT_LEAST(msg->version, 9, 1))
+               pos = 1;
+       else
+               pos = 0;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       sms_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+
+       if (expect == -1)
+               return TRUE;
+
+       if (!g_isi_msg_data_get_byte(msg, pos, &cause)) {
+               DBG("Unable to parse cause");
+               return FALSE;
+       }
+
+       if (cause == expect)
+               return TRUE;
+
+       if (cause == SMS_ERR_PP_RESERVED) {
+               DBG("Request failed: 0x%02"PRIx8" (%s).\n\n  Unable to "
+                       "bootstrap SMS routing.\n  It appears some other "
+                       "component is already\n  registered as the SMS "
+                       "routing endpoint.\n  As a consequence, "
+                       "only sending SMSs is going to work.\n\n",
+                       cause, sms_isi_cause_name(cause));
+               return TRUE;
+       }
+
+       DBG("Request failed: %s", sms_isi_cause_name(cause));
+       return FALSE;
+}
+
+static void sca_sim_query_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_sms *sms = cbd->user;
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       ofono_sms_sca_query_cb_t cb = cbd->cb;
+
+       struct ofono_phone_number sca;
+       struct sms_params *info;
+       size_t len = sizeof(struct sms_params);
+       uint8_t bcd_len;
+
+       if (!check_sim(msg, SIM_SMS_RESP, READ_PARAMETER))
+               goto error;
+
+       if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &info, len))
+               goto error;
+
+       if (info->alphalen > 17)
+               info->alphalen = 17;
+       else if (info->alphalen < 1)
+               info->alphalen = 1;
+
+       info->alpha[info->alphalen - 1] = '\0';
+
+       sd->params.absent = info->absent;
+       sd->params.tp_pid = info->tp_pid;
+       sd->params.tp_dcs = info->tp_dcs;
+       sd->params.tp_vp = info->tp_vp;
+
+       memcpy(sd->params.dst, info->dst, sizeof(sd->params.dst));
+       memcpy(sd->params.sca, info->sca, sizeof(sd->params.sca));
+
+       sd->params.alphalen = info->alphalen;
+       memcpy(sd->params.alpha, info->alpha, sizeof(sd->params.alpha));
+
+       /*
+        * Bitmask indicating absence of parameters --
+        * If second bit is set it indicates that the SCA is absent
+        */
+       if (info->absent & 0x2)
+               goto error;
+
+       bcd_len = info->sca[0];
+
+       if (bcd_len == 0 || bcd_len > 12)
+               goto error;
+
+       extract_bcd_number(info->sca + 2, bcd_len - 1, sca.number);
+       sca.type = 0x80 | info->sca[1];
+
+       CALLBACK_WITH_SUCCESS(cb, &sca, cbd->data);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static gboolean sca_sim_query(GIsiClient *client, void *data, GDestroyNotify notify)
+{
+       const uint8_t msg[] = {
+               SIM_SMS_REQ,
+               READ_PARAMETER,
+               1,      /* Location, default is 1 */
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), sca_sim_query_resp_cb,
+                                       data, notify);
+}
+
+static void isi_sca_query(struct ofono_sms *sms,
+                               ofono_sms_sca_query_cb_t cb, void *data)
+{
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data);
+
+       if (cbd == NULL || sd->sim == NULL)
+               goto error;
+
+       if (sca_sim_query(sd->sim, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+       g_free(cbd);
+}
+
+static void sca_sim_set_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sms_sca_set_cb_t cb = cbd->cb;
+
+       if (!check_sim(msg, SIM_SMS_RESP, UPDATE_PARAMETER)) {
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+               return;
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static gboolean sca_sim_set(GIsiClient *client, struct sim_efsmsp *params,
+                               const struct ofono_phone_number *sca, void *data,
+                               GDestroyNotify notify)
+{
+       uint8_t msg[] = {
+               SIM_SMS_REQ,
+               UPDATE_PARAMETER,
+               1,      /* Location, default is 1 */
+       };
+       struct iovec iov[2] = {
+               { msg, sizeof(msg) },
+               { params, sizeof(struct sim_efsmsp) },
+       };
+       uint8_t *bcd;
+
+       bcd = params->sca;
+       params->absent &= ~SMS_PI_SERVICE_CENTER_ADDRESS;
+
+       encode_bcd_number(sca->number, bcd + 2);
+       bcd[0] = 1 + (strlen(sca->number) + 1) / 2;
+       bcd[1] = sca->type & 0xFF;
+
+       return g_isi_client_vsend(client, iov, 2, sca_sim_set_resp_cb,
+                                       data, notify);
+}
+
+static void isi_sca_set(struct ofono_sms *sms,
+                       const struct ofono_phone_number *sca,
+                       ofono_sms_sca_set_cb_t cb, void *data)
+{
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data);
+
+       if (cbd == NULL || sd->sim == NULL)
+               goto error;
+
+       if (sca_sim_set(sd->sim, &sd->params, sca, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void submit_failure_debug(struct sms_report *report)
+{
+       const char *cause;
+
+       if (report->type == SMS_CAUSE_TYPE_COMMON)
+               cause = sms_isi_cause_name(report->cause);
+       else
+               cause = sms_gsm_cause_name(report->cause);
+
+       DBG("Message 0x%02"PRIx8" failed: %s", report->ref, cause);
+}
+
+static void submit_tpdu_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sms_submit_cb_t cb = cbd->cb;
+       struct sms_report *report;
+       size_t len = sizeof(struct sms_report);
+
+       if (!check_sms(msg, SMS_MESSAGE_SEND_RESP, -1))
+               goto error;
+
+       if (g_isi_msg_data_len(msg) < len)
+               goto error;
+
+       if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &report, len))
+               goto error;
+
+       if (report->type == SMS_CAUSE_TYPE_COMMON && report->cause == SMS_OK) {
+               CALLBACK_WITH_SUCCESS(cb, report->ref, cbd->data);
+               return;
+       }
+
+       submit_failure_debug(report);
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void submit_gsm_tpdu_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sms_submit_cb_t cb = cbd->cb;
+       struct sms_report *report;
+       size_t len = sizeof(struct sms_report);
+       GIsiSubBlockIter iter;
+
+       if (!check_sms(msg, SMS_MESSAGE_SEND_RESP, -1))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SMS_GSM_REPORT)
+                       continue;
+
+               if (!g_isi_sb_iter_get_struct(&iter, (void **) &report, len, 2))
+                       goto error;
+
+               if (report->type == SMS_CAUSE_TYPE_COMMON &&
+                               report->cause == SMS_OK) {
+                       CALLBACK_WITH_SUCCESS(cb, report->ref, cbd->data);
+                       return;
+               }
+
+               submit_failure_debug(report);
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static gboolean submit_tpdu(GIsiClient *client, unsigned char *pdu, int pdu_len,
+                               int tpdu_len, int mms, void *data,
+                               GDestroyNotify notify)
+{
+       uint8_t use_sca = (pdu_len - tpdu_len) > 1;
+       size_t sca_sb_len = use_sca ? 18 : 0;
+       size_t tpdu_sb_len = ALIGN4(6 + tpdu_len);
+       size_t tpdu_pad_len = tpdu_sb_len - (6 + tpdu_len);
+
+       uint8_t msg[] = {
+               SMS_MESSAGE_SEND_REQ,
+               mms,                    /* More messages to send */
+               SMS_ROUTE_ANY,          /* Use any (default) route */
+               0,                      /* Repeated message */
+               0, 0,                   /* Filler */
+               use_sca ? 3 : 2,        /* Subblock count */
+               ISI_16BIT(SMS_SB_SMS_PARAMETERS),
+               ISI_16BIT(8),           /* Subblock length */
+               SMS_PARAMETER_LOCATION_DEFAULT,
+               SMS_PI_SERVICE_CENTER_ADDRESS,
+               0, 0,                   /* Filler */
+               ISI_16BIT(SMS_SB_TPDU),
+               ISI_16BIT(tpdu_sb_len),
+               tpdu_len,
+               0,                      /* Filler */
+               /* Databytes aligned to next 32bit boundary */
+       };
+       uint8_t sca_sb[18] = {
+               ISI_16BIT(SMS_SB_ADDRESS),
+               ISI_16BIT(18),
+               SMS_SMSC_ADDRESS,
+               0,                      /* Filled in later */
+       };
+       uint8_t padding[4] = { 0 };
+       struct iovec iov[4] = {
+               { msg, sizeof(msg) },
+               { pdu + pdu_len - tpdu_len, tpdu_len },
+               { padding, tpdu_pad_len },
+               { sca_sb, sca_sb_len },
+       };
+
+       if (use_sca) {
+               sca_sb[5] = pdu_len - tpdu_len;
+               memcpy(sca_sb + 6, pdu, pdu_len - tpdu_len);
+       }
+
+       return g_isi_client_vsend_with_timeout(client, iov, 4, SMS_TIMEOUT,
+                                               submit_tpdu_resp_cb, data,
+                                               notify);
+}
+
+static gboolean submit_gsm_tpdu(GIsiClient *client, unsigned char *pdu,
+                               int pdu_len, int tpdu_len, int mms,
+                               void *data, GDestroyNotify notify)
+{
+       uint8_t use_sca = (pdu_len - tpdu_len) > 1;
+       size_t sca_sb_len = use_sca ? 16 : 0;
+       size_t tpdu_sb_len = ALIGN4(4 + tpdu_len);
+       size_t tpdu_pad_len = tpdu_sb_len - (4 + tpdu_len);
+
+       uint8_t msg[] = {
+               SMS_MESSAGE_SEND_REQ,
+               mms,    /* More messages to send */
+               SMS_ROUTE_CS_PREF,
+               0,      /* Repeated message */
+               SMS_SENDER_ANY,
+               SMS_TYPE_TEXT_MESSAGE,
+               1,      /* Subblock count */
+               SMS_GSM_TPDU,
+               tpdu_sb_len + sca_sb_len,
+               0,                      /* Filler */
+               use_sca ? 2 : 1,        /* Sub-sub blocks */
+               SMS_COMMON_DATA,
+               tpdu_sb_len,
+               tpdu_len,
+               0,      /* Packing required? */
+               /* Databytes aligned to next 32bit boundary */
+       };
+       uint8_t sca_sb[16] = {
+               SMS_ADDRESS,
+               16,     /* Subblock length */
+               SMS_GSM_0411_ADDRESS,
+               0,      /* Filled in later */
+       };
+       uint8_t padding[4] = { 0 };
+       struct iovec iov[4] = {
+               { msg, sizeof(msg) },
+               { pdu + pdu_len - tpdu_len, tpdu_len },
+               { padding, tpdu_pad_len },
+               { sca_sb, sca_sb_len },
+       };
+
+       if (use_sca) {
+               sca_sb[3] = pdu_len - tpdu_len;
+               memcpy(sca_sb + 4, pdu, pdu_len - tpdu_len);
+       }
+
+       /*
+        * Modem seems to time out SMS_MESSAGE_SEND_REQ in 5 seconds.
+        * Wait normal timeout plus the modem timeout.
+        */
+       return g_isi_client_vsend_with_timeout(client, iov, 4, SMS_TIMEOUT + 5,
+                                               submit_gsm_tpdu_resp_cb, data,
+                                               notify);
+}
+
+static void isi_submit(struct ofono_sms *sms, unsigned char *pdu,
+                       int pdu_len, int tpdu_len, int mms,
+                       ofono_sms_submit_cb_t cb, void *data)
+{
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data);
+
+       if (cbd == NULL)
+               goto error;
+
+       if (ISI_VERSION_AT_LEAST(&sd->version, 9, 1)) {
+               if (submit_tpdu(sd->client, pdu, pdu_len, tpdu_len, mms,
+                               cbd, g_free))
+                       return;
+       } else {
+               if (submit_gsm_tpdu(sd->client, pdu, pdu_len, tpdu_len, mms,
+                                       cbd, g_free))
+                       return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+       g_free(cbd);
+}
+
+static void bearer_query_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sms_bearer_query_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+       uint8_t sb, cs, ps;
+
+       if (!check_sms(msg, SMS_SETTINGS_READ_RESP, SMS_OK))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &sb))
+               goto error;
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 2, TRUE, sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SMS_SB_ROUTE_INFO)
+                       continue;
+
+               if (!g_isi_msg_data_get_byte(msg, 5, &cs))
+                       goto error;
+
+               if (!g_isi_msg_data_get_byte(msg, 6, &ps))
+                       goto error;
+
+               CALLBACK_WITH_SUCCESS(cb, cs_ps_pref_to_bearer(cs, ps),
+                                       cbd->data);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, cbd->data);
+}
+
+static void isi_bearer_query(struct ofono_sms *sms,
+                               ofono_sms_bearer_query_cb_t cb, void *data)
+{
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data);
+       const uint8_t msg[] = {
+               SMS_SETTINGS_READ_REQ,
+               SMS_SETTING_TYPE_ROUTE,
+               0,
+       };
+
+       DBG("");
+
+       if (cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, sizeof(msg), bearer_query_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+       g_free(cbd);
+}
+
+static void bearer_set_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_sms_bearer_set_cb_t cb = cbd->cb;
+
+       if (check_sms(msg, SMS_SETTINGS_UPDATE_RESP, SMS_OK))
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_bearer_set(struct ofono_sms *sms, int bearer,
+                               ofono_sms_bearer_set_cb_t cb, void *data)
+{
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data);
+       const uint8_t msg[] = {
+               SMS_SETTINGS_UPDATE_REQ,
+               SMS_SETTING_TYPE_ROUTE,
+               1,              /* Subblock count */
+               ISI_16BIT(SMS_SB_ROUTE_INFO),
+               ISI_16BIT(8),   /* Subblock length */
+               bearer_to_cs_pref(bearer),      /* CS priority */
+               bearer_to_ps_pref(bearer),      /* PS priority */
+               0, 0,
+       };
+
+       if (cbd == NULL)
+               goto error;
+
+       if (g_isi_client_send(sd->client, msg, sizeof(msg), bearer_set_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void send_status_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct sms_status *info;
+       size_t len = sizeof(struct sms_status);
+
+       DBG("");
+
+       if (g_isi_msg_id(msg) != SMS_MESSAGE_SEND_STATUS_IND)
+               return;
+
+       if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &info, len))
+               return;
+
+       DBG("status=0x%"PRIx8", ref=0x%"PRIx8", route=0x%"PRIx8
+               ", cseg=0x%"PRIx8", tseg=0x%"PRIx8,
+               info->status, info->ref, info->route, info->cseg,
+               info->tseg);
+
+       DBG("TODO: Status notification");
+}
+
+static void gsm_report_resp_cb(const GIsiMessage *msg, void *data)
+{
+       if (!check_sms(msg, SMS_GSM_RECEIVED_PP_REPORT_RESP, SMS_OK))
+               DBG("Sending report failed");
+}
+
+static void report_resp_cb(const GIsiMessage *msg, void *data)
+{
+       if (!check_sms(msg, SMS_RECEIVED_MSG_REPORT_RESP, SMS_OK))
+               DBG("Sending report failed");
+}
+
+static gboolean send_gsm_deliver_report(GIsiClient *client, gboolean success,
+                                       void *data, GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               SMS_GSM_RECEIVED_PP_REPORT_REQ,
+               success ? 0 : SMS_CAUSE_TYPE_GSM,
+               success ? SMS_OK : SMS_GSM_ERR_MEMORY_CAPACITY_EXC,
+               0, 0, 0,        /* Filler */
+               1,              /* Sub blocks */
+               SMS_GSM_DELIVER_REPORT,
+               8,              /* Subblock length */
+               0,              /* Message parameters */
+               0,              /* Cause type */
+               0, 0, 0,        /* Filler */
+               0,              /* Sub blocks */
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), gsm_report_resp_cb,
+                                       data, destroy);
+}
+
+static gboolean send_deliver_report(GIsiClient *client, gboolean success,
+                                       void *data, GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               SMS_RECEIVED_MSG_REPORT_REQ,
+               success ? 0 : SMS_CAUSE_TYPE_GSM,
+               success ? SMS_OK : SMS_GSM_ERR_MEMORY_CAPACITY_EXC,
+               0, 0, 0,        /* Filler */
+               0,              /* Subblocks */
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), report_resp_cb,
+                                       data, destroy);
+}
+
+static gboolean parse_sms_address(GIsiSubBlockIter *iter, unsigned offset,
+                                       struct sms_addr *add)
+{
+       add->data = NULL;
+
+       if (!g_isi_sb_iter_get_byte(iter, &add->type, offset))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &add->len, offset + 1))
+               return FALSE;
+
+       if (add->len == 0)
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_struct(iter, (void **) &add->data, add->len,
+                                       offset + 2))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean parse_sms_tpdu(GIsiSubBlockIter *iter, unsigned offset,
+                               struct sms_common *com)
+{
+       com->data = NULL;
+
+       if (!g_isi_sb_iter_get_byte(iter, &com->len, offset))
+               return FALSE;
+
+       if (com->len == 0)
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_struct(iter, (void **) &com->data, com->len,
+                                       offset + 2))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean parse_gsm_tpdu(GIsiSubBlockIter *parent, struct sms_addr *add,
+                               struct sms_common *com)
+{
+       GIsiSubBlockIter iter;
+
+       for (g_isi_sb_subiter_init(parent, &iter, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case SMS_ADDRESS:
+
+                       if (!parse_sms_address(&iter, 2, add))
+                               return FALSE;
+
+                       if (add->type != SMS_GSM_0411_ADDRESS)
+                               return FALSE;
+
+                       break;
+
+               case SMS_COMMON_DATA:
+
+                       if (!parse_sms_tpdu(&iter, 2, com))
+                               return FALSE;
+
+                       break;
+               }
+       }
+
+       return TRUE;
+}
+
+static void routing_ntf_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sms *sms = data;
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       struct sms_common tpdu;
+       struct sms_addr addr;
+       GIsiSubBlockIter iter;
+
+       uint8_t pdu[176];
+
+       if (g_isi_msg_id(msg) != SMS_PP_ROUTING_NTF)
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != SMS_GSM_TPDU)
+                       continue;
+
+               if (!parse_gsm_tpdu(&iter, &addr, &tpdu))
+                       return;
+       }
+
+       if (tpdu.data == NULL || addr.data == NULL ||
+                       tpdu.len + addr.len > sizeof(pdu))
+               return;
+
+       memcpy(pdu, addr.data, addr.len);
+       memcpy(pdu + addr.len, tpdu.data, tpdu.len);
+
+       /* 23.040 9.2.3.1 */
+       if ((tpdu.data[0] & 0x03) == 0x02)
+               ofono_sms_status_notify(sms, pdu, tpdu.len + addr.len, tpdu.len);
+       else
+               ofono_sms_deliver_notify(sms, pdu, tpdu.len + addr.len, tpdu.len);
+
+       send_gsm_deliver_report(sd->client, TRUE, NULL, NULL);
+}
+
+static void received_msg_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sms *sms = data;
+       struct sms_data *sd = ofono_sms_get_data(sms);
+       struct sms_common tpdu;
+       struct sms_addr addr;
+       GIsiSubBlockIter iter;
+
+       uint8_t pdu[176];
+       uint8_t sbcount;
+
+       DBG("");
+
+       if (g_isi_msg_id(msg) != SMS_RECEIVED_MSG_IND)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &sbcount))
+               return;
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 2, TRUE, sbcount);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case SMS_ADDRESS:
+
+                       if (!parse_sms_address(&iter, 4, &addr))
+                               return;
+
+                       if (addr.type != SMS_SMSC_ADDRESS)
+                               return;
+
+                       break;
+
+               case SMS_SB_TPDU:
+
+                       if (!parse_sms_tpdu(&iter, 4, &tpdu))
+                               return;
+
+                       break;
+               }
+       }
+
+       if (tpdu.data == NULL || addr.data == NULL ||
+                       tpdu.len + addr.len > sizeof(pdu))
+               return;
+
+       memcpy(pdu, addr.data, addr.len);
+       memcpy(pdu + addr.len, tpdu.data, tpdu.len);
+
+       /* 23.040 9.2.3.1 */
+       if ((tpdu.data[0] & 0x03) == 0x02)
+               ofono_sms_status_notify(sms, pdu, tpdu.len + addr.len, tpdu.len);
+       else
+               ofono_sms_deliver_notify(sms, pdu, tpdu.len + addr.len, tpdu.len);
+
+       send_deliver_report(sd->client, TRUE, NULL, NULL);
+}
+
+static void reception_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sms *sms = data;
+
+       if (sms == NULL)
+               return;
+
+       if (!check_sms(msg, SMS_RECEIVE_MESSAGE_RESP, SMS_RECEPTION_ACTIVE))
+               return;
+
+       ofono_sms_register(sms);
+}
+
+static void routing_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sms *sms = data;
+
+       if (sms == NULL)
+               return;
+
+       if (!check_sms(msg, SMS_PP_ROUTING_RESP, SMS_OK))
+               return;
+
+       ofono_sms_register(sms);
+}
+
+static void sim_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sms *sms = data;
+       struct sms_data *sd = ofono_sms_get_data(sms);
+
+       if (sd == NULL)
+               return;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Unable to bootstrap SIM service");
+
+               g_isi_client_destroy(sd->sim);
+               sd->sim = NULL;
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+}
+
+static gboolean set_routing(GIsiClient *client, void *data,
+                               GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               SMS_PP_ROUTING_REQ,
+               SMS_ROUTING_SET,
+               1,              /* Sub-block count */
+               SMS_GSM_ROUTING,
+               8,              /* Sub-block length */
+               SMS_GSM_TPDU_ROUTING,
+               SMS_GSM_MT_ALL_TYPE,
+               0, 0, 0,        /* Filler */
+               0,              /* Sub-sub-block count */
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), routing_resp_cb,
+                                       data, destroy);
+}
+
+static gboolean unset_routing(GIsiClient *client)
+{
+       const uint8_t msg[] = {
+               SMS_PP_ROUTING_REQ,
+               SMS_ROUTING_RELEASE,
+               0x01,           /* Sub-block count */
+               SMS_GSM_ROUTING,
+               0x08,           /* Sub-block length */
+               SMS_GSM_TPDU_ROUTING,
+               SMS_GSM_MT_ALL_TYPE,
+               0, 0, 0,        /* Filler */
+               0,              /* Sub-sub-block count */
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), NULL, NULL, NULL);
+}
+
+static gboolean activate_reception(GIsiClient *client, void *data,
+                                       GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               SMS_RECEIVE_MESSAGE_REQ,
+               SMS_RECEPTION_ACTIVATE,
+               0,
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), reception_resp_cb,
+                                       data, destroy);
+}
+
+static gboolean deactivate_reception(GIsiClient *client)
+{
+       const uint8_t msg[] = {
+               SMS_RECEIVE_MESSAGE_REQ,
+               SMS_RECEPTION_DEACTIVATE,
+               0,
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), NULL, NULL, NULL);
+}
+
+static void sms_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sms *sms = data;
+       struct sms_data *sd = ofono_sms_get_data(sms);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("unable to find SMS resource");
+               ofono_sms_remove(sms);
+               return;
+       }
+
+       if (sd == NULL)
+               return;
+
+       ISI_RESOURCE_DBG(msg);
+
+       sd->version.major = g_isi_msg_version_major(msg);
+       sd->version.minor = g_isi_msg_version_minor(msg);
+
+       if (ISI_VERSION_AT_LEAST(&sd->version, 9, 1))
+               activate_reception(sd->client, sms, NULL);
+       else
+               set_routing(sd->client, sms, NULL);
+
+       g_isi_client_verify(sd->sim, sim_reachable_cb, sms, NULL);
+}
+
+static int isi_sms_probe(struct ofono_sms *sms, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *modem = user;
+       struct sms_data *sd = g_try_new0(struct sms_data, 1);
+
+       if (sd == NULL)
+               return -ENOMEM;
+
+       sd->params.absent = 0xFF;
+       sd->params.alphalen = 1; /* Includes final UCS2-coded NUL */
+
+       sd->client = g_isi_client_create(modem, PN_SMS);
+       if (sd->client == NULL)
+               goto nomem;
+
+       sd->sim = g_isi_client_create(modem, PN_SIM);
+       if (sd->sim == NULL)
+               goto nomem;
+
+       ofono_sms_set_data(sms, sd);
+
+       g_isi_client_ind_subscribe(sd->client, SMS_MESSAGE_SEND_STATUS_IND,
+                                       send_status_ind_cb, sms);
+       g_isi_client_ind_subscribe(sd->client, SMS_RECEIVED_MSG_IND,
+                                       received_msg_ind_cb, sms);
+       g_isi_client_ntf_subscribe(sd->client, SMS_PP_ROUTING_NTF,
+                                       routing_ntf_cb, sms);
+       g_isi_client_verify(sd->client, sms_reachable_cb, sms, NULL);
+
+       return 0;
+
+nomem:
+       g_isi_client_destroy(sd->client);
+       g_free(sd);
+       return -ENOMEM;
+}
+
+static void isi_sms_remove(struct ofono_sms *sms)
+{
+       struct sms_data *sd = ofono_sms_get_data(sms);
+
+       if (sd == NULL)
+               return;
+
+       ofono_sms_set_data(sms, NULL);
+
+       /*
+        * Send a promiscuous routing release, so as not to
+        * hog resources unnecessarily after being removed
+        */
+       if (ISI_VERSION_AT_LEAST(&sd->version, 9, 1))
+               deactivate_reception(sd->client);
+       else
+               unset_routing(sd->client);
+
+       g_isi_client_destroy(sd->client);
+       g_isi_client_destroy(sd->sim);
+       g_free(sd);
+}
+
+static struct ofono_sms_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_sms_probe,
+       .remove                 = isi_sms_remove,
+       .sca_query              = isi_sca_query,
+       .sca_set                = isi_sca_set,
+       .submit                 = isi_submit,
+       .bearer_query           = isi_bearer_query,
+       .bearer_set             = isi_bearer_set,
+};
+
+void isi_sms_init(void)
+{
+       ofono_sms_driver_register(&driver);
+}
+
+void isi_sms_exit(void)
+{
+       ofono_sms_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/sms.h b/drivers/isimodem/sms.h
new file mode 100644 (file)
index 0000000..e53312f
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_SMS_H
+#define __ISIMODEM_SMS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_SMS                                 0x02
+#define CBS_TIMEOUT                            5
+#define SMS_TIMEOUT                            5
+
+enum sms_isi_cause {
+       SMS_OK =                                0x00,
+       SMS_ERR_ROUTING_RELEASED =              0x01,
+       SMS_ERR_INVALID_PARAMETER =             0x02,
+       SMS_ERR_DEVICE_FAILURE =                0x03,
+       SMS_ERR_PP_RESERVED =                   0x04,
+       SMS_ERR_ROUTE_NOT_AVAILABLE =           0x05,
+       SMS_ERR_ROUTE_NOT_ALLOWED =             0x06,
+       SMS_ERR_SERVICE_RESERVED =              0x07,
+       SMS_ERR_INVALID_LOCATION =              0x08,
+       SMS_ERR_NO_SIM =                        0x09,
+       SMS_ERR_SIM_NOT_READY =                 0x0A,
+       SMS_ERR_NO_NETW_RESPONSE =              0x0B,
+       SMS_ERR_DEST_ADDR_FDN_RESTRICTED =      0x0C,
+       SMS_ERR_SMSC_ADDR_FDN_RESTRICTED =      0x0D,
+       SMS_ERR_RESEND_ALREADY_DONE =           0x0E,
+       SMS_ERR_SMSC_ADDR_NOT_AVAILABLE =       0x0F,
+       SMS_ERR_ROUTING_FAILED =                0x10,
+       SMS_ERR_CS_INACTIVE =                   0x11,
+       SMS_ERR_SAT_MO_CONTROL_MODIFIED =       0x12,
+       SMS_ERR_SAT_MO_CONTROL_REJECT =         0x13,
+       SMS_ERR_TRACFONE_FAILED =               0x14,
+};
+
+enum sms_isi_cause_type {
+       SMS_CAUSE_TYPE_COMMON =         0x00,
+       SMS_CAUSE_TYPE_GSM =            0x01,
+};
+
+enum sms_gsm_cause {
+       SMS_GSM_ERR_UNASSIGNED_NUMBER =                         0x01,
+       SMS_GSM_ERR_OPER_DETERMINED_BARR =                      0x08,
+       SMS_GSM_ERR_CALL_BARRED =                               0x0A,
+       SMS_GSM_ERR_RESERVED =                                  0x0B,
+       SMS_GSM_ERR_MSG_TRANSFER_REJ =                          0x15,
+       SMS_GSM_ERR_MEMORY_CAPACITY_EXC =                       0x16,
+       SMS_GSM_ERR_DEST_OUT_OF_ORDER =                         0x1B,
+       SMS_GSM_ERR_UNDEFINED_SUBSCRIBER =                      0x1C,
+       SMS_GSM_ERR_FACILITY_REJECTED =                         0x1D,
+       SMS_GSM_ERR_UNKNOWN_SUBSCRIBER =                        0x1E,
+       SMS_GSM_ERR_NETW_OUT_OF_ORDER =                         0x26,
+       SMS_GSM_ERR_TEMPORARY_FAILURE =                         0x29,
+       SMS_GSM_ERR_CONGESTION =                                0x2A,
+       SMS_GSM_ERR_RESOURCE_UNAVAILABLE =                      0x2F,
+       SMS_GSM_ERR_REQ_FACILITY_NOT_SUB =                      0x32,
+       SMS_GSM_ERR_REQ_FACILITY_NOT_IMP =                      0x45,
+       SMS_GSM_ERR_INVALID_REFERENCE =                         0x51,
+       SMS_GSM_ERR_INCORRECT_MESSAGE =                         0x5F,
+       SMS_GSM_ERR_INVALID_MAND_INFO =                         0x60,
+       SMS_GSM_ERR_INVALID_MSG_TYPE =                          0x61,
+       SMS_GSM_ERR_MSG_NOT_COMP_WITH_ST =                      0x62,
+       SMS_GSM_ERR_INVALID_INFO_ELEMENT =                      0x63,
+       SMS_GSM_ERR_PROTOCOL_ERROR =                            0x6F,
+       SMS_GSM_ERR_INTERWORKING =                              0x7F,
+       SMS_GSM_ERR_NO_CAUSE =                                  0x80,
+       SMS_GSM_ERR_IMSI_UNKNOWN_HLR =                          0x82,
+       SMS_GSM_ERR_ILLEGAL_MS =                                0x83,
+       SMS_GSM_ERR_IMSI_UNKNOWN_VLR =                          0x84,
+       SMS_GSM_ERR_IMEI_NOT_ACCEPTED =                         0x85,
+       SMS_GSM_ERR_ILLEGAL_ME =                                0x86,
+       SMS_GSM_ERR_PLMN_NOT_ALLOWED =                          0x8B,
+       SMS_GSM_ERR_LA_NOT_ALLOWED =                            0x8C,
+       SMS_GSM_ERR_ROAM_NOT_ALLOWED_LA =                       0x8D,
+       SMS_GSM_ERR_NO_SUITABLE_CELLS_LA =                      0x8F,
+       SMS_GSM_ERR_NETWORK_FAILURE =                           0x91,
+       SMS_GSM_ERR_MAC_FAILURE =                               0x94,
+       SMS_GSM_ERR_SYNC_FAILURE =                              0x95,
+       SMS_GSM_ERR_LOW_LAYER_CONGESTION =                      0x96,
+       SMS_GSM_ERR_AUTH_UNACCEPTABLE =                         0x97,
+       SMS_GSM_ERR_SERV_OPT_NOT_SUPPORTED =                    0xA0,
+       SMS_GSM_ERR_SERV_OPT_NOT_SUBSCRIBED =                   0xA1,
+       SMS_GSM_ERR_SERV_OPT_TEMP_OUT_OF_ORDER =                0xA2,
+       SMS_GSM_ERR_CALL_CANNOT_BE_IDENTIFIED =                 0xA6,
+       SMS_GSM_ERR_SEMANTICALLY_INCORR_MSG =                   0xDF,
+       SMS_GSM_ERR_LOW_LAYER_INVALID_MAND_INFO =               0xE0,
+       SMS_GSM_ERR_LOW_LAYER_INVALID_MSG_TYPE =                0xE1,
+       SMS_GSM_ERR_LOW_LAYER_MSG_TYPE_NOT_COMP_WITH_ST =       0xE2,
+       SMS_GSM_ERR_LOW_LAYER_INVALID_INFO_ELEMENT =            0xE3,
+       SMS_GSM_ERR_CONDITIONAL_IE_ERROR =                      0xE4,
+       SMS_GSM_ERR_LOW_LAYER_MSG_NOT_COMP_WITH_ST =            0xE5,
+       SMS_GSM_ERR_CS_BARRED =                                 0xE8,
+       SMS_GSM_ERR_LOW_LAYER_PROTOCOL_ERROR =                  0xEF,
+};
+
+enum sms_message_id {
+       SMS_MESSAGE_SEND_REQ =                  0x02,
+       SMS_MESSAGE_SEND_RESP =                 0x03,
+       SMS_PP_ROUTING_REQ =                    0x06,
+       SMS_PP_ROUTING_RESP =                   0x07,
+       SMS_PP_ROUTING_NTF =                    0x08,
+       SMS_GSM_RECEIVED_PP_REPORT_REQ =        0x09,
+       SMS_GSM_RECEIVED_PP_REPORT_RESP =       0x0A,
+       SMS_GSM_CB_ROUTING_REQ =                0x0B,
+       SMS_GSM_CB_ROUTING_RESP =               0x0C,
+       SMS_GSM_CB_ROUTING_NTF =                0x0D,
+       SMS_MESSAGE_SEND_STATUS_IND =           0x22,
+       SMS_SETTINGS_UPDATE_REQ =               0x30,
+       SMS_SETTINGS_UPDATE_RESP =              0x31,
+       SMS_SETTINGS_READ_REQ =                 0x32,
+       SMS_SETTINGS_READ_RESP =                0x33,
+       SMS_RECEIVED_MSG_REPORT_REQ =           0x3B,
+       SMS_RECEIVED_MSG_REPORT_RESP =          0x3C,
+       SMS_RECEIVE_MESSAGE_REQ =               0x41,
+       SMS_RECEIVE_MESSAGE_RESP =              0x42,
+       SMS_RECEIVED_MSG_IND =                  0x43,
+};
+
+enum sms_subblock {
+       SMS_GSM_DELIVER =               0x00,
+       SMS_GSM_STATUS_REPORT =         0x01,
+       SMS_GSM_SUBMIT =                0x02,
+       SMS_GSM_COMMAND =               0x03,
+       SMS_GSM_DELIVER_REPORT =        0x06,
+       SMS_GSM_REPORT =                0x0C,
+       SMS_GSM_ROUTING =               0x0D,
+       SMS_GSM_CB_MESSAGE =            0x0E,
+       SMS_GSM_TPDU =                  0x11,
+       SMS_SB_TPDU =                   0x001C,
+       SMS_SB_ROUTE_INFO =             0x0023,
+       SMS_SB_SMS_PARAMETERS =         0x0031,
+       SMS_COMMON_DATA =               0x80,
+       SMS_ADDRESS =                   0x82,
+       SMS_SB_ADDRESS =                0x0082,
+};
+
+enum sms_routing_command {
+       SMS_ROUTING_RELEASE =           0x00,
+       SMS_ROUTING_SET =               0x01,
+       SMS_ROUTING_SUSPEND =           0x02,
+       SMS_ROUTING_RESUME =            0x03,
+       SMS_ROUTING_UPDATE =            0x04,
+};
+
+enum sms_route_preference {
+       SMS_ROUTE_ANY =                 0x00,
+       SMS_ROUTE_GPRS_PREF =           0x00,
+       SMS_ROUTE_CS =                  0x01,
+       SMS_ROUTE_GPRS =                0x02,
+       SMS_ROUTE_CS_PREF =             0x03,
+       SMS_ROUTE_DEFAULT =             0x04,
+};
+
+enum sms_routing_mode {
+       SMS_GSM_ROUTING_MODE_ALL =      0x0B,
+       SMS_GSM_ROUTING_MODE_CB_DDL =   0x0C,
+};
+
+enum sms_routing_type {
+       SMS_GSM_TPDU_ROUTING =          0x06,
+};
+
+enum sms_message_type {
+       SMS_GSM_MT_ALL_TYPE =           0x06,
+};
+
+enum sms_address_type {
+       SMS_UNICODE_ADDRESS =           0x00,
+       SMS_GSM_0340_ADDRESS =          0x01,
+       SMS_GSM_0411_ADDRESS =          0x02,
+       SMS_SMSC_ADDRESS =              0x02,
+};
+
+enum sms_sender_type {
+       SMS_SENDER_ANY =                0x00,
+       SMS_SENDER_SIM_ATK =            0x01,
+};
+
+enum sms_content_type {
+       SMS_TYPE_DEFAULT =              0x00,
+       SMS_TYPE_TEXT_MESSAGE =         0x01,
+};
+
+enum sms_subject_list_type {
+       SMS_CB_ALLOWED_IDS_LIST =       0x00,
+       SMS_CB_NOT_ALLOWED_IDS_LIST =   0x01,
+};
+
+enum sms_reception_command {
+       SMS_RECEPTION_ACTIVATE =        0x01,
+       SMS_RECEPTION_DEACTIVATE =      0x02,
+};
+
+enum sms_reception_status {
+       SMS_RECEPTION_ACTIVE =          0x01,
+       SMS_RECEPTION_INACTIVE =        0x02,
+};
+
+enum sms_setting_type {
+       SMS_SETTING_TYPE_ROUTE =        0x02,
+};
+
+enum sms_route_priority {
+       SMS_ROUTE_NOT_AVAILABLE =       0x00,
+       SMS_ROUTE_PRIORITY_1 =          0x01,
+       SMS_ROUTE_PRIORITY_2 =          0x02,
+};
+
+enum sms_parameter_indicator {
+       SMS_PI_DESTINATION_ADDRESS =    0x01,
+       SMS_PI_SERVICE_CENTER_ADDRESS = 0x02,
+       SMS_PI_PROTOCOL_ID =            0x04,
+       SMS_PI_DATA_CODING_SCHEME =     0x08,
+       SMS_PI_VALIDITY_PERIOD =        0x10,
+};
+
+enum sms_parameter_location {
+       SMS_PARAMETER_LOCATION_DEFAULT =        0x00,
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __ISIMODEM_SMS_H */
diff --git a/drivers/isimodem/ss.h b/drivers/isimodem/ss.h
new file mode 100644 (file)
index 0000000..e3fc770
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_SS_H
+#define __ISIMODEM_SS_H
+
+#define PN_SS                                  0x06
+#define SS_TIMEOUT                             15
+#define SS_MAX_USSD_LENGTH                     160
+
+enum ss_message_id {
+       SS_SERVICE_REQ =                        0x00,
+       SS_SERVICE_COMPLETED_RESP =             0x01,
+       SS_SERVICE_FAILED_RESP =                0x02,
+       SS_SERVICE_NOT_SUPPORTED_RESP =         0x03,
+       SS_GSM_USSD_SEND_REQ =                  0x04,
+       SS_GSM_USSD_SEND_RESP =                 0x05,
+       SS_GSM_USSD_RECEIVE_IND =               0x06,
+       SS_STATUS_IND =                         0x09,
+       SS_SERVICE_COMPLETED_IND =              0x10,
+};
+
+enum ss_ussd_type {
+       SS_GSM_USSD_MT_REPLY =                  0x01,
+       SS_GSM_USSD_COMMAND =                   0x02,
+       SS_GSM_USSD_REQUEST =                   0x03,
+       SS_GSM_USSD_NOTIFY =                    0x04,
+       SS_GSM_USSD_END =                       0x05
+};
+
+enum ss_ussd_status {
+       SS_GSM_STATUS_REQUEST_USSD_START =      0x02,
+       SS_GSM_STATUS_REQUEST_USSD_STOP =       0x03,
+       SS_GSM_STATUS_REQUEST_USSD_FAILED =     0x04
+};
+
+enum ss_operations {
+       SS_ACTIVATION =                         0x01,
+       SS_DEACTIVATION =                       0x02,
+       SS_REGISTRATION =                       0x03,
+       SS_ERASURE =                            0x04,
+       SS_INTERROGATION =                      0x05,
+       SS_GSM_PASSWORD_REGISTRATION =          0x06
+};
+
+enum ss_basic_service_codes {
+       SS_ALL_TELE_AND_BEARER =                0,
+       SS_GSM_ALL_TELE =                       10,
+       SS_GSM_TELEPHONY =                      11,
+       SS_GSM_ALL_DATA_TELE =                  12,
+       SS_GSM_FACSIMILE =                      13,
+       SS_GSM_SMS =                            16,
+       SS_GSM_VOICE_GROUP =                    17,
+       SS_GSM_ALL_TELE_EXC_SMS =               19,
+       SS_GSM_ALL_BEARER =                     20,
+       SS_GSM_ALL_ASYNC =                      21,
+       SS_GSM_ALL_SYNC =                       22,
+       SS_GSM_ALL_DATA_CIRCUIT_SYNC =          24,
+       SS_GSM_ALL_DATA_CIRCUIT_ASYNC =         25,
+       SS_GSM_ALL_DATA_PACKET_SYNC =           26,
+       SS_GSM_ALL_PAD_ACCESS =                 27
+};
+
+enum ss_codes {
+       SS_GSM_ALL_FORWARDINGS =                002,
+       SS_GSM_ALL_COND_FORWARDINGS =           004,
+       SS_GSM_FORW_UNCONDITIONAL =             21,
+       SS_GSM_BARR_ALL_OUT =                   33,
+       SS_GSM_OUTGOING_BARR_SERV =             333,
+       SS_GSM_INCOMING_BARR_SERV =             353,
+       SS_GSM_BARR_ALL_IN =                    35,
+       SS_GSM_CALL_WAITING =                   43,
+       SS_GSM_FORW_NO_REPLY =                  61,
+       SS_GSM_FORW_NO_REACH =                  62,
+       SS_GSM_FORW_BUSY =                      67,
+       SS_GSM_ALL_BARRINGS =                   330,
+       SS_GSM_BARR_OUT_INTER =                 331,
+       SS_GSM_BARR_OUT_INTER_EXC_HOME =        332,
+       SS_GSM_BARR_ALL_IN_ROAM =               351,
+       SS_GSM_CLIP =                           0x001E,
+       SS_GSM_CLIR =                           0x001F,
+       SS_GSM_COLP =                           0x004C,
+       SS_GSM_COLR =                           0x004D,
+       SS_GSM_CNAP =                           0x012C,
+       SS_GSM_ECT =                            0x0060
+};
+
+enum ss_response_data {
+       SS_SEND_ADDITIONAL_INFO =               0x01,
+};
+
+enum ss_subblock {
+       SS_FORWARDING =                         0x00,
+       SS_STATUS_RESULT =                      0x01,
+       SS_GSM_PASSWORD =                       0x03,
+       SS_GSM_FORWARDING_INFO =                0x04,
+       SS_GSM_FORWARDING_FEATURE =             0x05,
+       SS_GSM_BARRING_INFO =                   0x06,
+       SS_GSM_BARRING_FEATURE =                0x07,
+       SS_GSM_DATA =                           0x08,
+       SS_GSM_BSC_INFO =                       0x09,
+       SS_GSM_GENERIC_SERVICE_INFO =           0x0A,
+       SS_GSM_PASSWORD_INFO =                  0x0B,
+       SS_GSM_CLIR_INFO =                      0x0C,
+       SS_GSM_INDICATE_PASSWORD_ERROR =        0x0D,
+       SS_GSM_INDICATE_ERROR =                 0x0E,
+       SS_GSM_ADDITIONAL_INFO =                0x2F,
+       SS_GSM_USSD_STRING =                    0x32
+};
+
+enum ss_isi_cause {
+       SS_GSM_ACTIVE =                         0x01,
+       SS_GSM_REGISTERED =                     0x02,
+       SS_GSM_PROVISIONED =                    0x04,
+       SS_GSM_QUIESCENT =                      0x08,
+};
+
+enum ss_gsm_cli_restriction_option {
+       SS_GSM_CLI_PERMANENT =                  0x00,
+       SS_GSM_DEFAULT_RESTRICTED =             0x01,
+       SS_GSM_CLI_DEFAULT_ALLOWED =            0x02,
+       SS_GSM_OVERRIDE_ENABLED =               0x03,
+       SS_GSM_OVERRIDE_DISABLED =              0x04
+};
+
+enum ss_constants {
+       SS_UNDEFINED_TIME =                     0x00,
+};
+
+/* TS 27.007 Supplementary service notifications +CSSN */
+enum ss_cssi {
+       SS_MO_UNCONDITIONAL_FORWARDING =        0,
+       SS_MO_CONDITIONAL_FORWARDING =          1,
+       SS_MO_CALL_FORWARDED =                  2,
+       SS_MO_CALL_WAITING =                    3,
+       SS_MO_CUG_CALL =                        4,
+       SS_MO_OUTGOING_BARRING =                5,
+       SS_MO_INCOMING_BARRING =                6,
+       SS_MO_CLIR_SUPPRESSION_REJECTED =       7,
+       SS_MO_CALL_DEFLECTED =                  8,
+};
+
+enum ss_cssu {
+       SS_MT_CALL_FORWARDED =                  0,
+       SS_MT_CUG_CALL =                        1,
+       SS_MT_VOICECALL_ON_HOLD =               2,
+       SS_MT_VOICECALL_RETRIEVED =             3,
+       SS_MT_MULTIPARTY_VOICECALL =            4,
+       SS_MT_VOICECALL_HOLD_RELEASED =         5,
+       SS_MT_FORWARD_CHECK_SS_MESSAGE =        6,
+       SS_MT_VOICECALL_IN_TRANSFER =           7,
+       SS_MT_VOICECALL_TRANSFERRED =           8,
+       SS_MT_CALL_DEFLECTED =                  9,
+};
+
+#endif /* __ISIMODEM_SS_H */
diff --git a/drivers/isimodem/uicc-util.c b/drivers/isimodem/uicc-util.c
new file mode 100644 (file)
index 0000000..f04ca5a
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sim.h>
+
+#include "simutil.h"
+#include "sim.h"
+#include "uicc-util.h"
+#include "uicc.h"
+#include "debug.h"
+
+#define USIM_APP_DEDICATED_FILE                0x7FFF
+
+gboolean uicc_get_fileid_path(struct uicc_sim_data *sd,
+                               int *mf_path,
+                               int *df1_path,
+                               int *df2_path,
+                               unsigned char *df_len,
+                               int fileid)
+{
+       switch (fileid) {
+       case SIM_EFPL_FILEID:
+       case SIM_EF_ICCID_FILEID:
+               *mf_path = SIM_MF_FILEID;
+               *df1_path = 0x0000;
+               *df2_path = 0x0000;
+               *df_len = 2;
+               break;
+       case SIM_EFSMSP_FILEID:
+       case SIM_EFSDN_FILEID:
+       case SIM_EFMSISDN_FILEID:
+               *mf_path = SIM_MF_FILEID;
+
+               if (sd->app_type == UICC_APPL_TYPE_ICC_SIM)
+                       *df1_path = SIM_DFTELECOM_FILEID;
+               else
+                       *df1_path = USIM_APP_DEDICATED_FILE;
+
+               *df2_path = 0x0000;
+               *df_len = 4;
+               break;
+       case SIM_EFLI_FILEID:
+       case SIM_EFSPN_FILEID:
+       case SIM_EFAD_FILEID:
+       case SIM_EFPNN_FILEID:
+       case SIM_EFOPL_FILEID:
+       case SIM_EFMBDN_FILEID:
+       case SIM_EFMBI_FILEID:
+       case SIM_EFMWIS_FILEID:
+       case SIM_EFSPDI_FILEID:
+       case SIM_EFECC_FILEID:
+       case SIM_EFCBMI_FILEID:
+       case SIM_EFCBMIR_FILEID:
+       case SIM_EFCBMID_FILEID:
+       case SIM_EFIMSI_FILEID:
+       case SIM_EFPHASE_FILEID: /*Did not find in TS 31.102 v6.21.0*/
+       case SIM_EFARR_FILEID:
+       case SIM_EF_CPHS_INFORMATION_FILEID: /*Found from unofficial source*/
+               *mf_path = SIM_MF_FILEID;
+
+               if (sd->app_type == UICC_APPL_TYPE_ICC_SIM)
+                       *df1_path = SIM_DFGSM_FILEID;
+               else
+                       *df1_path = USIM_APP_DEDICATED_FILE;
+
+               *df2_path = 0x0000;
+               *df_len = 4;
+               break;
+               /* No info */
+       case SIM_EF_CPHS_MBDN_FILEID:
+       case SIM_EF_CPHS_MWIS_FILEID:
+               DBG("======== No path info for %04X", fileid);
+               return FALSE;
+       case SIM_EFADN_FILEID: /* Only for SIM */
+       case SIM_EFEXT1_FILEID: /* Only for SIM */
+               *mf_path = SIM_MF_FILEID;
+               *df1_path = SIM_DFTELECOM_FILEID;
+               *df2_path = 0x0000;
+               *df_len = 4;
+               break;
+       default:
+               *mf_path = SIM_MF_FILEID;
+               *df1_path = SIM_DFTELECOM_FILEID;
+               *df2_path = SIM_DFPHONEBOOK_FILEID;
+               *df_len = 6;
+               break;
+       }
+
+       return TRUE;
+}
+
+uint8_t uicc_get_sfi(const int fileid)
+{
+       /* SFI list from 3GPP TS 31.102 Annex H */
+       switch (fileid) {
+       case SIM_EFECC_FILEID:
+               return 01;
+       case SIM_EFLI_FILEID:
+               return 02;
+       case SIM_EFAD_FILEID:
+               return 03;
+       case SIM_EFIMSI_FILEID:
+               return 07;
+       case SIM_EFCBMID_FILEID:
+               return 0x0E;
+       case SIM_EFPNN_FILEID:
+               return 0x19;
+       case SIM_EFOPL_FILEID:
+               return 0x1A;
+       default:
+               return UICC_SFI_NOT_PRESENT;
+       }
+}
diff --git a/drivers/isimodem/uicc-util.h b/drivers/isimodem/uicc-util.h
new file mode 100644 (file)
index 0000000..7c8179e
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_UICC_UTIL_H
+#define __ISIMODEM_UICC_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <glib/gtypes.h>
+#include <gisi/client.h>
+
+struct uicc_sim_data;
+
+struct uicc_sim_application {
+       int id;
+       uint8_t type;
+       uint8_t status;
+       uint8_t length;
+
+       struct uicc_sim_data *sim;
+};
+
+struct uicc_sim_data {
+       GIsiClient *client;
+       unsigned flags;
+       int app_id;
+       int app_type;
+       uint8_t client_id;
+
+       GIsiVersion version;
+
+       gboolean server_running;
+
+       gboolean pin_state_received;
+       gboolean passwd_required;
+
+       /* Application state */
+       gboolean uicc_app_started;
+       uint8_t trying_app_id;
+       uint8_t trying_app_type;
+       GHashTable *app_table;
+
+       uint8_t pin1_id;
+       uint8_t pin2_id;
+};
+
+gboolean uicc_get_fileid_path(struct uicc_sim_data *sd,
+                               int *mf_path,
+                               int *df1_path,
+                               int *df2_path,
+                               unsigned char *df_len,
+                               int fileid);
+
+uint8_t uicc_get_sfi(const int fileid);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __ISIMODEM_UICC_UTIL_H */
diff --git a/drivers/isimodem/uicc.c b/drivers/isimodem/uicc.c
new file mode 100644 (file)
index 0000000..df79ae4
--- /dev/null
@@ -0,0 +1,1704 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/message.h>
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sim.h>
+
+#include "simutil.h"
+#include "isimodem.h"
+#include "isiutil.h"
+#include "sim.h"
+#include "uicc.h"
+#include "uicc-util.h"
+#include "debug.h"
+
+/* File info parameters */
+#define FCP_TEMPLATE                   0x62
+#define FCP_FILE_SIZE                  0x80
+#define FCP_FILE_DESC                  0x82
+#define FCP_FILE_ID                    0x83
+#define FCP_FILE_LIFECYCLE             0x8A
+#define FCP_FILE_SECURITY_ARR          0x8B
+#define FCP_FILE_SECURITY_COMPACT      0x8C
+#define FCP_FILE_SECURITY_EXPANDED     0xAB
+#define FCP_PIN_STATUS                 0xC6
+#define SIM_EFARR_FILEID               0x6f06
+#define MAX_SIM_APPS                   10
+#define MAX_IMSI_LENGTH                        15
+
+enum uicc_flag {
+       UICC_FLAG_APP_STARTED =         1 << 0,
+       UICC_FLAG_PIN_STATE_RECEIVED =  1 << 1,
+       UICC_FLAG_PASSWD_REQUIRED =     1 << 2,
+};
+
+static GHashTable *g_modems;
+
+struct file_info {
+       int fileid;
+       int length;
+       int structure;
+       int record_length;
+       uint8_t access[3];
+       uint8_t file_status;
+};
+
+static const struct file_info static_file_info[] = {
+       { SIM_EFSPN_FILEID, 17, 0, 0, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EF_ICCID_FILEID, 10, 0, 10, { 0x0f, 0xff, 0xee }, 1 },
+       { SIM_EFPL_FILEID, 1, 0, 1, { 0x0f, 0xff, 0xff }, 1 },
+       { SIM_EFLI_FILEID, 1, 0, 1, { 0x0f, 0xff, 0xff }, 1 },
+       { SIM_EFMSISDN_FILEID, 28, 1, 28, { 0x01, 0xff, 0xee }, 1 },
+       { SIM_EFAD_FILEID, 20, 0, 20, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EFPHASE_FILEID, 1, 0, 1, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EFPNN_FILEID, 4 * 18, 1, 18, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EFOPL_FILEID, 4 * 24, 1, 24, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EFMBI_FILEID, 5, 1, 5, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EFMWIS_FILEID, 6, 1, 6, { 0x01, 0xff, 0xee }, 1 },
+       { SIM_EFSPDI_FILEID, 64, 0, 64, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EFECC_FILEID, 5 * 3, 0, 3, { 0x0e, 0xff, 0xee }, 1 },
+       { SIM_EFCBMIR_FILEID, 8 * 4, 0, 4, { 0x01, 0xff, 0xee }, 1 },
+       { SIM_EFCBMI_FILEID, 8 * 2, 0, 2, { 0x01, 0xff, 0xee }, 1 },
+       { SIM_EFCBMID_FILEID, 8 * 2, 0, 2, { 0x01, 0xff, 0x11 }, 1 },
+       { SIM_EFSMSP_FILEID, 56, 1, 56, { 0x01, 0xff, 0xee }, 1 },
+       { SIM_EFIMSI_FILEID, 9, 0, 9, { 0x0e, 0xff, 0xee }, 1 },
+};
+
+static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t service)
+{
+       uint8_t type;
+       uint8_t cause;
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       sim_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &cause) ||
+                       cause != UICC_STATUS_OK) {
+               DBG("Request failed: %s", uicc_status_name(cause));
+               return FALSE;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &type) || type != service) {
+               DBG("Unexpected service: 0x%02X (0x%02X)", type, service);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+struct uicc_file_info_cb_data {
+       void *cb;
+       void *data;
+       void *user;
+       struct ofono_sim *sim;
+};
+
+static gboolean decode_uicc_usim_type(GIsiSubBlockIter *iter, uint16_t *length,
+                                       uint16_t *file_id,
+                                       uint16_t *record_length,
+                                       uint8_t *records, uint8_t *structure)
+{
+       uint8_t fcp = 0;
+       uint8_t desc = 0;
+       uint8_t coding = 0;
+       uint8_t fcp_len = 0;
+       uint8_t read = 0;
+       uint8_t item_len = 0;
+
+       if (!g_isi_sb_iter_get_byte(iter, &fcp, 8))
+               return FALSE;
+
+       if (fcp != FCP_TEMPLATE)
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &fcp_len, 9))
+               return FALSE;
+
+       for (read = 0; read < fcp_len; read += item_len + 2) {
+
+               uint8_t id;
+
+               if (!g_isi_sb_iter_get_byte(iter, &id, read + 10))
+                       return FALSE;
+
+               if (!g_isi_sb_iter_get_byte(iter, &item_len, read + 11))
+                       return FALSE;
+
+               switch (id) {
+               case FCP_FILE_SIZE:
+
+                       if (item_len != 2)
+                               return FALSE;
+
+                       if (!g_isi_sb_iter_get_word(iter, length, read + 10 + 2))
+                               return FALSE;
+
+                       break;
+
+               case FCP_FILE_ID:
+
+                       if (item_len != 2)
+                               return FALSE;
+
+                       if (!g_isi_sb_iter_get_word(iter, file_id, read + 10 + 2))
+                               return FALSE;
+
+                       break;
+
+               case FCP_FILE_DESC:
+
+                       if (item_len < 2)
+                               return FALSE;
+
+                       if (!g_isi_sb_iter_get_byte(iter, &desc, read + 10 + 2))
+                               return FALSE;
+
+                       if (!g_isi_sb_iter_get_byte(iter, &coding, read + 10 + 3))
+                               return FALSE;
+
+                       if (item_len < 4)
+                               break;
+
+                       if (!g_isi_sb_iter_get_word(iter, record_length,
+                                                       read + 10 + 4))
+                               return FALSE;
+
+                       if (!g_isi_sb_iter_get_byte(iter, records, read + 10 + 6))
+                               return FALSE;
+
+                       break;
+
+               /*
+                * Not implemented, using static access rules
+                * as these are used only for cacheing See
+                * ETSI TS 102 221, ch 11.1.1.4.7 and Annexes
+                * E, F and G.
+                */
+               case FCP_FILE_SECURITY_ARR:
+               case FCP_FILE_SECURITY_COMPACT:
+               case FCP_FILE_SECURITY_EXPANDED:
+               case FCP_FILE_LIFECYCLE:
+               default:
+                       DBG("FCP id %02X not supported", id);
+                       break;
+               }
+       }
+
+       if ((desc & 7) == 1)
+               *structure = OFONO_SIM_FILE_STRUCTURE_TRANSPARENT;
+       else if ((desc & 7) == 2)
+               *structure = OFONO_SIM_FILE_STRUCTURE_FIXED;
+       else if ((desc & 7) == 6)
+               *structure = OFONO_SIM_FILE_STRUCTURE_CYCLIC;
+
+       return TRUE;
+}
+
+static void uicc_file_info_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct uicc_file_info_cb_data *cbd = opaque;
+       struct uicc_sim_data *sd = ofono_sim_get_data(cbd->sim);
+       struct file_info const *info = cbd->user;
+       ofono_sim_file_info_cb_t cb = cbd->cb;
+
+       GIsiSubBlockIter iter;
+
+       uint16_t length = 0;
+       uint16_t record_length = 0;
+       uint8_t structure = 0xFF;
+       uint8_t records = 0;
+       uint16_t file_id = 0;
+       uint8_t access[3] = {0, 0, 0};
+       uint8_t item_len = 0;
+
+       uint8_t message_id = 0;
+       uint8_t service_type = 0;
+       uint8_t status = 0;
+       uint8_t details = 0;
+       uint8_t num_subblocks = 0;
+       uint8_t file_status = 1;
+
+       message_id = g_isi_msg_id(msg);
+
+       DBG("uicc_file_info_resp_cb: msg_id=%d, msg len=%zu", message_id,
+               g_isi_msg_data_len(msg));
+
+       if (message_id != UICC_APPL_CMD_RESP)
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &service_type) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &status) ||
+                       !g_isi_msg_data_get_byte(msg, 2, &details) ||
+                       !g_isi_msg_data_get_byte(msg, 5, &num_subblocks))
+               goto error;
+
+       DBG("%s, service %s, status %s, details %s, nm_sb %d",
+               uicc_message_id_name(message_id),
+               uicc_service_type_name(service_type),
+               uicc_status_name(status), uicc_details_name(details),
+               num_subblocks);
+
+       if (info) {
+               access[0] = info->access[0];
+               access[1] = info->access[1];
+               access[2] = info->access[2];
+               file_status = info->file_status;
+       }
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_subblocks);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
+
+               DBG("Subblock %s", uicc_subblock_name(sb_id));
+
+               if (sb_id != UICC_SB_FCI)
+                       continue;
+
+               DBG("Decoding UICC_SB_FCI");
+
+               switch (sd->app_type) {
+               case UICC_APPL_TYPE_UICC_USIM:
+                       DBG("UICC_APPL_TYPE_UICC_USIM");
+
+                       if (!decode_uicc_usim_type(&iter, &length, &file_id,
+                                                       &record_length,
+                                                       &records,
+                                                       &structure))
+                               goto error;
+
+                       break;
+
+               case UICC_APPL_TYPE_ICC_SIM:
+                       DBG("UICC_APPL_TYPE_ICC_SIM");
+
+                       if (!g_isi_sb_iter_get_word(&iter, &length, 10))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_word(&iter, &file_id, 12))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &access[0], 16))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &access[0], 17))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &access[0], 18))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &item_len, 20))
+                               goto error;
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &structure, 21))
+                               goto error;
+
+                       if (item_len == 2) {
+                               uint8_t byte;
+
+                               if (!g_isi_sb_iter_get_byte(&iter, &byte, 22))
+                                       goto error;
+
+                               record_length = byte;
+                       }
+                       break;
+
+               default:
+                       DBG("Application type %d not supported", sd->app_type);
+                       break;
+               }
+
+               DBG("fileid=%04X, filelen=%d, records=%d, reclen=%d, structure=%d",
+                       file_id, length, records, record_length, structure);
+
+               CALLBACK_WITH_SUCCESS(cb, length, structure, record_length,
+                                       access, file_status, cbd->data);
+               return;
+       }
+
+error:
+       DBG("Error reading file info");
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, cbd->data);
+}
+
+static gboolean send_uicc_read_file_info(GIsiClient *client, uint8_t app_id,
+                                               int fileid, uint8_t df_len,
+                                               int mf_path, int df1_path,
+                                               int df2_path,
+                                               GIsiNotifyFunc notify, void *data,
+                                               GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               UICC_APPL_CMD_REQ,
+               UICC_APPL_FILE_INFO,    /* Service type */
+               app_id,
+               UICC_SESSION_ID_NOT_USED,
+               0, 0,                   /* Filler */
+               1,                      /* Number of subblocks */
+               ISI_16BIT(UICC_SB_APPL_PATH),
+               ISI_16BIT(16),          /* Subblock length */
+               ISI_16BIT(fileid),
+               uicc_get_sfi(fileid),   /* Elementary file short file id */
+               0,                      /* Filler */
+               df_len,
+               0,                      /* Filler */
+               ISI_16BIT(mf_path),
+               ISI_16BIT(df1_path),
+               ISI_16BIT(df2_path),
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
+}
+
+static void uicc_read_file_info(struct ofono_sim *sim, int fileid,
+                               ofono_sim_file_info_cb_t cb, void *data)
+{
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       struct uicc_file_info_cb_data *cbd;
+
+       /* Prepare for static file info used for access rights */
+       int i;
+       int N = sizeof(static_file_info) / sizeof(static_file_info[0]);
+       int mf_path = 0;
+       int df1_path = 0;
+       int df2_path = 0;
+       uint8_t df_len = 0;
+
+       cbd = g_try_new0(struct uicc_file_info_cb_data, 1);
+       if (!cbd)
+               goto error;
+
+       cbd->cb = cb;
+       cbd->data = data;
+       cbd->sim = sim;
+       cbd->user = NULL;
+
+       DBG("File info for ID=%04X app id %d", fileid, sd->app_id);
+
+       for (i = 0; i < N; i++) {
+               if (fileid == static_file_info[i].fileid) {
+                       cbd->user = (void *) &static_file_info[i];
+                       break;
+               }
+       }
+
+       DBG("File info for ID=%04X: %p", fileid, cbd->user);
+
+       if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path,
+                                       &df_len, fileid))
+               goto error;
+
+       if (send_uicc_read_file_info(sd->client, sd->app_id, fileid, df_len,
+                                       mf_path, df1_path, df2_path,
+                                       uicc_file_info_resp_cb,
+                                       cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, data);
+       g_free(cbd);
+}
+
+static void uicc_read_file_transp_resp_cb(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_sim_read_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+
+       uint32_t filelen = 0;
+       uint8_t *filedata = NULL;
+       uint8_t num_sb = 0;
+
+       DBG("");
+
+       if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_TRANSPARENT))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
+               goto error;
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               int sb_id = g_isi_sb_iter_get_id(&iter);
+
+               DBG("Subblock %s", uicc_subblock_name(sb_id));
+
+               if (sb_id != UICC_SB_FILE_DATA)
+                       continue;
+
+               if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata,
+                                               filelen, 8))
+                       goto error;
+
+               DBG("Transparent EF read: 1st byte %02x, len %d",
+                       filedata[0], filelen);
+               CALLBACK_WITH_SUCCESS(cb, filedata, filelen, cbd->data);
+               return;
+       }
+
+error:
+       DBG("Error reading transparent EF");
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
+}
+
+static gboolean send_uicc_read_file_transparent(GIsiClient *client,
+                                               uint8_t app_id, uint8_t client_id,
+                                               int fileid, uint8_t df_len,
+                                               int mf_path, int df1_path,
+                                               int df2_path,
+                                               GIsiNotifyFunc notify,
+                                               void *data,
+                                               GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               UICC_APPL_CMD_REQ,
+               UICC_APPL_READ_TRANSPARENT,
+               app_id,
+               UICC_SESSION_ID_NOT_USED,
+               0, 0,           /* Filler */
+               3,              /* Number of subblocks */
+               ISI_16BIT(UICC_SB_CLIENT),
+               ISI_16BIT(8),   /* Subblock length*/
+               0, 0, 0,        /* Filler */
+               client_id,
+               ISI_16BIT(UICC_SB_TRANSPARENT),
+               ISI_16BIT(8),   /* Subblock length */
+               ISI_16BIT(0),   /* File offset */
+               ISI_16BIT(0),   /* Data amount (0=all) */
+               ISI_16BIT(UICC_SB_APPL_PATH),
+               ISI_16BIT(16),  /* Subblock length */
+               ISI_16BIT(fileid),
+               uicc_get_sfi(fileid),   /* Elementary file short file id */
+               0,      /* Filler */
+               df_len,
+               0,
+               ISI_16BIT(mf_path),
+               ISI_16BIT(df1_path),
+               ISI_16BIT(df2_path),
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
+}
+
+static void uicc_read_file_transparent(struct ofono_sim *sim, int fileid,
+                                       int start, int length,
+                                       ofono_sim_read_cb_t cb, void *data)
+{
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+       int mf_path = 0;
+       int df1_path = 0;
+       int df2_path = 0;
+       uint8_t df_len = 0;
+
+       if (!cbd || !sd)
+               goto error;
+
+       DBG("File ID=%04X, client %d, AID %d", fileid, sd->client_id,
+               sd->app_id);
+
+       if (!uicc_get_fileid_path(sd, &mf_path, &df1_path,
+                                       &df2_path, &df_len, fileid))
+               goto error;
+
+       if (send_uicc_read_file_transparent(sd->client, sd->app_id, sd->client_id,
+                                               fileid, df_len, mf_path,
+                                               df1_path, df2_path,
+                                               uicc_read_file_transp_resp_cb,
+                                               cbd, g_free))
+               return;
+
+error:
+       DBG("Read file transparent failed");
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+       g_free(cbd);
+}
+
+static void read_file_linear_resp(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_sim_read_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+       uint8_t num_sb = 0;
+       uint8_t *filedata = NULL;
+       uint32_t filelen = 0;
+
+       DBG("");
+
+       if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_LINEAR_FIXED))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
+               goto error;
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
+
+               DBG("Subblock %s", uicc_subblock_name(sb_id));
+
+               if (sb_id != UICC_SB_FILE_DATA)
+                       continue;
+
+               if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata,
+                                               filelen, 8))
+                       goto error;
+
+               DBG("Linear fixed EF read: 1st byte %02x, len %d", filedata[0],
+                       filelen);
+
+               CALLBACK_WITH_SUCCESS(cb, filedata, filelen, cbd->data);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
+}
+
+static gboolean send_uicc_read_file_linear(GIsiClient *client, uint8_t app_id,
+                                               uint8_t client_id,
+                                               int fileid, int record,
+                                               int rec_length,
+                                               unsigned char df_len,
+                                               int mf_path, int df1_path,
+                                               int df2_path,
+                                               GIsiNotifyFunc notify,
+                                               void *data,
+                                               GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               UICC_APPL_CMD_REQ,
+               UICC_APPL_READ_LINEAR_FIXED,
+               app_id,
+               UICC_SESSION_ID_NOT_USED,
+               0, 0,           /* Filler */
+               3,              /* Number of subblocks */
+               ISI_16BIT(UICC_SB_CLIENT),
+               ISI_16BIT(8),   /*Subblock length */
+               0, 0, 0,        /* Filler */
+               client_id,
+               ISI_16BIT(UICC_SB_LINEAR_FIXED),
+               ISI_16BIT(8),   /*Subblock length */
+               record,
+               0,              /* Record offset */
+               rec_length & 0xff,      /*Data amount (0=all)*/
+               0,
+               ISI_16BIT(UICC_SB_APPL_PATH),
+               ISI_16BIT(16),  /* Subblock length */
+               ISI_16BIT(fileid),
+               uicc_get_sfi(fileid),   /* Elementary file short file id */
+               0,              /* Filler */
+               df_len,
+               0,
+               ISI_16BIT(mf_path),
+               ISI_16BIT(df1_path),
+               ISI_16BIT(df2_path),
+       };
+
+       return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
+}
+
+static void uicc_read_file_linear(struct ofono_sim *sim, int fileid, int record,
+                                       int rec_length, ofono_sim_read_cb_t cb,
+                                       void *data)
+{
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+       int mf_path = 0;
+       int df1_path = 0;
+       int df2_path = 0;
+       uint8_t df_len = 0;
+
+       if (!sd || !cbd)
+               goto error;
+
+       DBG("File ID=%04X, record %d, client %d AID %d", fileid, record,
+               sd->client_id, sd->app_id);
+
+       if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path,
+                                       &df_len, fileid))
+               goto error;
+
+       if (send_uicc_read_file_linear(sd->client, sd->app_id, sd->client_id,
+                                       fileid, record, rec_length, df_len,
+                                       mf_path, df1_path, df2_path,
+                                       read_file_linear_resp, cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+       g_free(cbd);
+}
+
+static void uicc_read_file_cyclic(struct ofono_sim *sim, int fileid,
+                                       int record, int length,
+                                       ofono_sim_read_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void uicc_write_file_transparent(struct ofono_sim *sim, int fileid,
+                                       int start, int length,
+                                       const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void uicc_write_file_linear(struct ofono_sim *sim, int fileid, int record,
+                                       int length, const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void uicc_write_file_cyclic(struct ofono_sim *sim, int fileid, int length,
+                                       const unsigned char *value,
+                                       ofono_sim_write_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static gboolean decode_imsi(uint8_t *data, int len, char *imsi)
+{
+       int i = 1; /* Skip first byte, the length field */
+       int j = 0;
+
+       if (data == NULL || len == 0)
+               return FALSE;
+
+       if (data[0] != 8 || data[0] > len)
+               return FALSE;
+
+       /* Ignore low-order semi-octet of the first byte */
+       imsi[j] = ((data[i] & 0xF0) >> 4) + '0';
+
+       for (i++, j++; i - 1 < data[0] && j < MAX_IMSI_LENGTH; i++) {
+               char nibble;
+
+               imsi[j++] = (data[i] & 0x0F) + '0';
+               nibble = (data[i] & 0xF0) >> 4;
+
+               if (nibble != 0x0F)
+                       imsi[j++] = nibble + '0';
+       }
+
+       imsi[j] = '\0';
+       return TRUE;
+}
+
+static void uicc_read_imsi_resp(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_sim_imsi_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+
+       uint32_t filelen = 0;
+       uint8_t *filedata = NULL;
+       uint8_t num_sb = 0;
+
+       char imsi[MAX_IMSI_LENGTH + 1] = { 0 };
+
+       DBG("");
+
+       if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_TRANSPARENT))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
+               goto error;
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               int sb_id = g_isi_sb_iter_get_id(&iter);
+
+               DBG("Subblock %s", uicc_subblock_name(sb_id));
+
+               if (sb_id != UICC_SB_FILE_DATA)
+                       continue;
+
+               if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata,
+                                               filelen, 8))
+                       goto error;
+
+               DBG("Transparent EF read: 1st byte %02x, len %d",
+                       filedata[0], filelen);
+
+               if (!decode_imsi(filedata, filelen, imsi))
+                       goto error;
+
+               DBG("IMSI %s", imsi);
+               CALLBACK_WITH_SUCCESS(cb, imsi, cbd->data);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void uicc_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb,
+                               void *data)
+{
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+
+       int mf_path = 0;
+       int df1_path = 0;
+       int df2_path = 0;
+       uint8_t df_len = 0;
+
+       if (!cbd)
+               goto error;
+
+       DBG("Client %d, AID %d", sd->client_id, sd->app_id);
+
+       if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path, &df_len,
+                                       SIM_EFIMSI_FILEID))
+               goto error;
+
+       if (send_uicc_read_file_transparent(sd->client, sd->app_id, sd->client_id,
+                                               SIM_EFIMSI_FILEID, df_len,
+                                               mf_path, df1_path, df2_path,
+                                               uicc_read_imsi_resp,
+                                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+       g_free(cbd);
+}
+
+static void uicc_query_passwd_state_resp(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_sim_passwd_cb_t cb = cbd->cb;
+       uint8_t type;
+       uint8_t cause;
+
+       DBG("");
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               goto error;
+       }
+
+       if (g_isi_msg_id(msg) != UICC_PIN_RESP) {
+               DBG("Unexpected msg: %s", sim_message_id_name(g_isi_msg_id(msg)));
+               goto error;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &type) ||
+                       type != UICC_PIN_PROMPT_VERIFY) {
+               DBG("Unexpected service: 0x%02X (0x%02X)", type,
+                       UICC_PIN_PROMPT_VERIFY);
+               goto error;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &cause))
+               goto error;
+
+       DBG("Status: %d %s", cause, uicc_status_name(cause));
+
+       if (cause == UICC_STATUS_PIN_DISABLED) {
+               CALLBACK_WITH_SUCCESS(cb, OFONO_SIM_PASSWORD_NONE, cbd->data);
+               return;
+       }
+
+       DBG("Request failed or not implemented: %s", uicc_status_name(cause));
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void uicc_query_passwd_state(struct ofono_sim *sim,
+                                       ofono_sim_passwd_cb_t cb, void *data)
+{
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+
+       const uint8_t req[] = {
+               UICC_PIN_REQ,
+               UICC_PIN_PROMPT_VERIFY,
+               sd->app_id,
+               0, 0, 0,        /* Filler */
+               1,              /* Number of subblocks */
+               ISI_16BIT(UICC_SB_PIN_REF),
+               ISI_16BIT(8),   /*Sub block length*/
+               sd->pin1_id,    /* Pin ID */
+               0, 0, 0,        /* Filler */
+       };
+
+       DBG("");
+
+       if (g_isi_client_send(sd->client, req, sizeof(req),
+                               uicc_query_passwd_state_resp, cbd, g_free))
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+       g_free(cbd);
+}
+
+static void uicc_send_passwd(struct ofono_sim *sim, const char *passwd,
+                               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void uicc_query_pin_retries_resp(const GIsiMessage *msg, void *opaque)
+{
+       struct isi_cb_data *cbd = opaque;
+       ofono_sim_pin_retries_cb_t cb = cbd->cb;
+       int retries[OFONO_SIM_PASSWORD_INVALID];
+       GIsiSubBlockIter iter;
+
+       uint8_t num_sb = 0;
+       uint8_t pins = 0;
+       uint8_t pina = 0;
+       uint8_t puka = 0;
+
+       DBG("");
+
+       if (!check_resp(msg, UICC_PIN_RESP, UICC_PIN_INFO))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
+               goto error;
+
+       DBG("Subblock count %d", num_sb);
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
+
+               DBG("Sub-block %s", uicc_subblock_name(sb_id));
+
+               if (sb_id != UICC_SB_PIN_INFO)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &pins, 4))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &pina, 5))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &puka, 6))
+                       goto error;
+
+               DBG("PIN status %X PIN Attrib %d PUK attrib %d", pins,
+                       pina, puka);
+
+               retries[OFONO_SIM_PASSWORD_SIM_PIN] = pina;
+               retries[OFONO_SIM_PASSWORD_SIM_PUK] = puka;
+
+               CALLBACK_WITH_SUCCESS(cb, retries, cbd->data);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void uicc_query_pin_retries(struct ofono_sim *sim,
+                                       ofono_sim_pin_retries_cb_t cb,
+                                       void *data)
+{
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
+
+       const uint8_t req[] = {
+               UICC_PIN_REQ,
+               UICC_PIN_INFO,
+               sd->app_id,
+               0, 0, 0,        /* Filler */
+               1,              /* Number of subblocks */
+               ISI_16BIT(UICC_SB_PIN_REF),
+               ISI_16BIT(8),   /* Subblock length */
+               sd->pin1_id,    /* Pin ID */
+               0, 0, 0,        /* Filler */
+       };
+
+       DBG("");
+
+       if (g_isi_client_send(sd->client, req, sizeof(req),
+                               uicc_query_pin_retries_resp, cbd, g_free))
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, NULL, data);
+       g_free(cbd);
+}
+
+static void uicc_reset_passwd(struct ofono_sim *sim, const char *puk,
+                               const char *passwd, ofono_sim_lock_unlock_cb_t cb,
+                               void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void uicc_change_passwd(struct ofono_sim *sim,
+                               enum ofono_sim_password_type passwd_type,
+                               const char *old, const char *new,
+                               ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void uicc_lock(struct ofono_sim *sim, enum ofono_sim_password_type type,
+                       int enable, const char *passwd,
+                       ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void uicc_query_locked(struct ofono_sim *sim,
+                               enum ofono_sim_password_type type,
+                               ofono_sim_locked_cb_t cb, void *data)
+{
+       DBG("Not implemented");
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static gboolean decode_fcp_pin_status(const GIsiSubBlockIter *iter, uint8_t read,
+                                       uint8_t *pin1, uint8_t *pin2)
+{
+       uint8_t do_len;
+       uint8_t len;
+       uint8_t tag;
+       uint8_t id;
+       uint8_t tag_pos;
+
+       DBG("Decoding PIN status");
+
+       if (!g_isi_sb_iter_get_byte(iter, &do_len, read))
+               return FALSE;
+
+       tag_pos = read + 1 + do_len;
+
+       if (!g_isi_sb_iter_get_byte(iter, &tag, tag_pos))
+               return FALSE;
+
+       while (tag == 0x83) {
+
+               if (!g_isi_sb_iter_get_byte(iter, &len, tag_pos + 1))
+                       return FALSE;
+
+               if (!g_isi_sb_iter_get_byte(iter, &id, tag_pos + 2))
+                       return FALSE;
+
+               tag_pos += 2 + len;
+
+               if (!g_isi_sb_iter_get_byte(iter, &tag, tag_pos))
+                       return FALSE;
+
+               DBG("PIN_len %d, PIN id %02x, PIN tag %02x", len, id, tag);
+
+               if (id >= 0x01 && id <= 0x08)
+                       *pin1 = id;
+               else if (id >= 0x81 && id <= 0x88)
+                       *pin2 = id;
+       }
+       return TRUE;
+}
+
+static gboolean decode_fci_sb(const GIsiSubBlockIter *iter, int app_type,
+                               uint8_t *pin1, uint8_t *pin2)
+{
+       uint8_t fcp = 0;
+       uint8_t fcp_len = 0;
+       uint8_t read = 0;
+       uint8_t item_len = 0;
+
+       DBG("Decoding UICC_SB_FCI");
+
+       if (app_type != UICC_APPL_TYPE_UICC_USIM)
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &fcp, 8))
+               return FALSE;
+
+       if (fcp != FCP_TEMPLATE)
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &fcp_len, 9))
+               return FALSE;
+
+       for (read = 0; read < fcp_len; read += item_len + 2) {
+               uint8_t id;
+
+               if (!g_isi_sb_iter_get_byte(iter, &id, read + 10))
+                       return FALSE;
+
+               if (!g_isi_sb_iter_get_byte(iter, &item_len, read + 11))
+                       return FALSE;
+
+               if (id != FCP_PIN_STATUS)
+                       continue;
+
+               if (!decode_fcp_pin_status(iter, read + 13, pin1, pin2))
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+static gboolean decode_chv_sb(const GIsiSubBlockIter *iter, int app_type,
+                               uint8_t *pin1, uint8_t *pin2)
+{
+       uint8_t chv_id = 0;
+       uint8_t pin_id = 0;
+
+       DBG("Decoding UICC_SB_CHV");
+
+       if (app_type != UICC_APPL_TYPE_ICC_SIM)
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &chv_id, 4))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &pin_id, 5))
+               return FALSE;
+
+       switch (chv_id) {
+       case 1:
+               *pin1 = pin_id;
+               break;
+
+       case 2:
+               *pin2 = pin_id;
+               break;
+
+       default:
+               return FALSE;
+       }
+
+       DBG("CHV=%d, pin_id=%2x, PIN1 %02x, PIN2 %02x", chv_id, pin_id, *pin1,
+               *pin2);
+
+       return TRUE;
+}
+
+static void uicc_application_activate_resp(const GIsiMessage *msg, void *opaque)
+{
+       struct ofono_sim *sim = opaque;
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       GIsiSubBlockIter iter;
+       uint8_t cause, num_sb;
+
+       DBG("");
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return;
+       }
+
+       if (g_isi_msg_id(msg) != UICC_APPLICATION_RESP) {
+               DBG("Unexpected msg: %s",
+                       sim_message_id_name(g_isi_msg_id(msg)));
+               return;
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &cause))
+               return;
+
+       if (cause != UICC_STATUS_OK && cause != UICC_STATUS_APPL_ACTIVE) {
+               DBG("TODO: handle application activation");
+               return;
+       }
+
+       if (!sd->uicc_app_started) {
+               sd->app_id = sd->trying_app_id;
+               sd->app_type = sd->trying_app_type;
+               sd->uicc_app_started = TRUE;
+
+               DBG("UICC application activated");
+
+               ofono_sim_inserted_notify(sim, TRUE);
+               ofono_sim_register(sim);
+
+               g_hash_table_remove_all(sd->app_table);
+       }
+
+       if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
+               return;
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
+
+               DBG("Subblock %s", uicc_subblock_name(sb_id));
+
+               switch (sb_id) {
+               case UICC_SB_CLIENT:
+
+                       if (!g_isi_sb_iter_get_byte(&iter, &sd->client_id, 7))
+                               return;
+
+                       DBG("Client id %d", sd->client_id);
+                       break;
+
+               case UICC_SB_FCI:
+
+                       if (!decode_fci_sb(&iter, sd->app_type, &sd->pin1_id,
+                                               &sd->pin2_id))
+                               return;
+
+                       DBG("PIN1 %02x, PIN2 %02x", sd->pin1_id, sd->pin2_id);
+                       break;
+
+               case UICC_SB_CHV:
+
+                       if (!decode_chv_sb(&iter, sd->app_type, &sd->pin1_id,
+                                               &sd->pin2_id))
+                               return;
+
+                       DBG("PIN1 %02x, PIN2 %02x", sd->pin1_id, sd->pin2_id);
+                       break;
+
+               default:
+                       DBG("Skipping sub-block: %s (%zu bytes)",
+                               uicc_subblock_name(g_isi_sb_iter_get_id(&iter)),
+                               g_isi_sb_iter_get_len(&iter));
+                       break;
+               }
+       }
+}
+
+static gboolean send_application_activate_req(GIsiClient *client,
+                                               uint8_t app_type,
+                                               uint8_t app_id,
+                                               GIsiNotifyFunc notify,
+                                               void *data,
+                                               GDestroyNotify destroy)
+{
+       const uint8_t msg[] = {
+               UICC_APPLICATION_REQ,
+               UICC_APPL_HOST_ACTIVATE,
+               2,              /* Number of subblocks */
+               ISI_16BIT(UICC_SB_APPLICATION),
+               ISI_16BIT(8),   /* Subblock length */
+               0, 0,           /* Filler */
+               app_type,
+               app_id,
+               ISI_16BIT(UICC_SB_APPL_INFO),
+               ISI_16BIT(8),   /* Subblock length */
+               0, 0, 0,        /* Filler */
+               /*
+                * Next field indicates whether the application
+                * initialization procedure will follow the activation
+                * or not
+                */
+               UICC_APPL_START_UP_INIT_PROC,
+       };
+
+       DBG("App type %d, AID %d", app_type, app_id);
+
+       return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
+}
+
+static void uicc_application_list_resp(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       GIsiSubBlockIter iter;
+       uint8_t num_sb;
+       struct uicc_sim_application *sim_app;
+
+       /* Throw away old app table */
+       g_hash_table_remove_all(sd->app_table);
+
+       if (!check_resp(msg, UICC_APPLICATION_RESP, UICC_APPL_LIST))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
+               goto error;
+
+       /* Iterate through the application list */
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+               uint8_t app_type;
+               uint8_t app_id;
+               uint8_t app_status;
+               uint8_t app_len;
+
+               if (g_isi_sb_iter_get_id(&iter) != UICC_SB_APPL_DATA_OBJECT)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &app_type, 6))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &app_id, 7))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &app_status, 8))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &app_len, 9))
+                       goto error;
+
+               if (app_type != UICC_APPL_TYPE_ICC_SIM &&
+                               app_type != UICC_APPL_TYPE_UICC_USIM)
+                       continue;
+
+               sim_app = g_try_new0(struct uicc_sim_application, 1);
+               if (!sim_app) {
+                       DBG("out of memory!");
+                       goto error;
+               }
+
+               sim_app->type = app_type;
+               sim_app->id = app_id;
+               sim_app->status = app_status;
+               sim_app->length = app_len;
+               sim_app->sim = sd;
+
+               g_hash_table_replace(sd->app_table, &sim_app->id, sim_app);
+       }
+
+       if (!sd->uicc_app_started) {
+               GHashTableIter iter;
+               struct uicc_sim_application *app;
+
+               gpointer key;
+               gpointer value;
+
+               g_hash_table_iter_init(&iter, sd->app_table);
+
+               if (!g_hash_table_iter_next(&iter, &key, &value))
+                       return;
+
+               app = value;
+               sd->trying_app_type = app->type;
+               sd->trying_app_id = app->id;
+
+               g_hash_table_remove(sd->app_table, &app->id);
+
+               if (!send_application_activate_req(sd->client, app->type, app->id,
+                                               uicc_application_activate_resp,
+                                               data, NULL)) {
+                       DBG("Failed to activate: 0x%02X (type=0x%02X)",
+                               app->id, app->type);
+                       return;
+               }
+       }
+       return;
+
+error:
+       DBG("Decoding application list failed");
+
+       g_isi_client_destroy(sd->client);
+       sd->client = NULL;
+
+       ofono_sim_remove(sim);
+}
+
+static void uicc_card_status_resp(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       GIsiSubBlockIter iter;
+       uint8_t card_status = 0;
+       uint8_t num_sb = 0;
+
+       DBG("");
+
+       if (!sd->server_running)
+               return;
+
+       if (!check_resp(msg, UICC_CARD_RESP, UICC_CARD_STATUS_GET))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &card_status))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
+               goto error;
+
+       DBG("Subblock count %d", num_sb);
+
+       for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != UICC_SB_CARD_STATUS)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &card_status, 7))
+                       goto error;
+
+               DBG("card_status = 0x%X", card_status);
+
+               /* Check if card is ready */
+               if (card_status == 0x21) {
+                       const uint8_t msg[] = {
+                               UICC_APPLICATION_REQ,
+                               UICC_APPL_LIST,
+                               0,      /* Number of subblocks */
+                       };
+
+                       DBG("card is ready");
+                       ofono_sim_inserted_notify(sim, TRUE);
+
+                       if (g_isi_client_send(sd->client, msg, sizeof(msg),
+                                               uicc_application_list_resp,
+                                               data, NULL))
+                               return;
+
+                       DBG("Failed to query application list");
+                       goto error;
+
+               } else {
+                       DBG("card not ready");
+                       ofono_sim_inserted_notify(sim, FALSE);
+                       return;
+               }
+       }
+
+error:
+       g_isi_client_destroy(sd->client);
+       sd->client = NULL;
+
+       ofono_sim_remove(sim);
+}
+
+static void uicc_card_status_req(struct ofono_sim *sim,
+                                       struct uicc_sim_data *sd)
+{
+       const uint8_t req[] = {
+               UICC_CARD_REQ,
+               UICC_CARD_STATUS_GET,
+               0,
+       };
+
+       DBG("");
+
+       if (g_isi_client_send(sd->client, req, sizeof(req),
+                                   uicc_card_status_resp, sim, NULL))
+               return;
+
+       g_isi_client_destroy(sd->client);
+       sd->client = NULL;
+
+       ofono_sim_remove(sim);
+}
+
+static void uicc_card_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+
+       DBG("");
+
+       if (g_isi_msg_id(msg) != UICC_CARD_IND)
+               return;
+
+       /* We're not interested in card indications if server isn't running */
+       if (!sd->server_running)
+               return;
+
+       /* Request card status */
+       uicc_card_status_req(sim, sd);
+}
+
+static void uicc_status_resp(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       uint8_t status = 0, server_status = 0;
+       gboolean server_running = FALSE;
+
+       if (!check_resp(msg, UICC_RESP, UICC_STATUS_GET))
+               goto error;
+
+       if (g_isi_msg_error(msg) < 0)
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &status) ||
+                       !g_isi_msg_data_get_byte(msg, 3, &server_status))
+               goto error;
+
+       DBG("status=0x%X, server_status=0x%X", status, server_status);
+
+       if (status == UICC_STATUS_OK &&
+                       server_status == UICC_STATUS_START_UP_COMPLETED) {
+               DBG("server is up!");
+               server_running = TRUE;
+       }
+
+
+       if (!server_running) {
+               sd->server_running = FALSE;
+
+               /* TODO: Remove SIM etc... */
+               return;
+       }
+
+       if (sd->server_running && server_running) {
+               DBG("Server status didn't change...");
+               return;
+       }
+
+       /* Server is running */
+       sd->server_running = TRUE;
+
+       /* Request card status */
+       uicc_card_status_req(sim, sd);
+       return;
+
+error:
+       g_isi_client_destroy(sd->client);
+       sd->client = NULL;
+
+       ofono_sim_remove(sim);
+}
+
+static void uicc_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+       const uint8_t req[] = { UICC_REQ, UICC_STATUS_GET, 0 };
+
+       int msg_id = g_isi_msg_id(msg);
+       DBG("%s", uicc_message_id_name(msg_id));
+
+       if (msg_id != UICC_IND)
+               return;
+
+       /* Request status */
+       if (g_isi_client_send(sd->client, req, sizeof(req), uicc_status_resp,
+                               data, NULL))
+               return;
+
+       DBG("status request failed!");
+
+       g_isi_client_destroy(sd->client);
+       sd->client = NULL;
+       ofono_sim_remove(sim);
+}
+
+static void uicc_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       struct uicc_sim_data *sd = ofono_sim_get_data(sim);
+
+       const uint8_t req[] = {
+               UICC_REQ,
+               UICC_STATUS_GET,
+               0,      /* Number of Sub Blocks (only from version 4.0) */
+       };
+
+       ISI_RESOURCE_DBG(msg);
+
+       if (g_isi_msg_error(msg) < 0)
+               goto error;
+
+       sd->version.major = g_isi_msg_version_major(msg);
+       sd->version.minor = g_isi_msg_version_minor(msg);
+
+       /* UICC server is reachable: request indications */
+       g_isi_client_ind_subscribe(sd->client, UICC_IND, uicc_ind_cb, sim);
+       g_isi_client_ind_subscribe(sd->client, UICC_CARD_IND, uicc_card_ind_cb,
+                                       sim);
+
+       /* Update status */
+       if (g_isi_client_send(sd->client, req,
+                               sizeof(req) - ((sd->version.major < 4) ? 1 : 0),
+                               uicc_status_resp, data, NULL))
+               return;
+
+error:
+       g_isi_client_destroy(sd->client);
+       sd->client = NULL;
+
+       ofono_sim_remove(sim);
+}
+
+static void sim_app_destroy(gpointer p)
+{
+       struct uicc_sim_application *app = p;
+       if (!app)
+               return;
+
+       g_free(app);
+}
+
+static int uicc_sim_probe(struct ofono_sim *sim, unsigned int vendor,
+                                       void *user)
+{
+       GIsiModem *modem = user;
+       struct uicc_sim_data *sd;
+
+       sd = g_try_new0(struct uicc_sim_data, 1);
+       if (sd == NULL)
+               return -ENOMEM;
+
+       /* Create hash table for the UICC applications */
+       sd->app_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
+                                               sim_app_destroy);
+       if (sd->app_table == NULL) {
+               g_free(sd);
+               return -ENOMEM;
+       }
+
+       sd->client = g_isi_client_create(modem, PN_UICC);
+       if (sd->client == NULL) {
+               g_hash_table_destroy(sd->app_table);
+               g_free(sd);
+               return -ENOMEM;
+       }
+
+       g_hash_table_insert(g_modems, g_isi_client_modem(sd->client), sim);
+
+       sd->server_running = FALSE;
+       sd->uicc_app_started = FALSE;
+       sd->pin_state_received = FALSE;
+       sd->passwd_required = TRUE;
+       ofono_sim_set_data(sim, sd);
+
+       g_isi_client_verify(sd->client, uicc_reachable_cb, sim, NULL);
+
+       return 0;
+}
+
+static void uicc_sim_remove(struct ofono_sim *sim)
+{
+       struct uicc_sim_data *data = ofono_sim_get_data(sim);
+
+       ofono_sim_set_data(sim, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_hash_table_remove(g_modems, g_isi_client_modem(data->client));
+
+       g_hash_table_destroy(data->app_table);
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_sim_driver driver = {
+       .name                   = "wgmodem2.5",
+       .probe                  = uicc_sim_probe,
+       .remove                 = uicc_sim_remove,
+       .read_file_info         = uicc_read_file_info,
+       .read_file_transparent  = uicc_read_file_transparent,
+       .read_file_linear       = uicc_read_file_linear,
+       .read_file_cyclic       = uicc_read_file_cyclic,
+       .write_file_transparent = uicc_write_file_transparent,
+       .write_file_linear      = uicc_write_file_linear,
+       .write_file_cyclic      = uicc_write_file_cyclic,
+       .read_imsi              = uicc_read_imsi,
+       .query_passwd_state     = uicc_query_passwd_state,
+       .send_passwd            = uicc_send_passwd,
+       .query_pin_retries      = uicc_query_pin_retries,
+       .reset_passwd           = uicc_reset_passwd,
+       .change_passwd          = uicc_change_passwd,
+       .lock                   = uicc_lock,
+       .query_locked           = uicc_query_locked,
+};
+
+void isi_uicc_init(void)
+{
+       g_modems = g_hash_table_new(g_direct_hash, g_direct_equal);
+       ofono_sim_driver_register(&driver);
+}
+
+void isi_uicc_exit(void)
+{
+       g_hash_table_destroy(g_modems);
+       ofono_sim_driver_unregister(&driver);
+}
+
+gboolean isi_uicc_properties(GIsiModem *modem, int *app_id, int *app_type,
+                               int *client_id)
+{
+       struct ofono_sim *sim;
+       struct uicc_sim_data *sd;
+
+       sim = g_hash_table_lookup(g_modems, modem);
+       if (sim == NULL)
+               return FALSE;
+
+       sd = ofono_sim_get_data(sim);
+       if (sd == NULL)
+               return FALSE;
+
+       if (app_id != NULL)
+               *app_id = sd->app_id;
+
+       if (app_type != NULL)
+               *app_type = sd->app_type;
+
+       if (client_id != NULL)
+               *client_id = sd->client_id;
+
+       return TRUE;
+}
diff --git a/drivers/isimodem/uicc.h b/drivers/isimodem/uicc.h
new file mode 100644 (file)
index 0000000..dfcd476
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __ISIMODEM_UICC_H
+#define __ISIMODEM_UICC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <glib/gtypes.h>
+
+#include <gisi/client.h>
+#include <gisi/modem.h>
+
+#define PN_UICC 0x8C
+
+#define UICC_APPL_ID_UNKNOWN                   0x00
+#define UICC_SFI_NOT_PRESENT                   0x00
+#define UICC_SESSION_ID_NOT_USED               0x00
+
+enum uicc_status {
+       UICC_STATUS_OK =                        0x00,
+       UICC_STATUS_FAIL =                      0x01,
+       UICC_STATUS_UNKNOWN =                   0x02,
+       UICC_STATUS_NOT_READY =                 0x10,
+       UICC_STATUS_START_UP_COMPLETED =        0x11,
+       UICC_STATUS_SHUTTING_DOWN =             0x12,
+       UICC_STATUS_CARD_NOT_READY =            0x20,
+       UICC_STATUS_CARD_READY =                0x21,
+       UICC_STATUS_CARD_DISCONNECTED =         0x22,
+       UICC_STATUS_CARD_NOT_PRESENT =          0x23,
+       UICC_STATUS_CARD_REJECTED =             0x24,
+       UICC_STATUS_APPL_ACTIVE =               0x30,
+       UICC_STATUS_APPL_NOT_ACTIVE =           0x31,
+       UICC_STATUS_PIN_ENABLED =               0x40,
+       UICC_STATUS_PIN_DISABLED =              0x41,
+};
+
+enum uicc_subblock {
+       UICC_SB_SHUT_DOWN_CONFIG =              0x0023,
+       UICC_SB_CARD_STATUS =                   0x0001,
+       UICC_SB_CARD_INFO =                     0x0024,
+       UICC_SB_CARD_REJECT_CAUSE =             0x0025,
+       UICC_SB_CLIENT =                        0x001F,
+       UICC_SB_APPL_DATA_OBJECT =              0x0002,
+       UICC_SB_APPLICATION =                   0x0003,
+       UICC_SB_APPL_INFO =                     0x0004,
+       UICC_SB_APPL_STATUS =                   0x0005,
+       UICC_SB_FCP =                           0x0007,
+       UICC_SB_FCI =                           0x001C,
+       UICC_SB_CHV =                           0x001B,
+       UICC_SB_PIN =                           0x0008,
+       UICC_SB_PIN_REF =                       0x0009,
+       UICC_SB_PUK =                           0x000A,
+       UICC_SB_PIN_SUBST =                     0x000B,
+       UICC_SB_PIN_INFO =                      0x000C,
+       UICC_SB_APPL_PATH =                     0x000D,
+       UICC_SB_SESSION =                       0x000E,
+       UICC_SB_FILE_DATA =                     0x000F,
+       UICC_SB_APDU =                          0x0014,
+       UICC_SB_TRANSPARENT_READ =              0x0010,
+       UICC_SB_TRANSPARENT_UPDATE =            0x0011,
+       UICC_SB_TRANSPARENT =                   0x0012,
+       UICC_SB_LINEAR_FIXED =                  0x0013,
+       UICC_SB_CYCLIC =                        0x0026,
+       UICC_SB_TERMINAL_PROFILE =              0x0015,
+       UICC_SB_TERMINAL_RESPONSE =             0x001D,
+       UICC_SB_ENVELOPE =                      0x0021,
+       UICC_SB_POLLING_SET =                   0x0016,
+       UICC_SB_REFRESH =                       0x0017,
+       UICC_SB_AID =                           0x0006,
+       UICC_SB_REFRESH_RESULT =                0x0018,
+       UICC_SB_APDU_ACTIONS =                  0x0019,
+       UICC_SB_OBJECT_ID =                     0x001A,
+       UICC_SB_STATUS_WORD =                   0x0020,
+       UICC_SB_APDU_SAP_INFO =                 0x0022,
+       UICC_SB_ACCESS_MODE =                   0x0027,
+       UICC_SB_RESP_INFO =                     0x0028,
+       UICC_SB_APDU_SAP_CONFIG =               0x0029,
+};
+
+enum uicc_message_id {
+       UICC_REQ =                              0x00,
+       UICC_RESP =                             0x01,
+       UICC_IND =                              0x02,
+       UICC_CARD_REQ =                         0x03,
+       UICC_CARD_RESP =                        0x04,
+       UICC_CARD_IND =                         0x05,
+       UICC_APPLICATION_REQ =                  0x06,
+       UICC_APPLICATION_RESP =                 0x07,
+       UICC_APPLICATION_IND =                  0x08,
+       UICC_PIN_REQ =                          0x09,
+       UICC_PIN_RESP =                         0x0A,
+       UICC_PIN_IND =                          0x0B,
+       UICC_APPL_CMD_REQ =                     0x0C,
+       UICC_APPL_CMD_RESP =                    0x0D,
+       UICC_APPL_CMD_IND =                     0x0E,
+       UICC_CONNECTOR_REQ =                    0x0F,
+       UICC_CONNECTOR_RESP =                   0x10,
+       UICC_CAT_REQ =                          0x12,
+       UICC_CAT_RESP =                         0x13,
+       UICC_CAT_IND =                          0x14,
+       UICC_APDU_REQ =                         0x15,
+       UICC_APDU_RESP =                        0x16,
+       UICC_APDU_RESET_IND =                   0x17,
+       UICC_REFRESH_REQ =                      0x18,
+       UICC_REFRESH_RESP =                     0x19,
+       UICC_REFRESH_IND =                      0x1A,
+       UICC_SIMLOCK_REQ =                      0x1B,
+       UICC_SIMLOCK_RESP =                     0x1C,
+       UICC_APDU_SAP_REQ =                     0x1E,
+       UICC_APDU_SAP_RESP =                    0x1F,
+       UICC_APDU_SAP_IND =                     0x20,
+       UICC_PWR_CTRL_REQ =                     0x21,
+       UICC_PWR_CTRL_RESP =                    0x22,
+       UICC_PWR_CTRL_IND =                     0x23,
+       UICC_CARD_READER_IND =                  0x26,
+};
+
+enum uicc_service_type {
+       UICC_APPL_LIST =                        0x01,
+       UICC_APPL_HOST_ACTIVATE =               0x03,
+       UICC_APPL_START_UP_COMPLETE =           0x05,
+       UICC_APPL_SHUT_DOWN_INITIATED =         0x06,
+       UICC_APPL_STATUS_GET =                  0x07,
+       UICC_APPL_HOST_DEACTIVATE =             0x09,
+       UICC_PIN_VERIFY =                       0x11,
+       UICC_PIN_UNBLOCK =                      0x12,
+       UICC_PIN_DISABLE =                      0x13,
+       UICC_PIN_ENABLE =                       0x14,
+       UICC_PIN_CHANGE =                       0x15,
+       UICC_PIN_SUBSTITUTE =                   0x16,
+       UICC_PIN_INFO =                         0x17,
+       UICC_PIN_PROMPT_VERIFY =                0x18,
+       UICC_APPL_READ_TRANSPARENT =            0x21,
+       UICC_APPL_UPDATE_TRANSPARENT =          0x22,
+       UICC_APPL_READ_LINEAR_FIXED =           0x23,
+       UICC_APPL_UPDATE_LINEAR_FIXED =         0x24,
+       UICC_APPL_FILE_INFO =                   0x25,
+       UICC_APPL_APDU_SEND =                   0x26,
+       UICC_APPL_CLEAR_CACHE =                 0x27,
+       UICC_APPL_SESSION_START =               0x28,
+       UICC_APPL_SESSION_END =                 0x29,
+       UICC_APPL_READ_CYCLIC =                 0x2A,
+       UICC_APPL_UPDATE_CYCLIC =               0x2B,
+       UICC_CONNECT =                          0x31,
+       UICC_DISCONNECT =                       0x32,
+       UICC_RECONNECT =                        0x33,
+       UICC_CAT_ENABLE =                       0x41,
+       UICC_CAT_DISABLE =                      0x42,
+       UICC_CAT_TERMINAL_PROFILE =             0x43,
+       UICC_CAT_TERMINAL_RESPONSE =            0x44,
+       UICC_CAT_ENVELOPE =                     0x45,
+       UICC_CAT_POLLING_SET =                  0x46,
+       UICC_CAT_REFRESH =                      0x47,
+       UICC_CAT_POLL =                         0x48,
+       UICC_APDU_SEND =                        0x51,
+       UICC_APDU_ATR_GET =                     0x52,
+       UICC_APDU_CONTROL =                     0x53,
+       UICC_REFRESH_STATUS =                   0x61,
+       UICC_APPL_TERMINATED =                  0x71,
+       UICC_APPL_RECOVERED =                   0x72,
+       UICC_APPL_ACTIVATED =                   0x75,
+       UICC_PIN_VERIFY_NEEDED =                0x81,
+       UICC_PIN_UNBLOCK_NEEDED =               0x82,
+       UICC_PIN_PERMANENTLY_BLOCKED =          0x83,
+       UICC_PIN_VERIFIED =                     0x84,
+       UICC_CAT_FETCHED_CMD =                  0x91,
+       UICC_CAT_NOT_SUPPORTED =                0x92,
+       UICC_CAT_REG_FAILED =                   0x93,
+       UICC_CAT_REG_OK =                       0x94,
+       UICC_REFRESH_PERMISSION =               0xA1,
+       UICC_REFRESH_STARTING =                 0xA2,
+       UICC_REFRESH_CANCELLED =                0xA3,
+       UICC_REFRESH_NOW =                      0xA4,
+       UICC_START_UP_COMPLETE =                0xB0,
+       UICC_STATUS_GET =                       0xB1,
+       UICC_READY =                            0xB2,
+       UICC_READY_FOR_ACTIVATION =             0xB3,
+       UICC_INITIALIZED =                      0xB4,
+       UICC_SHUTTING_DOWN =                    0xB5,
+       UICC_SHUT_DOWN_CONFIG =                 0xB6,
+       UICC_ERROR =                            0xB7,
+       UICC_CARD_DISCONNECTED =                0xC0,
+       UICC_CARD_REMOVED =                     0xC1,
+       UICC_CARD_NOT_PRESENT =                 0xC2,
+       UICC_CARD_READY =                       0xC4,
+       UICC_CARD_STATUS_GET =                  0xC5,
+       UICC_CARD_REJECTED =                    0xC8,
+       UICC_CARD_INFO_GET =                    0xC9,
+       UICC_SIMLOCK_ACTIVE =                   0xD0,
+       UICC_APDU_SAP_ACTIVATE =                0xE1,
+       UICC_APDU_SAP_DEACTIVATE =              0xE2,
+       UICC_APDU_SAP_ATR_GET =                 0xE3,
+       UICC_APDU_SAP_COLD_RESET =              0xE4,
+       UICC_APDU_SAP_WARM_RESET =              0xE5,
+       UICC_APDU_SAP_APDU_SEND =               0xE6,
+       UICC_APDU_SAP_RECOVERY =                0xE7,
+       UICC_APDU_SAP_CONFIG_GET =              0xE8,
+       UICC_PWR_CTRL_ENABLE =                  0xF1,
+       UICC_PWR_CTRL_DISABLE =                 0xF2,
+       UICC_PWR_CTRL_WAIT =                    0xF3,
+       UICC_PWR_CTRL_PROCEED =                 0xF4,
+       UICC_PWR_CTRL_PERMISSION =              0xFA,
+};
+
+enum uicc_appl_type_table {
+       UICC_APPL_TYPE_UNKNOWN =                0x00,
+       UICC_APPL_TYPE_ICC_SIM =                0x01,
+       UICC_APPL_TYPE_UICC_USIM =              0x02,
+};
+
+enum uicc_pin_qualifier {
+       UICC_PIN_NEW =                          0x01,
+       UICC_PIN_OLD =                          0x02,
+};
+
+enum uicc_appl_start_up_type {
+       UICC_APPL_START_UP_NO_INIT_PROC =       0x00,
+       UICC_APPL_START_UP_INIT_PROC =          0x01,
+};
+
+enum uicc_card_type {
+       UICC_CARD_TYPE_ICC =                    0x01,
+       UICC_CARD_TYPE_UICC =                   0x02,
+};
+
+enum uicc_details {
+       UICC_NO_DETAILS =                       0x00,
+       UICC_INVALID_PARAMETERS =               0x01,
+       UICC_FILE_NOT_FOUND =                   0x02,
+       UICC_SECURITY_CONDITIONS_NOT_SATISFIED =        0x03,
+       UICC_APPL_CONFLICT =                            0x04,
+       UICC_CARD_ERROR =                               0x05,
+       UICC_SERVICE_NOT_SUPPORTED =                    0x06,
+       UICC_SESSION_EXPIRED =                          0x07,
+};
+
+enum uicc_simlock_status {
+       UICC_SIMLOCK_STATUS_ACTIVE =            0x01,
+       UICC_SIMLOCK_STATUS_INACTIVE =          0x02,
+};
+
+enum uicc_apdu_status_word {
+       UICC_PIN_STATUS_AUTH_RETRIES =          0x63c0,
+       UICC_PIN_STATUS_AUTH_BLOCKED =          0x6983,
+       UICC_PIN_STATUS_AUTH_FAILED =           0x9840,
+};
+
+enum uicc_template {
+       UICC_TEMPLATE_APPLICATION =             0x61,
+       UICC_TEMPLATE_FCP =                     0x62,
+       UICC_TEMPLATE_SECURITY_ENVIRONMENT =    0x7B,
+};
+
+enum uicc_fcp_param {
+       UICC_FCP_PARAM_FILE_SIZE_DATA =         0x80,
+       UICC_FCP_PARAM_FILE_SIZE_TOTAL =        0x81,
+       UICC_FCP_PARAM_FILE_DESC =              0x82,
+       UICC_FCP_PARAM_FILE_ID =                0x83,
+       UICC_FCP_PARAM_AID =                    0x84,
+       UICC_FCP_PARAM_LIFECYCLE =              0x8A,
+       UICC_FCP_PARAM_SECURITY_REFERENCE =     0x8B,
+       UICC_FCP_PARAM_SECURITY_COMPACT =       0x8C,
+       UICC_FCP_PARAM_SECURITY_EXPANDED =      0xAB,
+       UICC_FCP_PARAM_PIN_STATUS =             0xC6,
+};
+
+enum uicc_app_param {
+       UICC_APP_PARAM_ID =                     0x4F,
+       UICC_APP_PARAM_LABEL =                  0x50,
+       UICC_APP_PARAM_PATH =                   0x51,
+       UICC_APP_PARAM_COMMAND =                0x52,
+       UICC_APP_PARAM_DISC_DATA =              0x53,
+       UICC_APP_PARAM_DISC_TEMPLATE =          0x73,
+       UICC_APP_PARAM_URL =                    0x5F50,
+};
+
+gboolean isi_uicc_properties(GIsiModem *modem, int *app_id, int *app_type,
+                               int *client_id);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __ISIMODEM_UICC_H */
diff --git a/drivers/isimodem/ussd.c b/drivers/isimodem/ussd.c
new file mode 100644 (file)
index 0000000..71f246d
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/message.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/ussd.h>
+
+#include "smsutil.h"
+#include "util.h"
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "ss.h"
+#include "debug.h"
+
+struct ussd_info {
+       uint8_t dcs;
+       uint8_t type;
+       uint8_t len;
+};
+
+struct ussd_data {
+       GIsiClient *client;
+       GIsiVersion version;
+       int mt_session;
+};
+
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
+{
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", g_isi_msg_strerror(msg));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       ss_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static void ussd_notify_ack(struct ussd_data *ud)
+{
+       const uint8_t msg[] = {
+               SS_GSM_USSD_SEND_REQ,
+               SS_GSM_USSD_NOTIFY,
+               0,      /* subblock count */
+       };
+
+       g_isi_client_send(ud->client, msg, sizeof(msg), NULL, NULL, NULL);
+}
+
+static void ussd_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_ussd *ussd = data;
+       struct ussd_data *ud = ofono_ussd_get_data(ussd);
+       struct ussd_info *info;
+       size_t len = sizeof(struct ussd_info);
+       uint8_t *string;
+       int status;
+
+       if (g_isi_msg_id(msg) != SS_GSM_USSD_RECEIVE_IND)
+               return;
+
+       if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &info, len))
+               return;
+
+       if (!g_isi_msg_data_get_struct(msg, len, (const void **) &string,
+                                       info->len))
+               return;
+
+       switch (info->type) {
+       case 0:
+               /* Nothing - this is response to NOTIFY_ACK REQ */
+               return;
+
+       case SS_GSM_USSD_MT_REPLY:
+               /* This never happens, but.. */
+               status = OFONO_USSD_STATUS_LOCAL_CLIENT_RESPONDED;
+               break;
+
+       case SS_GSM_USSD_COMMAND:
+
+               /* Ignore, we get SS_GSM_USSD_REQUEST, too */
+               if (ud->mt_session)
+                       return;
+
+               status = OFONO_USSD_STATUS_ACTION_REQUIRED;
+               break;
+
+       case SS_GSM_USSD_NOTIFY:
+               status = OFONO_USSD_STATUS_NOTIFY;
+               ussd_notify_ack(ud);
+               break;
+
+       case SS_GSM_USSD_END:
+               status = OFONO_USSD_STATUS_TERMINATED;
+               ud->mt_session = 0;
+               break;
+
+       case SS_GSM_USSD_REQUEST:
+               ud->mt_session = 1;
+               status = OFONO_USSD_STATUS_ACTION_REQUIRED;
+               break;
+
+       default:
+               status = OFONO_USSD_STATUS_NOT_SUPPORTED;
+       }
+
+       DBG("type: %u %s, dcs: 0x%02x, len: %u",
+               info->type, ss_ussd_type_name(info->type), info->dcs,
+               info->len);
+
+       ofono_ussd_notify(ussd, status, info->dcs, string, info->len);
+}
+
+static void ussd_send_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_ussd_cb_t cb = cbd->cb;
+
+       if (check_response_status(msg, SS_GSM_USSD_SEND_RESP))
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void isi_request(struct ofono_ussd *ussd, int dcs,
+                       const unsigned char *pdu, int len, ofono_ussd_cb_t cb,
+                       void *data)
+{
+       struct ussd_data *ud = ofono_ussd_get_data(ussd);
+       struct isi_cb_data *cbd = isi_cb_data_new(ussd, cb, data);
+
+       size_t sb_len = ALIGN4(4 + len);
+       size_t pad_len = sb_len - (4 + len);
+
+       const uint8_t padding[4] = { 0 };
+       const uint8_t msg[] = {
+               SS_GSM_USSD_SEND_REQ,
+               ud->mt_session ? SS_GSM_USSD_MT_REPLY : SS_GSM_USSD_COMMAND,
+               1,              /* subblock count */
+               SS_GSM_USSD_STRING,
+               sb_len,
+               dcs,            /* DCS */
+               len,            /* string length */
+               /* USSD string goes here */
+       };
+       struct iovec iov[3] = {
+               { (uint8_t *) msg, sizeof(msg) },
+               { (uint8_t *) pdu, len },
+               { (uint8_t *) padding, pad_len },
+       };
+
+       if (cbd == NULL || ud == NULL)
+               goto error;
+
+       if (g_isi_client_vsend(ud->client, iov, 3, ussd_send_resp_cb, cbd,
+                               g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void isi_cancel(struct ofono_ussd *ussd, ofono_ussd_cb_t cb, void *data)
+{
+       struct ussd_data *ud = ofono_ussd_get_data(ussd);
+       struct isi_cb_data *cbd = isi_cb_data_new(ussd, cb, data);
+       const uint8_t msg[] = {
+               SS_GSM_USSD_SEND_REQ,
+               SS_GSM_USSD_END,
+               0, /* subblock count */
+       };
+
+       if (cbd == NULL || ud == NULL)
+               goto error;
+
+       if (g_isi_client_send(ud->client, msg, sizeof(msg), ussd_send_resp_cb,
+                               cbd, g_free))
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void ussd_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_ussd *ussd = data;
+       struct ussd_data *ud = ofono_ussd_get_data(ussd);
+
+       if (g_isi_msg_error(msg) < 0) {
+               ofono_ussd_remove(ussd);
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_ind_subscribe(ud->client, SS_GSM_USSD_RECEIVE_IND,
+                                       ussd_ind_cb, ussd);
+
+       ofono_ussd_register(ussd);
+}
+
+static int isi_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *modem = user;
+       struct ussd_data *ud;
+
+       ud = g_try_new0(struct ussd_data, 1);
+
+       if (ud == NULL)
+               return -ENOMEM;
+
+       ud->client = g_isi_client_create(modem, PN_SS);
+       if (ud->client == NULL) {
+               g_free(ud);
+               return -ENOMEM;
+       }
+
+       ofono_ussd_set_data(ussd, ud);
+
+       g_isi_client_verify(ud->client, ussd_reachable_cb, ussd, NULL);
+
+       return 0;
+}
+
+static void isi_ussd_remove(struct ofono_ussd *ussd)
+{
+       struct ussd_data *data = ofono_ussd_get_data(ussd);
+
+       ofono_ussd_set_data(ussd, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_ussd_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_ussd_probe,
+       .remove                 = isi_ussd_remove,
+       .request                = isi_request,
+       .cancel                 = isi_cancel
+};
+
+void isi_ussd_init(void)
+{
+       ofono_ussd_driver_register(&driver);
+}
+
+void isi_ussd_exit(void)
+{
+       ofono_ussd_driver_unregister(&driver);
+}
diff --git a/drivers/isimodem/voicecall.c b/drivers/isimodem/voicecall.c
new file mode 100644 (file)
index 0000000..2f38f68
--- /dev/null
@@ -0,0 +1,1956 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/message.h>
+#include <gisi/iter.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "isimodem.h"
+#include "isiutil.h"
+#include "call.h"
+#include "debug.h"
+
+#define ISI_CALL_TIMEOUT       1000
+
+struct isi_call {
+       uint8_t id;
+       uint8_t call_id;
+       uint8_t status;
+       uint8_t prev_status;
+       uint8_t mode;
+       uint8_t mode_info;
+       uint8_t cause_type;
+       uint8_t cause;
+       uint8_t addr_type;
+       uint8_t presentation;
+       uint8_t name_presentation;
+       uint8_t reason;
+       char address[20];
+       char name[20];
+       char addr_pad[4];
+};
+
+struct call_addr_info {
+       uint8_t call_id;
+       uint8_t mode;
+       uint8_t mode_info;
+       uint8_t status;
+       uint8_t filler[2];
+       uint8_t addr_type;
+       uint8_t presentation;
+       uint8_t filler2;
+       uint8_t addr_len;
+};
+
+struct call_info {
+       uint8_t call_id;
+       uint8_t mode;
+       uint8_t mode_info;
+       uint8_t status;
+};
+
+struct isi_voicecall {
+       GIsiClient *client;
+       GIsiClient *pn_call;
+       GIsiClient *pn_modem_call;
+       struct isi_call_req_ctx *queue;
+       struct isi_call calls[8];
+       void *control_req_irc;
+};
+
+typedef void isi_call_req_step(struct isi_call_req_ctx *ctx, int reason);
+
+struct isi_call_req_ctx {
+       struct isi_call_req_ctx *next;
+       struct isi_call_req_ctx **prev;
+       isi_call_req_step *step;
+       struct ofono_voicecall *ovc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+};
+
+static struct isi_call_req_ctx *isi_call_req(struct ofono_voicecall *ovc,
+                                               const void *__restrict req,
+                                               size_t len,
+                                               GIsiNotifyFunc handler,
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       struct isi_call_req_ctx *irc;
+
+       irc = g_try_new0(struct isi_call_req_ctx, 1);
+       if (irc == NULL) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return NULL;
+       }
+
+       irc->ovc = ovc;
+       irc->cb = cb;
+       irc->data = data;
+
+       if (g_isi_client_send(ivc->client, req, len, handler, irc, NULL))
+               return irc;
+
+       g_free(irc);
+       return NULL;
+}
+
+static void isi_ctx_queue(struct isi_call_req_ctx *irc, isi_call_req_step *next)
+{
+       struct isi_voicecall *ivc;
+
+       if (irc->prev != NULL) {
+               irc->step = next;
+               return;
+       }
+
+       ivc = ofono_voicecall_get_data(irc->ovc);
+       if (ivc->queue) {
+               irc->next = ivc->queue;
+               irc->next->prev = &irc->next;
+       }
+
+       irc->prev = &ivc->queue;
+       ivc->queue = irc;
+}
+
+static void isi_ctx_remove(struct isi_call_req_ctx *irc)
+{
+       if (irc->prev == NULL)
+               return;
+
+       *irc->prev = irc->next;
+
+       if (irc->next) {
+               irc->next->prev = irc->prev;
+               irc->next = NULL;
+       }
+       irc->prev = NULL;
+}
+
+static void isi_ctx_free(struct isi_call_req_ctx *irc)
+{
+       if (irc == NULL)
+               return;
+
+       isi_ctx_remove(irc);
+       g_free(irc);
+}
+
+static gboolean isi_ctx_return(struct isi_call_req_ctx *irc,
+                               enum ofono_error_type type, int error)
+{
+       if (irc == NULL)
+               return TRUE;
+
+       if (irc->cb) {
+               struct ofono_error e = {
+                       .type = type,
+                       .error = error
+               };
+               irc->cb(&e, irc->data);
+       }
+
+       isi_ctx_free(irc);
+       return TRUE;
+}
+
+static gboolean isi_ctx_return_failure(struct isi_call_req_ctx *irc)
+{
+       return isi_ctx_return(irc, OFONO_ERROR_TYPE_FAILURE, 0);
+}
+
+static gboolean isi_ctx_return_success(struct isi_call_req_ctx *irc)
+{
+       if (irc == NULL || irc->step == NULL)
+               return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0);
+
+       irc->step(irc, 0);
+       return TRUE;
+}
+
+/* Decoding subblocks */
+static void isi_call_any_address_sb_proc(struct isi_voicecall *ivc,
+                                               struct isi_call *call,
+                                               GIsiSubBlockIter *sb)
+{
+       uint8_t type;
+       uint8_t pres;
+       uint8_t len;
+       char *addr;
+
+       if (!g_isi_sb_iter_get_byte(sb, &type, 2))
+               return;
+
+       if (!g_isi_sb_iter_get_byte(sb, &pres, 3))
+               return;
+
+       if (!g_isi_sb_iter_get_byte(sb, &len, 5))
+               return;
+
+       if (!g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * len, 6))
+               return;
+
+       call->addr_type = type | 0x80;
+       call->presentation = pres;
+       strncpy(call->address, addr, sizeof(call->address));
+
+       g_free(addr);
+}
+
+static void isi_call_origin_address_sb_proc(struct isi_voicecall *ivc,
+                                               struct isi_call *call,
+                                               GIsiSubBlockIter *sb)
+{
+       if (call->address[0] == '\0')
+               isi_call_any_address_sb_proc(ivc, call, sb);
+}
+
+static void isi_call_destination_address_sb_proc(struct isi_voicecall *ivc,
+                                                       struct isi_call *call,
+                                                       GIsiSubBlockIter *sb)
+{
+       if (call->address[0] == '\0')
+               isi_call_any_address_sb_proc(ivc, call, sb);
+}
+
+static void isi_call_origin_info_sb_proc(struct isi_voicecall *ivc,
+                                               struct isi_call *call,
+                                               GIsiSubBlockIter *sb)
+{
+       uint8_t pres;
+       uint8_t id;
+       uint8_t len;
+       char *name;
+
+       if (!g_isi_sb_iter_get_byte(sb, &pres, 2))
+               return;
+
+       if (!g_isi_sb_iter_get_byte(sb, &id, 6))
+               return;
+
+       if (!g_isi_sb_iter_get_byte(sb, &len, 7))
+               return;
+
+       if (!g_isi_sb_iter_get_alpha_tag(sb, &name, 2 * len, 8))
+               return;
+
+       DBG("Got name %s", name);
+       call->name_presentation = pres;
+       strncpy(call->name, name, sizeof(call->name));
+
+       g_free(name);
+}
+
+static void isi_call_mode_sb_proc(struct isi_voicecall *ivc,
+                                       struct isi_call *call,
+                                       GIsiSubBlockIter *sb)
+{
+       uint8_t mode;
+       uint8_t info;
+
+       if (!g_isi_sb_iter_get_byte(sb, &mode, 2) ||
+                       !g_isi_sb_iter_get_byte(sb, &info, 3))
+               return;
+
+       call->mode = mode;
+       call->mode_info = info;
+}
+
+static void isi_call_cause_sb_proc(struct isi_voicecall *ivc,
+                                       struct isi_call *call,
+                                       GIsiSubBlockIter *sb)
+{
+       uint8_t type;
+       uint8_t cause;
+
+       if (!g_isi_sb_iter_get_byte(sb, &type, 2) ||
+                       !g_isi_sb_iter_get_byte(sb, &cause, 3))
+               return;
+
+       call->cause_type = type;
+       call->cause = cause;
+}
+
+static void isi_call_status_sb_proc(struct isi_voicecall *ivc,
+                                       struct isi_call *call,
+                                       GIsiSubBlockIter *sb)
+{
+       uint8_t status;
+
+       if (!g_isi_sb_iter_get_byte(sb, &status, 2))
+               return;
+       call->prev_status = call->status;
+       call->status = status;
+}
+
+static struct isi_call *isi_call_status_info_sb_proc(struct isi_voicecall *ivc,
+                                                       GIsiSubBlockIter *sb)
+{
+       struct isi_call *call = NULL;
+       int i;
+       struct call_info *ci;
+       size_t len = sizeof(struct call_info);
+
+       if (!g_isi_sb_iter_get_struct(sb, (void *) &ci, len, 2))
+               return NULL;
+
+       i = ci->call_id & 7;
+
+       if (1 <= i && i <= 7) {
+               call = &ivc->calls[i];
+               call->call_id = ci->call_id;
+               call->status = ci->status;
+               call->mode = ci->mode;
+               call->mode_info = ci->mode_info;
+       }
+
+       return call;
+}
+
+static struct isi_call *isi_call_addr_and_status_info_sb_proc(
+                                               struct isi_voicecall *ivc,
+                                               GIsiSubBlockIter *sb)
+{
+       struct isi_call *call = NULL;
+       int i;
+       struct call_addr_info *ci;
+       size_t len = sizeof(struct call_addr_info);
+       char *addr;
+
+       if (!g_isi_sb_iter_get_struct(sb, (void *) &ci, len, 2))
+               return NULL;
+
+       if (!g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * ci->addr_len, 12))
+               return NULL;
+
+       i = ci->call_id & 7;
+
+       if (1 <= i && i <= 7) {
+               call = &ivc->calls[i];
+               call->call_id = ci->call_id;
+               call->status = ci->status;
+               call->mode = ci->mode;
+               call->mode_info = ci->mode_info;
+               call->addr_type = ci->addr_type | 0x80;
+               call->presentation = ci->presentation;
+               strncpy(call->address, addr, sizeof call->address);
+       }
+
+       g_free(addr);
+       return call;
+}
+
+static int isi_call_status_to_clcc(const struct isi_call *call)
+{
+       switch (call->status) {
+       case CALL_STATUS_CREATE:
+               return 2;
+
+       case CALL_STATUS_COMING:
+               return 4;
+
+       case CALL_STATUS_PROCEEDING:
+
+               if ((call->mode_info & CALL_MODE_ORIGINATOR))
+                       return 4; /* MT */
+               else
+                       return 2; /* MO */
+
+       case CALL_STATUS_MO_ALERTING:
+               return 3;
+
+       case CALL_STATUS_MT_ALERTING:
+               return 4;
+
+       case CALL_STATUS_WAITING:
+               return 5;
+
+       case CALL_STATUS_MO_RELEASE:
+               return 6;
+
+       case CALL_STATUS_MT_RELEASE:
+               if ((call->prev_status == CALL_STATUS_MT_ALERTING) ||
+                               (call->prev_status == CALL_STATUS_COMING) ||
+                               (call->prev_status == CALL_STATUS_WAITING))
+                       return 4;
+               else
+                       return 6;
+
+       case CALL_STATUS_ACTIVE:
+       case CALL_STATUS_HOLD_INITIATED:
+               return 0;
+
+       case CALL_STATUS_HOLD:
+       case CALL_STATUS_RETRIEVE_INITIATED:
+               return 1;
+
+       case CALL_STATUS_RECONNECT_PENDING:
+       case CALL_STATUS_SWAP_INITIATED:
+       default:
+               return 0;
+       }
+}
+
+static struct ofono_call isi_call_as_ofono_call(const struct isi_call *call)
+{
+       struct ofono_call ocall;
+       struct ofono_phone_number *number = &ocall.phone_number;
+
+       ofono_call_init(&ocall);
+       ocall.id = call->id;
+       ocall.type = 0; /* Voice call */
+       ocall.direction = call->mode_info & CALL_MODE_ORIGINATOR;
+       ocall.status = isi_call_status_to_clcc(call);
+
+       memcpy(number->number, call->address, sizeof(number->number));
+       memcpy(ocall.name, call->name, sizeof(ocall.name));
+
+       number->type = 0x80 | call->addr_type;
+       ocall.clip_validity = call->presentation & 3;
+       ocall.cnap_validity = call->name_presentation & 3;
+
+       if (ocall.clip_validity == 0 && strlen(number->number) == 0)
+               ocall.clip_validity = 2;
+
+       if (ocall.cnap_validity == 0 && strlen(call->name) == 0)
+               ocall.cnap_validity = 2;
+
+       return ocall;
+}
+
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
+{
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       net_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static struct isi_call *isi_call_set_idle(struct isi_call *call)
+{
+       uint8_t id;
+
+       if (call == NULL)
+               return NULL;
+
+       id = call->id;
+       memset(call, 0, sizeof(struct isi_call));
+       call->id = id;
+
+       return call;
+}
+
+static void isi_call_disconnected(struct ofono_voicecall *ovc,
+                                       struct isi_call *call)
+{
+       struct ofono_error error = {
+               OFONO_ERROR_TYPE_NO_ERROR, 0
+       };
+
+       DBG("disconnected id=%u reason=%u", call->id, call->reason);
+
+       ofono_voicecall_disconnected(ovc, call->id, call->reason, &error);
+
+       isi_call_set_idle(call);
+}
+
+static void isi_call_set_disconnect_reason(struct isi_call *call)
+{
+       enum ofono_disconnect_reason reason;
+
+       if (call->reason != OFONO_DISCONNECT_REASON_UNKNOWN)
+               return;
+
+       switch (call->status) {
+       case CALL_STATUS_IDLE:
+               reason = OFONO_DISCONNECT_REASON_UNKNOWN;
+               break;
+
+       case CALL_STATUS_MO_RELEASE:
+               reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+               break;
+
+       case CALL_STATUS_MT_RELEASE:
+               reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+               break;
+
+       case CALL_STATUS_TERMINATED:
+       default:
+               reason = OFONO_DISCONNECT_REASON_ERROR;
+       }
+
+       call->reason = reason;
+}
+
+static void isi_call_notify(struct ofono_voicecall *ovc, struct isi_call *call)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       struct isi_call_req_ctx *irc, **queue;
+       struct ofono_call ocall;
+
+       DBG("called with status=%s (0x%02X)",
+               call_status_name(call->status), call->status);
+
+       for (queue = &ivc->queue; (irc = *queue);) {
+               irc->step(irc, call->status);
+
+               if (*queue == irc)
+                       queue = &irc->next;
+       }
+
+       switch (call->status) {
+       case CALL_STATUS_IDLE:
+               isi_call_disconnected(ovc, call);
+               return;
+
+       case CALL_STATUS_COMING:
+       case CALL_STATUS_PROCEEDING:
+               if ((call->mode_info & CALL_MODE_ORIGINATOR))
+                       /* Do not notify early MT calls */
+                       return;
+               break;
+
+       case CALL_STATUS_MO_RELEASE:
+       case CALL_STATUS_MT_RELEASE:
+               /*
+               * Core requires the call status to be either incoming
+               * or waiting to identify the disconnected call as missed.
+               * The MT RELEASE is not mapped to any state in +CLCC, but
+               * we need the disconnect reason.
+               */
+               isi_call_set_disconnect_reason(call);
+               break;
+       case CALL_STATUS_TERMINATED:
+               DBG("State( CALL_STATUS_TERMINATED ) need not be reported to Core");
+               /*
+               * The call terminated is not reported to core as
+               * these intermediate states are not processed in
+               * the core. We report the call status when it becomes
+               * idle and TERMINATED is not mapped to +CLCC. The disconnect
+               * reason is set, so that the call termination cause
+               * in case of error is available to the core.
+               */
+               isi_call_set_disconnect_reason(call);
+               return;
+       case CALL_STATUS_ANSWERED:
+               DBG("State need not be reported to Core");
+               return;
+       }
+
+       ocall = isi_call_as_ofono_call(call);
+
+       DBG("id=%u,%s,%u,\"%s\",\"%s\",%u,%u",
+               ocall.id,
+               ocall.direction ? "terminated" : "originated",
+               ocall.status,
+               ocall.phone_number.number,
+               ocall.name,
+               ocall.phone_number.type,
+               ocall.clip_validity);
+
+       ofono_voicecall_notify(ovc, &ocall);
+}
+
+static void isi_call_create_resp(const GIsiMessage *msg, void *data)
+{
+       struct isi_call_req_ctx *irc = data;
+       uint8_t call_id;
+       uint8_t subblocks;
+
+       if (!check_response_status(msg, CALL_CREATE_RESP))
+               goto failure;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       call_id == CALL_ID_NONE)
+               goto failure;
+
+       if (!g_isi_msg_data_get_byte(msg, 1, &subblocks))
+               goto failure;
+
+       if (subblocks != 0) {
+               GIsiSubBlockIter iter;
+               struct isi_call call = { 0 };
+
+               for (g_isi_sb_iter_init(&iter, msg, 2);
+                               g_isi_sb_iter_is_valid(&iter);
+                               g_isi_sb_iter_next(&iter)) {
+
+                       switch (g_isi_sb_iter_get_id(&iter)) {
+                       case CALL_CAUSE:
+                               isi_call_cause_sb_proc(NULL, &call, &iter);
+                               DBG("CALL_CREATE_RESP "
+                                       "cause_type=0x%02x cause=0x%02x",
+                                       call.cause_type, call.cause);
+                               goto failure;
+                       }
+               }
+       }
+
+       isi_ctx_return_success(irc);
+       return;
+
+failure:
+       isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_ctx *isi_modem_call_create_req(
+                                               struct ofono_voicecall *ovc,
+                                               uint8_t presentation,
+                                               uint8_t addr_type,
+                                               char const address[21],
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
+{
+       size_t addr_len = strlen(address);
+       size_t sub_len = ALIGN4(6 + 2 * addr_len);
+       size_t offset = 3 + 4 + 4 + 6;
+       uint8_t req[3 + 4 + 4 + 6 + 40] = {
+               CALL_CREATE_REQ,
+               0, /* No id */
+               3, /* Mode, Clir, Number */
+               CALL_MODE, 4, CALL_MODE_SPEECH, 0,
+               CALL_LINE_ID, 4, presentation, 0,
+               CALL_DESTINATION_ADDRESS, sub_len, addr_type & 0x7F, 0, 0,
+               addr_len,
+               /* uint16_t addr[20] */
+       };
+       size_t rlen = 3 + 4 + 4 + sub_len;
+       size_t i;
+
+       if (addr_len > 20) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return NULL;
+       }
+
+       for (i = 0; i < addr_len; i++)
+               req[offset + 2 * i + 1] = address[i];
+
+       return isi_call_req(ovc, req, rlen, isi_call_create_resp, cb, data);
+}
+
+static struct isi_call_req_ctx *isi_call_create_req(struct ofono_voicecall *ovc,
+                                                       uint8_t presentation,
+                                                       uint8_t addr_type,
+                                                       char const address[21],
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
+{
+       size_t addr_len = strlen(address);
+       size_t sub_len = ALIGN4(6 + 2 * addr_len);
+       size_t offset = 3 + 4 + 8 + 6;
+       uint8_t req[3 + 4 + 8 + 6 + 40] = {
+               CALL_CREATE_REQ,
+               0,              /* No id */
+               3,              /* Mode, Clir, Number */
+               CALL_MODE, 4, CALL_MODE_SPEECH, CALL_MODE_INFO_NONE,
+               CALL_ORIGIN_INFO, 8, presentation, 0, 0, 0, 0, 0,
+               CALL_DESTINATION_ADDRESS, sub_len, addr_type & 0x7F, 0, 0,
+               addr_len,
+               /* uint16_t addr[20] */
+       };
+       size_t rlen = 3 + 4 + 8 + sub_len;
+       size_t i;
+
+       if (addr_len > 20) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return NULL;
+       }
+
+       for (i = 0; i < addr_len; i++)
+               req[offset + 2 * i + 1] = address[i];
+
+       return isi_call_req(ovc, req, rlen, isi_call_create_resp, cb, data);
+}
+
+static void isi_call_status_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_voicecall *ovc = data;
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       struct isi_call *call;
+       GIsiSubBlockIter iter;
+
+       uint8_t call_id;
+       uint8_t old_status;
+
+       if (ivc == NULL || g_isi_msg_id(msg) != CALL_STATUS_IND ||
+                       !g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       (call_id & 7) == 0)
+               return;
+
+       call = &ivc->calls[call_id & 7];
+       old_status = call->status;
+       call->call_id = call_id;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case CALL_STATUS:
+                       isi_call_status_sb_proc(ivc, call, &iter);
+                       break;
+
+               case CALL_MODE:
+                       isi_call_mode_sb_proc(ivc, call, &iter);
+                       break;
+
+               case CALL_CAUSE:
+                       isi_call_cause_sb_proc(ivc, call, &iter);
+                       break;
+
+               case CALL_DESTINATION_ADDRESS:
+                       isi_call_destination_address_sb_proc(ivc, call, &iter);
+                       break;
+
+               case CALL_ORIGIN_ADDRESS:
+                       isi_call_origin_address_sb_proc(ivc, call, &iter);
+                       break;
+
+               case CALL_ORIGIN_INFO:
+                       isi_call_origin_info_sb_proc(ivc, call, &iter);
+                       break;
+
+               case CALL_GSM_DETAILED_CAUSE:
+               case CALL_DESTINATION_PRE_ADDRESS:
+               case CALL_DESTINATION_POST_ADDRESS:
+               case CALL_DESTINATION_SUBADDRESS:
+               case CALL_GSM_EVENT_INFO:
+               case CALL_NW_CAUSE:
+                       break;
+               }
+       }
+
+       if (old_status == call->status)
+               return;
+
+       isi_call_notify(ovc, call);
+}
+
+static void isi_call_terminated_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_voicecall *ovc = data;
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       struct isi_call *call;
+
+       uint8_t call_id;
+       uint8_t old_status;
+
+       if (ivc == NULL || g_isi_msg_id(msg) != CALL_TERMINATED_IND ||
+                       !g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       (call_id & 7) == 0)
+               return;
+
+       call = &ivc->calls[call_id & 7];
+       old_status = call->status;
+
+       if (old_status == CALL_STATUS_IDLE)
+               return;
+
+       call->status = CALL_STATUS_TERMINATED;
+       isi_call_notify(ovc, call);
+}
+
+static gboolean decode_notify(GIsiSubBlockIter *iter)
+{
+       uint8_t byte;
+
+       if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
+               return FALSE;
+
+       switch (byte) {
+       case CALL_NOTIFY_USER_SUSPENDED:
+               DBG("CALL_NOTIFY_USER_SUSPENDED");
+               break;
+
+       case CALL_NOTIFY_USER_RESUMED:
+               DBG("CALL_NOTIFY_USER_RESUMED");
+               break;
+
+       case CALL_NOTIFY_BEARER_CHANGE:
+               DBG("CALL_NOTIFY_BEARER_CHANGE");
+               break;
+
+       default:
+               DBG("Unknown notification: 0x%02X", byte);
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_ss_code(GIsiSubBlockIter *iter, int *cssi, int *cssu)
+{
+       uint16_t word;
+
+       if (!g_isi_sb_iter_get_word(iter, &word, 2))
+               return FALSE;
+
+       switch (word) {
+       case CALL_SSC_ALL_FWDS:
+               DBG("Call forwarding is active");
+               break;
+
+       case CALL_SSC_ALL_COND_FWD:
+               *cssi = SS_MO_CONDITIONAL_FORWARDING;
+               DBG("Some of conditional call forwardings active");
+               break;
+
+       case CALL_SSC_CFU:
+               *cssi = SS_MO_UNCONDITIONAL_FORWARDING;
+               DBG("Unconditional call forwarding is active");
+               break;
+
+       case CALL_SSC_OUTGOING_BARR_SERV:
+               *cssi = SS_MO_OUTGOING_BARRING;
+               DBG("Outgoing calls are barred");
+               break;
+
+       case CALL_SSC_INCOMING_BARR_SERV:
+               *cssi = SS_MO_INCOMING_BARRING;
+               DBG("Incoming calls are barred");
+               break;
+
+       case CALL_SSC_CALL_WAITING:
+               DBG("Incoming calls are barred");
+               break;
+
+       case CALL_SSC_CLIR:
+               DBG("CLIR connected unknown indication.");
+               break;
+
+       case CALL_SSC_MPTY:
+               *cssu = SS_MT_MULTIPARTY_VOICECALL;
+               DBG("Multiparty call entered.");
+               break;
+
+       case CALL_SSC_CALL_HOLD:
+               *cssu = SS_MT_VOICECALL_HOLD_RELEASED;
+               DBG("Call on hold has been released.");
+               break;
+
+       default:
+               DBG("Unknown/unhandled notification: 0x%02X", word);
+               break;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_ss_status(GIsiSubBlockIter *iter)
+{
+       uint8_t byte;
+
+       if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
+               return FALSE;
+
+       if (byte & CALL_SS_STATUS_ACTIVE)
+               DBG("CALL_SS_STATUS_ACTIVE");
+
+       if (byte & CALL_SS_STATUS_REGISTERED)
+               DBG("CALL_SS_STATUS_REGISTERED");
+
+       if (byte & CALL_SS_STATUS_PROVISIONED)
+               DBG("CALL_SS_STATUS_PROVISIONED");
+
+       if (byte & CALL_SS_STATUS_QUIESCENT)
+               DBG("CALL_SS_STATUS_QUIESCENT");
+
+       return TRUE;
+}
+
+static gboolean decode_ss_notify(GIsiSubBlockIter *iter, int *cssi, int *cssu)
+{
+       uint8_t byte;
+
+       if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
+               return FALSE;
+
+       if (byte & CALL_SSN_INCOMING_IS_FWD) {
+               *cssu = SS_MT_CALL_FORWARDED;
+               DBG("This is a forwarded call #1.");
+       }
+
+       if (byte & CALL_SSN_INCOMING_FWD)
+               DBG("This is a forwarded call #2.");
+
+       if (byte & CALL_SSN_OUTGOING_FWD) {
+               *cssi = SS_MO_CALL_FORWARDED;
+               DBG("Call has been forwarded.");
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_ss_notify_indicator(GIsiSubBlockIter *iter, int *cssi)
+{
+       uint8_t byte;
+
+       if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
+               return FALSE;
+
+       if (byte & CALL_SSI_CALL_IS_WAITING) {
+               *cssi = SS_MO_CALL_WAITING;
+               DBG("Call is waiting.");
+       }
+
+       if (byte & CALL_SSI_MPTY)
+               DBG("Multiparty call");
+
+       if (byte & CALL_SSI_CLIR_SUPPR_REJ) {
+               *cssi = SS_MO_CLIR_SUPPRESSION_REJECTED;
+               DBG("CLIR suppression rejected");
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_ss_hold_indicator(GIsiSubBlockIter *iter, int *cssu)
+{
+       uint8_t byte;
+
+       if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
+               return FALSE;
+
+       if (byte == CALL_HOLD_IND_RETRIEVED) {
+               *cssu = SS_MT_VOICECALL_RETRIEVED;
+               DBG("Call has been retrieved");
+       } else if (byte & CALL_HOLD_IND_ON_HOLD) {
+               *cssu = SS_MT_VOICECALL_ON_HOLD;
+               DBG("Call has been put on hold");
+       } else {
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_ss_ect_indicator(GIsiSubBlockIter *iter, int *cssu)
+{
+       uint8_t byte;
+
+       if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
+               return FALSE;
+
+       if (byte & CALL_ECT_CALL_STATE_ALERT) {
+               *cssu = SS_MT_VOICECALL_IN_TRANSFER;
+               DBG("Call is being connected with the remote party in "
+                       "alerting state");
+       }
+
+       if (byte & CALL_ECT_CALL_STATE_ACTIVE) {
+               *cssu = SS_MT_VOICECALL_TRANSFERRED;
+               DBG("Call has been connected with the other remote "
+                       "party in explicit call transfer operation.");
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_remote_address(GIsiSubBlockIter *iter,
+                                       struct ofono_phone_number *number,
+                                       int *index)
+{
+       uint8_t type, len;
+       char *addr;
+
+       if (!g_isi_sb_iter_get_byte(iter, &type, 2))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &len, 5))
+               return FALSE;
+
+       if (len > OFONO_MAX_PHONE_NUMBER_LENGTH)
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_alpha_tag(iter, &addr, 2 * len, 6))
+               return FALSE;
+
+       strncpy(number->number, addr, len);
+       number->number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       number->type = type;
+
+       g_free(addr);
+
+       return TRUE;
+}
+
+static gboolean decode_cug_info(GIsiSubBlockIter *iter, int *index, int *cssu)
+{
+       uint8_t pref;
+       uint8_t access;
+       uint16_t word;
+
+       if (!g_isi_sb_iter_get_byte(iter, &pref, 2))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_byte(iter, &access, 3))
+               return FALSE;
+
+       if (!g_isi_sb_iter_get_word(iter, &word, 4))
+               return FALSE;
+
+       DBG("Preferential CUG: 0x%02X", pref);
+       DBG("CUG output access: 0x%02X", access);
+
+       *index = word;
+       *cssu = SS_MO_CUG_CALL;
+
+       return TRUE;
+}
+
+static void notification_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_voicecall *ovc = data;
+       GIsiSubBlockIter iter;
+
+       struct ofono_phone_number number;
+       int index = 0;
+       int cssi = -1;
+       int cssu = -1;
+       uint8_t call_id;
+
+       if (ovc == NULL || g_isi_msg_id(msg) != CALL_GSM_NOTIFICATION_IND ||
+                       !g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       (call_id & 7) == 0)
+               return;
+
+       DBG("Received CallServer notification for call: 0x%02X", call_id);
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case CALL_GSM_NOTIFY:
+                       if (!decode_notify(&iter))
+                               return;
+
+                       break;
+
+               case CALL_GSM_SS_CODE:
+                       if (!decode_ss_code(&iter, &cssi, &cssu))
+                               return;
+
+                       break;
+
+               case CALL_GSM_SS_STATUS:
+                       if (!decode_ss_status(&iter))
+                               return;
+
+                       break;
+
+               case CALL_GSM_SS_NOTIFY:
+                       if (!decode_ss_notify(&iter, &cssi, &cssu))
+                               return;
+
+                       break;
+
+               case CALL_GSM_SS_NOTIFY_INDICATOR:
+                       if (!decode_ss_notify_indicator(&iter, &cssi))
+                               return;
+
+                       break;
+
+               case CALL_GSM_SS_HOLD_INDICATOR:
+                       if (!decode_ss_hold_indicator(&iter, &cssu))
+                               return;
+
+                       break;
+
+               case CALL_GSM_SS_ECT_INDICATOR:
+                       if (!decode_ss_ect_indicator(&iter, &cssu))
+                               return;
+
+                       break;
+
+               case CALL_GSM_REMOTE_ADDRESS:
+                       if (!decode_remote_address(&iter, &number, &index))
+                               return;
+
+                       break;
+
+               case CALL_GSM_REMOTE_SUBADDRESS:
+                       break;
+
+               case CALL_GSM_CUG_INFO:
+                       if (!decode_cug_info(&iter, &index, &cssu))
+                               return;
+
+                       break;
+
+               case CALL_ORIGIN_INFO:
+                       break;
+
+               case CALL_GSM_ALERTING_PATTERN:
+                       break;
+
+               case CALL_ALERTING_INFO:
+                       break;
+               }
+       }
+
+       if (cssi != -1)
+               ofono_voicecall_ssn_mo_notify(ovc, call_id & 7, cssi, index);
+
+       if (cssu != -1)
+               ofono_voicecall_ssn_mt_notify(ovc, call_id & 7, cssu, index,
+                                               &number);
+}
+
+static void isi_call_answer_resp(const GIsiMessage *msg, void *data)
+{
+       struct isi_call_req_ctx *irc = data;
+       uint8_t call_id;
+
+       if (!check_response_status(msg, CALL_ANSWER_RESP) ||
+                       !g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       call_id == CALL_ID_NONE) {
+               isi_ctx_return_failure(irc);
+               return;
+       }
+
+       isi_ctx_return_success(irc);
+}
+
+static struct isi_call_req_ctx *isi_call_answer_req(struct ofono_voicecall *ovc,
+                                                       uint8_t call_id,
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
+{
+       const uint8_t req[] = {
+               CALL_ANSWER_REQ,
+               call_id,
+               0
+       };
+
+       return isi_call_req(ovc, req, sizeof(req), isi_call_answer_resp,
+                               cb, data);
+}
+
+static void isi_call_release_resp(const GIsiMessage *msg, void *data)
+{
+       struct isi_call_req_ctx *irc = data;
+       GIsiSubBlockIter iter;
+       uint8_t cause_type;
+       uint8_t cause;
+
+       if (!check_response_status(msg, CALL_RELEASE_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
+                               !g_isi_sb_iter_get_byte(&iter, &cause, 3))
+                       goto error;
+       }
+
+       if ((cause_type == CALL_CAUSE_TYPE_SERVER ||
+                       cause_type == CALL_CAUSE_TYPE_CLIENT) &&
+                       (cause == CALL_CAUSE_RELEASE_BY_USER ||
+                       cause == CALL_CAUSE_BUSY_USER_REQUEST)) {
+               isi_ctx_return_success(irc);
+               return;
+       }
+
+error:
+       isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_ctx *isi_call_release_req(
+                                               struct ofono_voicecall *ovc,
+                                               uint8_t call_id,
+                                               enum call_cause_type cause_type,
+                                               uint8_t cause,
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
+{
+       const uint8_t req[] = {
+               CALL_RELEASE_REQ,
+               call_id,
+               1,      /* Sub-block count */
+               CALL_CAUSE,
+               4,      /* Sub-block length */
+               cause_type,
+               cause,
+       };
+
+       return isi_call_req(ovc, req, sizeof(req), isi_call_release_resp,
+                               cb, data);
+}
+
+static void isi_call_status_resp(const GIsiMessage *msg, void *data)
+{
+       struct isi_call_req_ctx *irc = data;
+       struct ofono_voicecall *ovc = irc->ovc;
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       GIsiSubBlockIter iter;
+       struct isi_call *call = NULL;
+
+       if (!check_response_status(msg, CALL_STATUS_RESP)) {
+               isi_ctx_return_failure(irc);
+               return;
+       }
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               switch (g_isi_sb_iter_get_id(&iter)) {
+               case CALL_STATUS_INFO:
+                       call = isi_call_status_info_sb_proc(ivc, &iter);
+                       break;
+
+               case CALL_ADDR_AND_STATUS_INFO:
+                       call = isi_call_addr_and_status_info_sb_proc(ivc,
+                                                                       &iter);
+                       if (call)
+                               isi_call_notify(ovc, call);
+                       break;
+
+               case CALL_CAUSE:
+
+                       if (call)
+                               isi_call_cause_sb_proc(ivc, call, &iter);
+                       break;
+               }
+       }
+
+       isi_ctx_return_success(irc);
+}
+
+static struct isi_call_req_ctx *isi_call_status_req(struct ofono_voicecall *ovc,
+                                                       uint8_t call_id,
+                                                       uint8_t mode,
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
+{
+       const uint8_t req[] = {
+               CALL_STATUS_REQ,
+               call_id,
+               1,      /* Sub-block count */
+               CALL_STATUS_MODE,
+               4,      /* Sub-block length */
+               mode, 0,
+       };
+
+       return isi_call_req(ovc, req, sizeof(req), isi_call_status_resp,
+                               cb, data);
+}
+
+static void isi_call_control_resp(const GIsiMessage *msg, void *data)
+{
+       struct isi_call_req_ctx *irc = data;
+       GIsiSubBlockIter iter;
+       uint8_t cause = CALL_CAUSE_NO_CAUSE;
+       uint8_t cause_type = 0;
+
+       if (!check_response_status(msg, CALL_CONTROL_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
+                               !g_isi_sb_iter_get_byte(&iter, &cause, 3))
+                       goto error;
+       }
+
+       if (cause == CALL_CAUSE_NO_CAUSE) {
+               isi_ctx_return_success(irc);
+               return;
+       }
+
+error:
+       isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_ctx *isi_call_control_req(
+                                               struct ofono_voicecall *ovc,
+                                               uint8_t call_id,
+                                               enum call_operation op,
+                                               uint8_t info,
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
+{
+       const uint8_t req[] = {
+               CALL_CONTROL_REQ,
+               call_id,
+               1,      /* Sub-block count */
+               CALL_OPERATION,
+               4,      /* Sub-block length */
+               op, info,
+       };
+
+       return isi_call_req(ovc, req, sizeof(req), isi_call_control_resp,
+                               cb, data);
+}
+
+static struct isi_call_req_ctx *isi_call_deflect_req(
+                                               struct ofono_voicecall *ovc,
+                                               uint8_t call_id,
+                                               uint8_t address_type,
+                                               const char address[21],
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
+{
+       size_t addr_len = strlen(address);
+       size_t sub_len = (6 + 2 * addr_len + 3) & ~3;
+       size_t i, offset = 3 + 4 + 6;
+       size_t rlen = 3 + 4 + sub_len;
+       uint8_t req[3 + 4 + 6 + 40] = {
+               CALL_CONTROL_REQ,
+               call_id,
+               2,              /* Sub-block count */
+               CALL_OPERATION,
+               4,              /* Sub-block length */
+               CALL_GSM_OP_DEFLECT, 0,
+               CALL_GSM_DEFLECTION_ADDRESS,
+               sub_len,        /* Sub-block length */
+               address_type & 0x7F,
+               0x7,            /* Default presentation */
+               0,              /* Filler */
+               addr_len,
+       };
+
+       if (addr_len > 20) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return NULL;
+       }
+
+       for (i = 0; i < addr_len; i++)
+               req[offset + 2 * i + 1] = address[i];
+
+       return isi_call_req(ovc, req, rlen, isi_call_control_resp, cb, data);
+}
+
+static void isi_call_control_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_voicecall *ovc = data;
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       GIsiSubBlockIter iter;
+       uint8_t cause_type = 0, cause = 0;
+
+       if (ivc == NULL || g_isi_msg_id(msg) != CALL_CONTROL_IND)
+               return;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
+                       continue;
+               if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
+                               !g_isi_sb_iter_get_byte(&iter, &cause, 3))
+                       return;
+       }
+
+       if (ivc->control_req_irc) {
+               if (!cause)
+                       isi_ctx_return_success(ivc->control_req_irc);
+               else
+                       isi_ctx_return_failure(ivc->control_req_irc);
+
+               ivc->control_req_irc = NULL;
+       }
+}
+
+static void isi_call_dtmf_send_resp(const GIsiMessage *msg, void *data)
+{
+       struct isi_call_req_ctx *irc = data;
+       GIsiSubBlockIter iter;
+       uint8_t cause_type;
+       uint8_t cause = CALL_CAUSE_NO_CAUSE;
+
+       if (!check_response_status(msg, CALL_DTMF_SEND_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
+                               !g_isi_sb_iter_get_byte(&iter, &cause, 3))
+                       goto error;
+       }
+
+       if (cause == CALL_CAUSE_NO_CAUSE) {
+               isi_ctx_return_success(irc);
+               return;
+       }
+
+error:
+       isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_ctx *isi_call_dtmf_send_req(
+                                               struct ofono_voicecall *ovc,
+                                               uint8_t call_id,
+                                               const char *string,
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
+{
+       size_t str_len = strlen(string);
+       size_t sub_len = 4 + ((2 * str_len + 3) & ~3);
+       size_t i, offset = 3 + 4 + 8 + 4;
+       size_t rlen = 3 + 4 + 8 + sub_len;
+       uint8_t req[3 + 4 + 8 + (255 & ~3)] = {
+               CALL_DTMF_SEND_REQ, call_id, 3,
+               CALL_DTMF_INFO, 4, CALL_DTMF_ENABLE_TONE_IND_SEND, 0,
+               CALL_DTMF_TIMERS, 8,
+               0, 200, /* duration in ms */
+               0, 100, /* gap in ms */
+               0, 0,   /* filler */
+               CALL_DTMF_STRING, sub_len,
+               100,     /* pause length */
+               str_len,
+               /* string */
+       };
+
+       if (sub_len >= 256) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return FALSE;
+       }
+
+       for (i = 0; i < str_len; i++)
+               req[offset + 2 * i + 1] = string[i];
+
+       return isi_call_req(ovc, req, rlen, isi_call_dtmf_send_resp, cb, data);
+}
+
+static void isi_dial(struct ofono_voicecall *ovc,
+                       const struct ofono_phone_number *number,
+                       enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+                       void *data)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       gboolean have_pn_call = g_isi_client_resource(ivc->client) == PN_CALL;
+       unsigned char presentation;
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_INVOCATION:
+               presentation = CALL_PRESENTATION_RESTRICTED;
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               presentation = CALL_PRESENTATION_ALLOWED;
+               break;
+       case OFONO_CLIR_OPTION_DEFAULT:
+       default:
+               presentation = have_pn_call ? CALL_GSM_PRESENTATION_DEFAULT :
+                               CALL_MODEM_PROP_PRESENT_DEFAULT;
+       }
+
+       if (have_pn_call)
+               isi_call_create_req(ovc, presentation, number->type,
+                                       number->number, cb, data);
+       else
+               isi_modem_call_create_req(ovc, presentation, number->type,
+                                               number->number, cb, data);
+}
+
+static void isi_answer(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb,
+                       void *data)
+{
+       isi_call_answer_req(ovc, CALL_ID_ALL, cb, data);
+}
+
+static void isi_hangup_current(struct ofono_voicecall *ovc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       /*
+        * Hangup call(s) that are not held or waiting:
+        * active calls or calls in progress.
+        */
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       int id = 0;
+       uint8_t cause = CALL_CAUSE_RELEASE_BY_USER;
+
+       for (id = 1; id <= 7; id++) {
+               if (ivc->calls[id].call_id & CALL_ID_WAITING)
+                       continue;
+               if (ivc->calls[id].call_id & CALL_ID_HOLD)
+                       continue;
+
+               switch (ivc->calls[id].status) {
+               case CALL_STATUS_CREATE:
+               case CALL_STATUS_COMING:
+               case CALL_STATUS_MO_ALERTING:
+               case CALL_STATUS_ANSWERED:
+               case CALL_STATUS_HOLD_INITIATED:
+                       goto release_by_id;
+               case CALL_STATUS_MT_ALERTING:
+                       cause = CALL_CAUSE_BUSY_USER_REQUEST;
+                       goto release_by_id;
+               case CALL_STATUS_PROCEEDING:
+                       if (ivc->calls[id].mode_info & CALL_MODE_ORIGINATOR)
+                               cause = CALL_CAUSE_BUSY_USER_REQUEST;
+                       goto release_by_id;
+               }
+       }
+
+       id = CALL_ID_ACTIVE;
+
+release_by_id:
+       isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT, cause, cb, data);
+}
+
+static void isi_release_all_held(struct ofono_voicecall *ovc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       isi_call_release_req(ovc, CALL_ID_HOLD, CALL_CAUSE_TYPE_CLIENT,
+                               CALL_CAUSE_RELEASE_BY_USER, cb, data);
+}
+
+static void isi_set_udub(struct ofono_voicecall *ovc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       /* Release waiting calls */
+       isi_call_release_req(ovc, CALL_ID_WAITING,
+                               CALL_CAUSE_TYPE_CLIENT,
+                               CALL_CAUSE_BUSY_USER_REQUEST, cb, data);
+}
+
+static void isi_retrieve(struct ofono_voicecall *ovc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       isi_call_control_req(ovc, CALL_ID_HOLD, CALL_OP_RETRIEVE, 0, cb, data);
+}
+
+static void isi_wait_and_answer(struct isi_call_req_ctx *irc, int event)
+{
+       DBG("irc=%p event=%u", (void *) irc, event);
+
+       if (event != CALL_STATUS_TERMINATED)
+               return;
+
+       isi_answer(irc->ovc, irc->cb, irc->data);
+       isi_ctx_free(irc);
+}
+
+static void isi_wait_and_retrieve(struct isi_call_req_ctx *irc, int event)
+{
+       DBG("irc=%p event=%u", (void *) irc, event);
+
+       if (event != CALL_STATUS_TERMINATED)
+               return;
+
+       isi_retrieve(irc->ovc, irc->cb, irc->data);
+       isi_ctx_free(irc);
+}
+
+static void isi_release_all_active(struct ofono_voicecall *ovc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       struct isi_call_req_ctx *irc;
+       int id = 0;
+       int waiting = 0;
+       int active = 0;
+       int hold = 0;
+
+       for (id = 1; id <= 7; id++) {
+
+               if (ivc->calls[id].call_id & CALL_ID_WAITING)
+                       waiting++;
+
+               if (ivc->calls[id].call_id & CALL_ID_HOLD)
+                       hold++;
+
+               if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
+                       active++;
+       }
+
+       if (!active) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       irc = isi_call_release_req(ovc, CALL_ID_ACTIVE,
+                                       CALL_CAUSE_TYPE_CLIENT,
+                                       CALL_CAUSE_RELEASE_BY_USER,
+                                       cb, data);
+       if (irc == NULL)
+               return;
+
+       if (waiting)
+               isi_ctx_queue(irc, isi_wait_and_answer);
+       else if (hold)
+               isi_ctx_queue(irc, isi_wait_and_retrieve);
+}
+
+static void isi_hold_all_active(struct ofono_voicecall *ovc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       int id = 0;
+       int op = 0;
+       int waiting = 0;
+       int active = 0;
+       int hold = 0;
+
+       for (id = 1; id <= 7; id++) {
+
+               if (ivc->calls[id].call_id & CALL_ID_WAITING)
+                       waiting++;
+
+               if (ivc->calls[id].call_id & CALL_ID_HOLD)
+                       hold++;
+
+               if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
+                       active++;
+       }
+
+       if (!waiting && !hold && !active) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       if (waiting) {
+               isi_call_answer_req(ovc, CALL_ID_WAITING, cb, data);
+
+       } else if (hold) {
+
+               if (active) {
+                       op = CALL_OP_SWAP;
+                       id = CALL_ID_ACTIVE;
+               } else {
+                       op = CALL_OP_RETRIEVE;
+                       id = CALL_ID_HOLD;
+               }
+               isi_call_control_req(ovc, id, op, 0, cb, data);
+
+       } else if (active) {
+               id = CALL_ID_ACTIVE;
+               op = CALL_OP_HOLD;
+
+               isi_call_control_req(ovc, id, op, 0, cb, data);
+       }
+}
+
+static void isi_release_specific(struct ofono_voicecall *ovc, int id,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       const struct isi_call *status;
+       uint8_t cause;
+
+       if (id < 1 || id > 7) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       status = &ivc->calls[id];
+       cause = CALL_CAUSE_RELEASE_BY_USER;
+
+       switch (status->status) {
+       case CALL_STATUS_MT_ALERTING:
+       case CALL_STATUS_WAITING:
+               cause = CALL_CAUSE_BUSY_USER_REQUEST;
+               break;
+
+       case CALL_STATUS_PROCEEDING:
+
+               if ((status->mode_info & CALL_MODE_ORIGINATOR))
+                       cause = CALL_CAUSE_BUSY_USER_REQUEST;
+                       break;
+       }
+
+       isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT, cause, cb, data);
+}
+
+static void isi_private_chat(struct ofono_voicecall *ovc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       if (id < 1 || id > 7) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       isi_call_control_req(ovc, id, CALL_OP_CONFERENCE_SPLIT, 0, cb, data);
+}
+
+static void isi_create_multiparty(struct ofono_voicecall *ovc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       isi_call_control_req(ovc, CALL_ID_ALL, CALL_OP_CONFERENCE_BUILD, 0,
+                               cb, data);
+}
+
+static void isi_transfer(struct ofono_voicecall *ovc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       uint8_t id;
+
+       for (id = 1; id <= 7; id++) {
+
+               if (ivc->calls[id].status == CALL_STATUS_MO_ALERTING)
+                       break;
+       }
+
+       if (id > 7)
+               id = CALL_ID_ACTIVE;
+
+       isi_call_control_req(ovc, id, CALL_GSM_OP_TRANSFER, 0, cb, data);
+}
+
+static void isi_deflect(struct ofono_voicecall *ovc,
+                       const struct ofono_phone_number *ph,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       isi_call_deflect_req(ovc, CALL_ID_WAITING, ph->type, ph->number,
+                               cb, data);
+}
+
+static void isi_swap_without_accept(struct ofono_voicecall *ovc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       int id = 0;
+       int op = 0;
+       int active = 0;
+       int hold = 0;
+
+       for (id = 1; id <= 7; id++) {
+
+               if (ivc->calls[id].call_id & CALL_ID_HOLD)
+                       hold++;
+
+               if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
+                       active++;
+       }
+
+       if (hold && active) {
+               id = CALL_ID_ACTIVE;
+               op = CALL_OP_SWAP;
+       } else if (active) {
+               id = CALL_ID_ACTIVE;
+               op = CALL_OP_HOLD;
+       } else if (hold) {
+               id = CALL_ID_HOLD;
+               op = CALL_OP_RETRIEVE;
+       } else {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       isi_call_control_req(ovc, id, op, 0, cb, data);
+}
+
+static void isi_send_tones(struct ofono_voicecall *ovc, const char *tones,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       isi_call_dtmf_send_req(ovc, CALL_ID_ALL, tones, cb, data);
+}
+
+static void subscribe_indications(GIsiClient *cl, void *data)
+{
+       g_isi_client_ind_subscribe(cl, CALL_STATUS_IND, isi_call_status_ind_cb,
+                                       data);
+       g_isi_client_ind_subscribe(cl, CALL_CONTROL_IND, isi_call_control_ind_cb,
+                                       data);
+       g_isi_client_ind_subscribe(cl, CALL_TERMINATED_IND,
+                                       isi_call_terminated_ind_cb, data);
+       g_isi_client_ind_subscribe(cl, CALL_GSM_NOTIFICATION_IND,
+                                       notification_ind_cb, data);
+
+}
+
+static void pn_call_verify_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_voicecall *ovc = data;
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("PN_CALL not reachable, removing client");
+               g_isi_client_destroy(ivc->pn_call);
+               ivc->pn_call = NULL;
+
+               if (ivc->pn_modem_call == NULL)
+                       ofono_voicecall_remove(ovc);
+
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       if (ivc == NULL || ivc->client != NULL)
+               return;
+
+       ivc->client = ivc->pn_call;
+
+       subscribe_indications(ivc->client, ovc);
+
+       if (!isi_call_status_req(ovc, CALL_ID_ALL,
+                                       CALL_STATUS_MODE_ADDR_AND_ORIGIN,
+                                       NULL, NULL))
+               DBG("Failed to request call status");
+
+       ofono_voicecall_register(ovc);
+}
+
+static void pn_modem_call_verify_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_voicecall *ovc = data;
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("PN_MODEM_CALL not reachable, removing client");
+               g_isi_client_destroy(ivc->pn_modem_call);
+               ivc->pn_modem_call = NULL;
+
+               if (ivc->pn_call == NULL)
+                       ofono_voicecall_remove(ovc);
+
+               return;
+       }
+
+       ISI_RESOURCE_DBG(msg);
+
+       if (ivc == NULL || ivc->client != NULL)
+               return;
+
+       ivc->client = ivc->pn_modem_call;
+
+       subscribe_indications(ivc->client, ovc);
+
+       if (!isi_call_status_req(ovc, CALL_ID_ALL,
+                                       CALL_STATUS_MODE_ADDR_AND_ORIGIN,
+                                       NULL, NULL))
+               DBG("Failed to request call status");
+
+       ofono_voicecall_register(ovc);
+}
+
+static int isi_probe(struct ofono_voicecall *ovc, unsigned int vendor,
+                       void *user)
+{
+       GIsiModem *modem = user;
+       struct isi_voicecall *ivc;
+       int id;
+
+       ivc = g_try_new0(struct isi_voicecall, 1);
+       if (ivc == NULL)
+               return -ENOMEM;
+
+       for (id = 0; id <= 7; id++)
+               ivc->calls[id].id = id;
+
+       ivc->pn_call = g_isi_client_create(modem, PN_CALL);
+       if (ivc->pn_call == NULL) {
+               g_free(ivc);
+               return -ENOMEM;
+       }
+
+       ivc->pn_modem_call = g_isi_client_create(modem, PN_MODEM_CALL);
+       if (ivc->pn_call == NULL) {
+               g_isi_client_destroy(ivc->pn_call);
+               g_free(ivc);
+               return -ENOMEM;
+       }
+
+       ofono_voicecall_set_data(ovc, ivc);
+
+       g_isi_client_verify(ivc->pn_call, pn_call_verify_cb, ovc, NULL);
+       g_isi_client_verify(ivc->pn_modem_call, pn_modem_call_verify_cb,
+                               ovc, NULL);
+
+       return 0;
+}
+
+static void isi_remove(struct ofono_voicecall *call)
+{
+       struct isi_voicecall *data = ofono_voicecall_get_data(call);
+
+       ofono_voicecall_set_data(call, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->pn_call);
+       g_isi_client_destroy(data->pn_modem_call);
+       g_free(data);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "isimodem",
+       .probe                  = isi_probe,
+       .remove                 = isi_remove,
+       .dial                   = isi_dial,
+       .answer                 = isi_answer,
+       .hangup_active          = isi_hangup_current,
+       .hold_all_active        = isi_hold_all_active,
+       .release_all_held       = isi_release_all_held,
+       .set_udub               = isi_set_udub,
+       .release_all_active     = isi_release_all_active,
+       .release_specific       = isi_release_specific,
+       .private_chat           = isi_private_chat,
+       .create_multiparty      = isi_create_multiparty,
+       .transfer               = isi_transfer,
+       .deflect                = isi_deflect,
+       .swap_without_accept    = isi_swap_without_accept,
+       .send_tones             = isi_send_tones,
+};
+
+void isi_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void isi_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/mbmmodem/gprs-context.c b/drivers/mbmmodem/gprs-context.c
new file mode 100644 (file)
index 0000000..e961afa
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "mbmmodem.h"
+
+#define MBM_E2NAP_DISCONNECTED 0
+#define MBM_E2NAP_CONNECTED 1
+#define MBM_E2NAP_CONNECTING 2
+
+#define AUTH_BUF_LENGTH OFONO_GPRS_MAX_USERNAME_LENGTH + \
+                       OFONO_GPRS_MAX_PASSWORD_LENGTH + 128
+
+#define MAX_DNS 5
+
+#define STATIC_IP_NETMASK "255.255.255.248"
+
+static const char *none_prefix[] = { NULL };
+static const char *e2ipcfg_prefix[] = { "*E2IPCFG:", NULL };
+static const char *enap_prefix[] = { "*ENAP:", NULL };
+
+static gboolean mbm_enap_poll(gpointer user_data);
+
+enum mbm_state {
+       MBM_NONE = 0,
+       MBM_ENABLING = 1,
+       MBM_DISABLING = 2,
+};
+
+struct gprs_context_data {
+       GAtChat *chat;
+       unsigned int active_context;
+       gboolean have_e2nap;
+       gboolean have_e2ipcfg;
+       unsigned int enap_source;
+       enum mbm_state mbm_state;
+       ofono_gprs_context_cb_t cb;
+       void *cb_data;                                  /* Callback data */
+       int enap;                                   /* State of the call */
+};
+
+static void mbm_e2ipcfg_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtResultIter iter;
+       int numdns = 0;
+       int type;
+       const char *str;
+       const char *ip = NULL;
+       const char *gateway = NULL;
+       const char *dns[MAX_DNS + 1];
+       struct ofono_modem *modem;
+       const char *interface;
+       gboolean success = FALSE;
+
+       DBG("ok %d", ok);
+
+       if (!ok)
+               goto out;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*E2IPCFG:") == FALSE)
+               return;
+
+       while (g_at_result_iter_open_list(&iter)) {
+               if (g_at_result_iter_next_number(&iter, &type) == FALSE)
+                       break;
+
+               if (g_at_result_iter_next_string(&iter, &str) == FALSE)
+                       break;
+
+               switch (type) {
+               case 1:
+                       ip = str;
+                       break;
+               case 2:
+                       gateway = str;
+                       break;
+               case 3:
+                       if (numdns < MAX_DNS)
+                               dns[numdns++] = str;
+                       break;
+               default:
+                       break;
+               }
+
+               if (g_at_result_iter_close_list(&iter) == FALSE)
+                       break;
+       }
+
+       dns[numdns] = NULL;
+
+       if (ip && gateway && numdns)
+               success = TRUE;
+
+out:
+       modem = ofono_gprs_context_get_modem(gc);
+       interface = ofono_modem_get_string(modem, "NetworkInterface");
+
+       ofono_info("IP: %s  Gateway: %s", ip, gateway);
+       ofono_info("DNS: %s, %s", dns[0], dns[1]);
+
+       ofono_gprs_context_set_interface(gc, interface);
+
+       if (success) {
+               ofono_gprs_context_set_ipv4_address(gc, ip, TRUE);
+               ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK);
+               ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+               ofono_gprs_context_set_ipv4_gateway(gc, gateway);
+       } else
+               ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE);
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+
+       gcd->mbm_state = MBM_NONE;
+       gcd->cb = NULL;
+       gcd->cb_data = NULL;
+}
+
+static void mbm_get_ip_details(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_modem *modem;
+       const char *interface;
+       char buf[64];
+
+       DBG("");
+
+       if (gcd->have_e2ipcfg) {
+               g_at_chat_send(gcd->chat, "AT*E2IPCFG?", e2ipcfg_prefix,
+                               mbm_e2ipcfg_cb, gc, NULL);
+               return;
+       }
+
+       snprintf(buf, sizeof(buf), "AT+CGPADDR=%u", gcd->active_context);
+       g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+
+       modem = ofono_gprs_context_get_modem(gc);
+       interface = ofono_modem_get_string(modem, "NetworkInterface");
+
+       ofono_gprs_context_set_interface(gc, interface);
+       ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE);
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+
+       gcd->mbm_state = MBM_NONE;
+       gcd->cb = NULL;
+       gcd->cb_data = NULL;
+}
+
+static void mbm_state_changed(struct ofono_gprs_context *gc, int state)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("state %d", state);
+
+       if (gcd->active_context == 0)
+               return;
+
+       switch (state) {
+       case MBM_E2NAP_DISCONNECTED:
+               DBG("disconnected");
+
+               if (gcd->mbm_state == MBM_DISABLING) {
+                       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+                       gcd->cb = NULL;
+               } else if (gcd->mbm_state == MBM_ENABLING) {
+                       CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+                       gcd->cb = NULL;
+               } else {
+                       ofono_gprs_context_deactivated(gc, gcd->active_context);
+               }
+
+               gcd->mbm_state = MBM_NONE;
+               gcd->cb_data = NULL;
+               gcd->active_context = 0;
+
+               break;
+
+       case MBM_E2NAP_CONNECTED:
+               DBG("connected");
+
+               if (gcd->mbm_state == MBM_ENABLING)
+                       mbm_get_ip_details(gc);
+
+               break;
+
+       case MBM_E2NAP_CONNECTING:
+               DBG("connecting");
+               break;
+
+       default:
+               break;
+       };
+
+       gcd->enap = state;
+}
+
+static void mbm_enap_poll_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtResultIter iter;
+       int state;
+
+       DBG("ok %d", ok);
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*ENAP:") == FALSE)
+               return;
+
+       g_at_result_iter_next_number(&iter, &state);
+
+       mbm_state_changed(gc, state);
+
+       if ((state == MBM_E2NAP_CONNECTED && gcd->mbm_state == MBM_DISABLING) ||
+                       state == MBM_E2NAP_CONNECTING)
+               gcd->enap_source = g_timeout_add_seconds(1, mbm_enap_poll, gc);
+}
+
+static gboolean mbm_enap_poll(gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       g_at_chat_send(gcd->chat, "AT*ENAP?", enap_prefix,
+                               mbm_enap_poll_cb, gc, NULL);
+
+       gcd->enap_source = 0;
+
+       return FALSE;
+}
+
+static void at_enap_down_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+
+       DBG("ok %d", ok);
+
+       /* Now we have to wait for the unsolicited notification to arrive */
+       if (ok && gcd->enap != 0) {
+               gcd->mbm_state = MBM_DISABLING;
+               gcd->cb = cb;
+               gcd->cb_data = cbd->data;
+
+               if (gcd->have_e2nap == FALSE)
+                       g_at_chat_send(gcd->chat, "AT*ENAP?", enap_prefix,
+                                       mbm_enap_poll_cb, gc, NULL);
+
+               return;
+       }
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void mbm_enap_up_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+
+       DBG("ok %d", ok);
+
+       if (ok) {
+               gcd->mbm_state = MBM_ENABLING;
+               gcd->cb = cb;
+               gcd->cb_data = cbd->data;
+
+               if (gcd->have_e2nap == FALSE)
+                       g_at_chat_send(gcd->chat, "AT*ENAP?", enap_prefix,
+                                       mbm_enap_poll_cb, gc, NULL);
+
+               return;
+       }
+
+       gcd->active_context = 0;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void mbm_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *ncbd;
+       char buf[64];
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               struct ofono_error error;
+
+               gcd->active_context = 0;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               cb(&error, cbd->data);
+               return;
+       }
+
+       ncbd = g_memdup(cbd, sizeof(struct cb_data));
+
+       snprintf(buf, sizeof(buf), "AT*ENAP=1,%u", gcd->active_context);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               mbm_enap_up_cb, ncbd, g_free) > 0)
+               return;
+
+       g_free(ncbd);
+
+       gcd->active_context = 0;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void mbm_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[AUTH_BUF_LENGTH];
+       int len;
+
+       /* IPv6 support not implemented */
+       if (ctx->proto != OFONO_GPRS_PROTO_IP)
+               goto error;
+
+       DBG("cid %u", ctx->cid);
+
+       gcd->active_context = ctx->cid;
+
+       cbd->user = gc;
+
+       len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+                               ctx->apn);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               mbm_cgdcont_cb, cbd, g_free) == 0)
+               goto error;
+
+       /*
+        * Set username and password, this should be done after CGDCONT
+        * or an error can occur.  We don't bother with error checking
+        * here
+        * */
+       snprintf(buf, sizeof(buf), "AT*EIAAUW=%d,1,\"%s\",\"%s\"",
+                       ctx->cid, ctx->username, ctx->password);
+
+       g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+
+       return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void mbm_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int cid,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       DBG("cid %u", cid);
+
+       cbd->user = gc;
+
+       if (g_at_chat_send(gcd->chat, "AT*ENAP=0", none_prefix,
+                               at_enap_down_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void e2nap_notifier(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       GAtResultIter iter;
+       int state;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*E2NAP:") == FALSE)
+               return;
+
+       g_at_result_iter_next_number(&iter, &state);
+
+       mbm_state_changed(gc, state);
+}
+
+static void mbm_e2nap_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("ok %d", ok);
+
+       gcd->have_e2nap = ok;
+
+       if (ok)
+               g_at_chat_register(gcd->chat, "*E2NAP:", e2nap_notifier,
+                                       FALSE, gc, NULL);
+}
+
+static void mbm_e2ipcfg_query_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       gcd->have_e2ipcfg = ok;
+}
+
+static int mbm_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+
+       DBG("");
+
+       gcd = g_try_new0(struct gprs_context_data, 1);
+       if (gcd == NULL)
+               return -ENOMEM;
+
+       gcd->chat = g_at_chat_clone(chat);
+
+       ofono_gprs_context_set_data(gc, gcd);
+
+       g_at_chat_send(gcd->chat, "AT*ENAPDBG=1", none_prefix,
+                               NULL, NULL, NULL);
+
+       g_at_chat_send(gcd->chat, "AT*E2NAP=1", none_prefix,
+                       mbm_e2nap_cb, gc, NULL);
+       g_at_chat_send(gcd->chat, "AT*E2IPCFG=?", e2ipcfg_prefix,
+                       mbm_e2ipcfg_query_cb, gc, NULL);
+
+       return 0;
+}
+
+static void mbm_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("");
+
+       if (gcd->enap_source) {
+               g_source_remove(gcd->enap_source);
+               gcd->enap_source = 0;
+       }
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "mbmmodem",
+       .probe                  = mbm_gprs_context_probe,
+       .remove                 = mbm_gprs_context_remove,
+       .activate_primary       = mbm_gprs_activate_primary,
+       .deactivate_primary     = mbm_gprs_deactivate_primary,
+};
+
+void mbm_gprs_context_init(void)
+{
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void mbm_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/mbmmodem/location-reporting.c b/drivers/mbmmodem/location-reporting.c
new file mode 100644 (file)
index 0000000..7c50ac2
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ProFUSION embedded systems.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/location-reporting.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gattty.h"
+
+#include "mbmmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *e2gpsctl_prefix[] = { "*E2GPSCTL:", NULL };
+
+struct gps_data {
+       GAtChat *chat;
+       GAtChat *data_chat;
+};
+
+static void mbm_e2gpsctl_disable_cb(gboolean ok, GAtResult *result,
+                                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_location_reporting *lr = cbd->user;
+       ofono_location_reporting_disable_cb_t cb = cbd->cb;
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+
+       DBG("lr=%p, ok=%d", lr, ok);
+
+       if (!ok) {
+               struct ofono_error error;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               cb(&error, cbd->data);
+
+               return;
+       }
+
+       g_at_chat_unref(gd->data_chat);
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static void mbm_location_reporting_disable(struct ofono_location_reporting *lr,
+                               ofono_location_reporting_disable_cb_t cb,
+                               void *data)
+{
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       DBG("lr=%p", lr);
+
+       cbd->user = lr;
+
+       if (g_at_chat_send(gd->chat, "AT*E2GPSCTL=0,5,1", none_prefix,
+                               mbm_e2gpsctl_disable_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static int enable_data_stream(struct ofono_location_reporting *lr)
+{
+       struct ofono_modem *modem;
+       const char *gps_dev;
+       GHashTable *options;
+       GIOChannel *channel;
+       GIOStatus status;
+       gsize written;
+       int fd;
+
+       modem = ofono_location_reporting_get_modem(lr);
+       gps_dev = ofono_modem_get_string(modem, "GPSDevice");
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               return -1;
+
+       g_hash_table_insert(options, "Baud", "115200");
+
+       channel = g_at_tty_open(gps_dev, options);
+
+       g_hash_table_destroy(options);
+
+       if (channel == NULL)
+               return -1;
+
+       fd = g_io_channel_unix_get_fd(channel);
+       status = g_io_channel_write_chars(channel, "AT*E2GPSNPD\r\n", -1,
+                                                               &written, NULL);
+
+       g_io_channel_set_close_on_unref(channel, FALSE);
+       g_io_channel_unref(channel);
+
+       if (status != G_IO_STATUS_NORMAL || written != 13) {
+               close(fd);
+
+               return -1;
+       }
+
+       return fd;
+}
+
+static void mbm_e2gpsctl_enable_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_location_reporting_enable_cb_t cb = cbd->cb;
+       struct ofono_location_reporting *lr = cbd->user;
+       struct ofono_error error;
+       int fd;
+
+       DBG("lr=%p ok=%d", lr, ok);
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+
+               return;
+       }
+
+       fd = enable_data_stream(lr);
+
+       if (fd < 0) {
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+
+               return;
+       }
+
+       cb(&error, fd, cbd->data);
+       close(fd);
+}
+
+static void mbm_location_reporting_enable(struct ofono_location_reporting *lr,
+                                       ofono_location_reporting_enable_cb_t cb,
+                                       void *data)
+{
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       DBG("lr=%p", lr);
+
+       cbd->user = lr;
+
+       if (g_at_chat_send(gd->chat, "AT*E2GPSCTL=1,5,1", none_prefix,
+                               mbm_e2gpsctl_enable_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, -1, data);
+       g_free(cbd);
+}
+
+static void mbm_location_reporting_support_cb(gboolean ok, GAtResult *result,
+                                                       gpointer user_data)
+{
+       struct ofono_location_reporting *lr = user_data;
+
+       if (!ok) {
+               ofono_location_reporting_remove(lr);
+
+               return;
+       }
+
+       ofono_location_reporting_register(lr);
+}
+
+static int mbm_location_reporting_probe(struct ofono_location_reporting *lr,
+                                               unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gps_data *gd;
+
+       gd = g_try_new0(struct gps_data, 1);
+       if (gd == NULL)
+               return -ENOMEM;
+
+       gd->chat = g_at_chat_clone(chat);
+
+       ofono_location_reporting_set_data(lr, gd);
+
+       g_at_chat_send(gd->chat, "AT*E2GPSCTL=?", e2gpsctl_prefix,
+                                       mbm_location_reporting_support_cb,
+                                       lr, NULL);
+
+       return 0;
+}
+
+static void mbm_location_reporting_remove(struct ofono_location_reporting *lr)
+{
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+
+       ofono_location_reporting_set_data(lr, NULL);
+
+       g_at_chat_unref(gd->chat);
+       g_free(gd);
+}
+
+static struct ofono_location_reporting_driver driver = {
+       .name                   = "mbmmodem",
+       .type                   = OFONO_LOCATION_REPORTING_TYPE_NMEA,
+       .probe                  = mbm_location_reporting_probe,
+       .remove                 = mbm_location_reporting_remove,
+       .enable                 = mbm_location_reporting_enable,
+       .disable                = mbm_location_reporting_disable,
+};
+
+void mbm_location_reporting_init()
+{
+       ofono_location_reporting_driver_register(&driver);
+}
+
+void mbm_location_reporting_exit()
+{
+       ofono_location_reporting_driver_unregister(&driver);
+}
diff --git a/drivers/mbmmodem/mbmmodem.c b/drivers/mbmmodem/mbmmodem.c
new file mode 100644 (file)
index 0000000..ee8d775
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "mbmmodem.h"
+
+static int mbmmodem_init(void)
+{
+       mbm_gprs_context_init();
+       mbm_stk_init();
+       mbm_location_reporting_init();
+
+       return 0;
+}
+
+static void mbmmodem_exit(void)
+{
+       mbm_location_reporting_exit();
+       mbm_stk_exit();
+       mbm_gprs_context_exit();
+}
+
+OFONO_PLUGIN_DEFINE(mbmmodem, "MBM modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       mbmmodem_init, mbmmodem_exit)
diff --git a/drivers/mbmmodem/mbmmodem.h b/drivers/mbmmodem/mbmmodem.h
new file mode 100644 (file)
index 0000000..e87501a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void mbm_gprs_context_init(void);
+extern void mbm_gprs_context_exit(void);
+
+extern void mbm_stk_init(void);
+extern void mbm_stk_exit(void);
+
+extern void mbm_location_reporting_init();
+extern void mbm_location_reporting_exit();
diff --git a/drivers/mbmmodem/stk.c b/drivers/mbmmodem/stk.c
new file mode 100644 (file)
index 0000000..e33c91b
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/stk.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "mbmmodem.h"
+
+struct stk_data {
+       GAtChat *chat;
+};
+
+static const char *none_prefix[] = { NULL };
+static const char *stke_prefix[] = { "*STKE:", NULL };
+
+static void stke_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_envelope_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       struct ofono_error error;
+       const guint8 *pdu = NULL;
+       gint len = 0;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto done;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*STKE:") == FALSE)
+               goto done;
+
+       /* Response data is optional */
+       g_at_result_iter_next_hexstring(&iter, &pdu, &len);
+
+       DBG("len %d", len);
+
+done:
+       cb(&error, pdu, len, cbd->data);
+}
+
+static void mbm_stk_envelope(struct ofono_stk *stk, int length,
+                               const unsigned char *command,
+                               ofono_stk_envelope_cb_t cb, void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, 64 + length * 2);
+       int len;
+
+       DBG("");
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT*STKE=\"");
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *command++);
+       len += sprintf(buf + len, "\"");
+
+       DBG("%s", buf);
+
+       if (g_at_chat_send(sd->chat, buf, stke_prefix,
+                                       stke_cb, cbd, g_free) > 0) {
+               g_free(buf);
+               return;
+       }
+
+error:
+       g_free(buf);
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+}
+
+static void stkr_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_stk_generic_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       DBG("");
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void mbm_stk_terminal_response(struct ofono_stk *stk, int length,
+                                       const unsigned char *command,
+                                       ofono_stk_generic_cb_t cb, void *data)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char *buf = g_try_new(char, 64 + length * 2);
+       int len;
+
+       DBG("");
+
+       if (buf == NULL)
+               goto error;
+
+       len = sprintf(buf, "AT*STKR=\"");
+       for (; length; length--)
+               len += sprintf(buf + len, "%02hhX", *command++);
+       len += sprintf(buf + len, "\"");
+
+       DBG("%s", buf);
+
+       if (g_at_chat_send(sd->chat, buf, none_prefix,
+                                       stkr_cb, cbd, g_free) > 0) {
+               g_free(buf);
+               return;
+       }
+
+error:
+       g_free(buf);
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void stki_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *pdu;
+       gint len;
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "*STKI:"))
+               return;
+
+       if (!g_at_result_iter_next_hexstring(&iter, &pdu, &len))
+               return;
+
+       ofono_stk_proactive_command_notify(stk, len, pdu);
+}
+
+static void stkn_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       GAtResultIter iter;
+       const guint8 *pdu;
+       gint len;
+
+       DBG("");
+
+       /* Proactive command has been handled by the modem. */
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*STKN:") == FALSE)
+               return;
+
+       if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE)
+               return;
+
+       if (len == 0)
+               return;
+
+       ofono_stk_proactive_command_handled_notify(stk, len, pdu);
+}
+
+static void stkend_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       DBG("");
+
+       ofono_stk_proactive_session_end_notify(stk);
+}
+
+static void mbm_stkc_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       DBG("");
+
+       if (!ok)
+               return;
+
+       g_at_chat_register(sd->chat, "*STKI:", stki_notify, FALSE, stk, NULL);
+       g_at_chat_register(sd->chat, "*STKN:", stkn_notify, FALSE, stk, NULL);
+       g_at_chat_register(sd->chat, "*STKEND",
+                                       stkend_notify, FALSE, stk, NULL);
+
+       ofono_stk_register(stk);
+}
+
+static int mbm_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct stk_data *sd;
+
+       DBG("");
+
+       sd = g_try_new0(struct stk_data, 1);
+       if (sd == NULL)
+               return -ENOMEM;
+
+       sd->chat = g_at_chat_clone(chat);
+
+       ofono_stk_set_data(stk, sd);
+
+       /* Perform PROFILE DOWNLOAD and enable *STKI / *STKN */
+       g_at_chat_send(sd->chat, "AT*STKC=1,\"19E1FFFF0000FF7FFF03FEFF\"",
+                       none_prefix, mbm_stkc_cb, stk, NULL);
+
+       return 0;
+}
+
+static void mbm_stk_remove(struct ofono_stk *stk)
+{
+       struct stk_data *sd = ofono_stk_get_data(stk);
+
+       DBG("");
+
+       ofono_stk_set_data(stk, NULL);
+
+       g_at_chat_unref(sd->chat);
+       g_free(sd);
+}
+
+static struct ofono_stk_driver driver = {
+       .name                   = "mbmmodem",
+       .probe                  = mbm_stk_probe,
+       .remove                 = mbm_stk_remove,
+       .envelope               = mbm_stk_envelope,
+       .terminal_response      = mbm_stk_terminal_response,
+};
+
+void mbm_stk_init(void)
+{
+       ofono_stk_driver_register(&driver);
+}
+
+void mbm_stk_exit(void)
+{
+       ofono_stk_driver_unregister(&driver);
+}
diff --git a/drivers/nwmodem/nwmodem.c b/drivers/nwmodem/nwmodem.c
new file mode 100644 (file)
index 0000000..9d65492
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "nwmodem.h"
+
+static int nwmodem_init(void)
+{
+       nw_radio_settings_init();
+
+       return 0;
+}
+
+static void nwmodem_exit(void)
+{
+       nw_radio_settings_exit();
+}
+
+OFONO_PLUGIN_DEFINE(nwmodem, "Novatel modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       nwmodem_init, nwmodem_exit)
diff --git a/drivers/nwmodem/nwmodem.h b/drivers/nwmodem/nwmodem.h
new file mode 100644 (file)
index 0000000..792814e
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void nw_radio_settings_init(void);
+extern void nw_radio_settings_exit(void);
diff --git a/drivers/nwmodem/radio-settings.c b/drivers/nwmodem/radio-settings.c
new file mode 100644 (file)
index 0000000..e6e405c
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "nwmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *nwrat_prefix[] = { "$NWRAT:", NULL };
+
+struct radio_settings_data {
+       GAtChat *chat;
+};
+
+static void nwrat_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+       enum ofono_radio_access_mode mode;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int value;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "$NWRAT:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &value) == FALSE)
+               goto error;
+
+       switch (value) {
+       case 0:
+               mode = OFONO_RADIO_ACCESS_MODE_ANY;
+               break;
+       case 1:
+               mode = OFONO_RADIO_ACCESS_MODE_GSM;
+               break;
+       case 2:
+               mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+               break;
+       default:
+               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+               return;
+       }
+
+       cb(&error, mode, cbd->data);
+
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void nw_query_rat_mode(struct ofono_radio_settings *rs,
+                               ofono_radio_settings_rat_mode_query_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(rsd->chat, "AT$NWRAT?", nwrat_prefix,
+                                       nwrat_query_cb, cbd, g_free) == 0) {
+               CALLBACK_WITH_FAILURE(cb, -1, data);
+               g_free(cbd);
+       }
+}
+
+static void nwrat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void nw_set_rat_mode(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode,
+                               ofono_radio_settings_rat_mode_set_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[20];
+       int value = 0;
+
+       switch (mode) {
+       case OFONO_RADIO_ACCESS_MODE_ANY:
+               value = 0;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_GSM:
+               value = 1;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_UMTS:
+               value = 2;
+               break;
+       case OFONO_RADIO_ACCESS_MODE_LTE:
+               goto error;
+       }
+
+       snprintf(buf, sizeof(buf), "AT$NWRAT=%u,2", value);
+
+       if (g_at_chat_send(rsd->chat, buf, none_prefix,
+                                       nwrat_modify_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void nwrat_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_radio_settings *rs = user_data;
+
+       if (!ok)
+               return;
+
+       ofono_radio_settings_register(rs);
+}
+
+static int nw_radio_settings_probe(struct ofono_radio_settings *rs,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct radio_settings_data *rsd;
+
+       rsd = g_try_new0(struct radio_settings_data, 1);
+       if (rsd == NULL)
+               return -ENOMEM;
+
+       rsd->chat = g_at_chat_clone(chat);
+
+       ofono_radio_settings_set_data(rs, rsd);
+
+       g_at_chat_send(rsd->chat, "AT$NWRAT=?", nwrat_prefix,
+                                       nwrat_support_cb, rs, NULL);
+
+       return 0;
+}
+
+static void nw_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+
+       ofono_radio_settings_set_data(rs, NULL);
+
+       g_at_chat_unref(rsd->chat);
+       g_free(rsd);
+}
+
+static struct ofono_radio_settings_driver driver = {
+       .name                   = "nwmodem",
+       .probe                  = nw_radio_settings_probe,
+       .remove                 = nw_radio_settings_remove,
+       .query_rat_mode         = nw_query_rat_mode,
+       .set_rat_mode           = nw_set_rat_mode
+};
+
+void nw_radio_settings_init(void)
+{
+       ofono_radio_settings_driver_register(&driver);
+}
+
+void nw_radio_settings_exit(void)
+{
+       ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/stemodem/caif_rtnl.c b/drivers/stemodem/caif_rtnl.c
new file mode 100644 (file)
index 0000000..1a42c14
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <fcntl.h>
+#include <linux/rtnetlink.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+
+#include "if_caif.h"
+#include "caif_rtnl.h"
+
+#define NLMSG_TAIL(nmsg) \
+       ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#define RTNL_MSG_SIZE 1024
+
+struct rtnl_msg {
+       struct nlmsghdr n;
+       struct ifinfomsg i;
+       char data[RTNL_MSG_SIZE];
+};
+
+struct iplink_req {
+       __u32 rtnlmsg_seqnr;
+       void *user_data;
+       caif_rtnl_create_cb_t callback;
+};
+
+static GSList *pending_requests;
+static __u32 rtnl_seqnr;
+static guint rtnl_watch;
+static GIOChannel *rtnl_channel;
+
+static struct iplink_req *find_request(__u32 seq)
+{
+       GSList *list;
+
+       for (list = pending_requests; list; list = list->next) {
+               struct iplink_req *req = list->data;
+
+               if (req->rtnlmsg_seqnr == seq)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static void parse_newlink_param(struct ifinfomsg *msg, int size,
+                                               int *index, char *ifname)
+{
+       struct rtattr *attr;
+
+       for (attr = IFLA_RTA(msg); RTA_OK(attr, size);
+               attr = RTA_NEXT(attr, size)) {
+
+               if (attr->rta_type == IFLA_IFNAME &&
+                               ifname != NULL) {
+
+                       strncpy(ifname, RTA_DATA(attr), IF_NAMESIZE);
+                       ifname[IF_NAMESIZE-1] = '\0';
+                       break;
+               }
+       }
+
+       *index = msg->ifi_index;
+}
+
+static void parse_rtnl_message(const void *buf, size_t len)
+{
+       struct ifinfomsg *msg;
+       struct iplink_req *req;
+       char ifname[IF_NAMESIZE];
+       int index;
+
+       while (len > 0) {
+               const struct nlmsghdr *hdr = buf;
+
+               if (!NLMSG_OK(hdr, len))
+                       break;
+
+               switch (hdr->nlmsg_type) {
+               case RTM_NEWLINK:
+                       req = g_slist_nth_data(pending_requests, 0);
+                       if (req == NULL)
+                               break;
+
+                       msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+                       parse_newlink_param(msg, IFA_PAYLOAD(hdr),
+                                                       &index, ifname);
+
+                       if (req->callback)
+                               req->callback(index, ifname, req->user_data);
+                       break;
+
+               case NLMSG_ERROR:
+                       req = find_request(hdr->nlmsg_seq);
+                       if (req == NULL)
+                               break;
+
+                       DBG("nlmsg error req");
+
+                       if (req->callback)
+                               req->callback(-1, ifname, req->user_data);
+                       break;
+
+               default:
+                       req = NULL;
+                       break;
+               }
+
+               len -= hdr->nlmsg_len;
+               buf += hdr->nlmsg_len;
+
+               if (req) {
+                       pending_requests = g_slist_remove(pending_requests,
+                                                               req);
+                       g_free(req);
+               }
+       }
+}
+
+static int add_attribute(struct nlmsghdr *n, unsigned int maxlen, int type,
+                               const void *data, int datalen)
+{
+       int len = RTA_LENGTH(datalen);
+       struct rtattr *rta;
+
+       if ((NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
+               DBG("attribute to large for message %d %d %d",
+                               n->nlmsg_len, len, maxlen);
+               return -1;
+       }
+
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), data, datalen);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+
+       return 0;
+}
+
+static inline void prep_rtnl_req(struct rtnl_msg *msg, int reqtype, __u32 seqnr)
+{
+       msg->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+       msg->n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL;
+       msg->n.nlmsg_type = reqtype;
+       msg->n.nlmsg_seq = seqnr;
+       msg->i.ifi_family = AF_UNSPEC;
+}
+
+static gboolean netlink_event(GIOChannel *chan,
+                               GIOCondition cond, void *data)
+{
+       unsigned char buf[RTNL_MSG_SIZE];
+       int len, sk;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               rtnl_watch = 0;
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(rtnl_channel);
+
+       len = recv(sk, buf, sizeof(buf), MSG_DONTWAIT);
+       if (len < 0) {
+               if (len == -EAGAIN)
+                       return TRUE;
+
+               rtnl_watch = 0;
+               return FALSE;
+       }
+
+       parse_rtnl_message(buf, len);
+
+       return TRUE;
+}
+
+int caif_rtnl_init(void)
+{
+       struct sockaddr_nl addr;
+       int sk, err;
+
+       sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       if (sk < 0)
+               return sk;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+       addr.nl_groups = RTMGRP_LINK;
+
+       err = bind(sk, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0) {
+               close(sk);
+               return err;
+       }
+
+       rtnl_channel = g_io_channel_unix_new(sk);
+       g_io_channel_set_flags(rtnl_channel, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_channel_set_close_on_unref(rtnl_channel, TRUE);
+
+       rtnl_watch = g_io_add_watch(rtnl_channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                               netlink_event, NULL);
+
+       return 0;
+}
+
+void caif_rtnl_exit(void)
+{
+       GSList *list;
+
+       if (rtnl_watch > 0)
+               g_source_remove(rtnl_watch);
+
+       g_io_channel_unref(rtnl_channel);
+
+       for (list = pending_requests; list; list = list->next) {
+               struct iplink_req *req = list->data;
+               g_free(req);
+       }
+
+       g_slist_free(pending_requests);
+}
+
+int caif_rtnl_create_interface(int type, int connid, int loop,
+                               caif_rtnl_create_cb_t cb, void *user_data)
+{
+       struct iplink_req *req;
+       struct sockaddr_nl addr;
+       struct rtnl_msg msg;
+       struct rtattr *linkinfo;
+       struct rtattr *data_start;
+       int err, sk;
+
+       req = g_try_new0(struct iplink_req, 1);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->user_data = user_data;
+       req->callback = cb;
+       memset(&msg, 0, RTNL_MSG_SIZE);
+
+       req->rtnlmsg_seqnr = ++rtnl_seqnr;
+       prep_rtnl_req(&msg, RTM_NEWLINK, req->rtnlmsg_seqnr);
+
+       linkinfo = NLMSG_TAIL(&msg.n);
+       add_attribute(&msg.n, sizeof(msg), IFLA_LINKINFO,
+                       NULL, 0);
+       add_attribute(&msg.n, sizeof(msg), IFLA_INFO_KIND,
+                       "caif", 4);
+       data_start = NLMSG_TAIL(&msg.n);
+       add_attribute(&msg.n, sizeof(msg), IFLA_INFO_DATA,
+                       NULL, 0);
+
+       switch (type) {
+       case IFLA_CAIF_IPV4_CONNID:
+       case IFLA_CAIF_IPV6_CONNID:
+               add_attribute(&msg.n, sizeof(msg),
+                               type, &connid,
+                               sizeof(connid));
+               break;
+       default:
+               DBG("unsupported linktype");
+               g_free(req);
+               return -EINVAL;
+       }
+
+       if (loop)
+               add_attribute(&msg.n, sizeof(msg),
+                               IFLA_CAIF_LOOPBACK, &loop, sizeof(loop));
+
+       data_start->rta_len = (void *)NLMSG_TAIL(&msg.n) - (void *)data_start;
+       linkinfo->rta_len = (void *)NLMSG_TAIL(&msg.n) - (void *)linkinfo;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+
+       sk = g_io_channel_unix_get_fd(rtnl_channel);
+
+       err = sendto(sk, &msg, msg.n.nlmsg_len, 0,
+                       (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0) {
+               g_free(req);
+               return err;
+       }
+
+       pending_requests = g_slist_append(pending_requests, req);
+
+       return 0;
+}
+
+int caif_rtnl_delete_interface(int index)
+{
+       struct sockaddr_nl addr;
+       struct rtnl_msg msg;
+       int err, sk;
+
+       if (index < 0)
+               return -EINVAL;
+
+       sk = g_io_channel_unix_get_fd(rtnl_channel);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+
+       memset(&msg, 0, sizeof(msg));
+       prep_rtnl_req(&msg, RTM_DELLINK, ++rtnl_seqnr);
+       msg.i.ifi_index = index;
+
+       err = sendto(sk, &msg, msg.n.nlmsg_len, 0,
+                       (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0)
+               return err;
+
+       return 0;
+}
diff --git a/drivers/stemodem/caif_rtnl.h b/drivers/stemodem/caif_rtnl.h
new file mode 100644 (file)
index 0000000..7b37a55
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef void (*caif_rtnl_create_cb_t) (int index, const char *ifname,
+                                                       void *user_data);
+
+extern int caif_rtnl_create_interface(int type, int connid, int loop,
+                               caif_rtnl_create_cb_t cb, void *user_data);
+extern int caif_rtnl_delete_interface(int index);
+
+extern int caif_rtnl_init(void);
+extern void caif_rtnl_exit(void);
diff --git a/drivers/stemodem/caif_socket.h b/drivers/stemodem/caif_socket.h
new file mode 100644 (file)
index 0000000..745d43d
--- /dev/null
@@ -0,0 +1,210 @@
+/* linux/caif_socket.h
+ * CAIF Definitions for CAIF socket and network layer
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author:      Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _LINUX_CAIF_SOCKET_H
+#define _LINUX_CAIF_SOCKET_H
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+#include <linux/socket.h>
+#else
+#include <sys/socket.h>
+#endif
+
+/* Copy definitions from include/linux/socket.h */
+#ifndef AF_CAIF
+#define AF_CAIF        37              /* CAIF Socket Address Family */
+#endif
+#ifndef PF_CAIF
+#define PF_CAIF        AF_CAIF /* CAIF Socket Protocol Family */
+#endif
+#ifndef SOL_CAIF
+#define SOL_CAIF       278             /* CAIF Socket Option Level */
+#endif
+
+/**
+ * enum caif_link_selector -    Physical Link Selection.
+ * @CAIF_LINK_HIGH_BANDW:      Physical interface for high-bandwidth
+ *                             traffic.
+ * @CAIF_LINK_LOW_LATENCY:     Physical interface for low-latency
+ *                             traffic.
+ *
+ * CAIF Link Layers can register their link properties.
+ * This enum is used for choosing between CAIF Link Layers when
+ * setting up CAIF Channels when multiple CAIF Link Layers exists.
+ */
+enum caif_link_selector {
+       CAIF_LINK_HIGH_BANDW,
+       CAIF_LINK_LOW_LATENCY
+};
+
+/**
+ * enum caif_channel_priority - CAIF channel priorities.
+ *
+ * @CAIF_PRIO_MIN:     Min priority for a channel.
+ * @CAIF_PRIO_LOW:     Low-priority channel.
+ * @CAIF_PRIO_NORMAL:  Normal/default priority level.
+ * @CAIF_PRIO_HIGH:    High priority level
+ * @CAIF_PRIO_MAX:     Max priority for channel
+ *
+ * Priority can be set on CAIF Channels in order to
+ * prioritize between traffic on different CAIF Channels.
+ * These priority levels are recommended, but the priority value
+ * is not restricted to the values defined in this enum, any value
+ * between CAIF_PRIO_MIN and CAIF_PRIO_MAX could be used.
+ */
+enum caif_channel_priority {
+       CAIF_PRIO_MIN    = 0x01,
+       CAIF_PRIO_LOW    = 0x04,
+       CAIF_PRIO_NORMAL = 0x0f,
+       CAIF_PRIO_HIGH   = 0x14,
+       CAIF_PRIO_MAX    = 0x1F
+};
+
+/**
+ * enum caif_protocol_type  -  CAIF Channel type.
+ * @CAIFPROTO_AT:              Classic AT channel.
+ * @CAIFPROTO_DATAGRAM:        Datagram channel.
+ * @CAIFPROTO_DATAGRAM_LOOP:   Datagram loopback channel, used for testing.
+ * @CAIFPROTO_UTIL:            Utility (Psock) channel.
+ * @CAIFPROTO_RFM:             Remote File Manager
+ * @CAIFPROTO_DEBUG:           Debug link
+ *
+ * This enum defines the CAIF Channel type to be used. This defines
+ * the service to connect to on the modem.
+ */
+enum caif_protocol_type {
+       CAIFPROTO_AT,
+       CAIFPROTO_DATAGRAM,
+       CAIFPROTO_DATAGRAM_LOOP,
+       CAIFPROTO_UTIL,
+       CAIFPROTO_RFM,
+       CAIFPROTO_DEBUG,
+       _CAIFPROTO_MAX
+};
+#define        CAIFPROTO_MAX _CAIFPROTO_MAX
+
+/**
+ * enum caif_at_type - AT Service Endpoint
+ * @CAIF_ATTYPE_PLAIN:      Connects to a plain vanilla AT channel.
+ */
+enum caif_at_type {
+       CAIF_ATTYPE_PLAIN = 2
+};
+ /**
+ * enum caif_debug_type - Content selection for debug connection
+ * @CAIF_DEBUG_TRACE_INTERACTIVE: Connection will contain
+ *                             both trace and interactive debug.
+ * @CAIF_DEBUG_TRACE:          Connection contains trace only.
+ * @CAIF_DEBUG_INTERACTIVE:    Connection to interactive debug.
+ */
+enum caif_debug_type {
+       CAIF_DEBUG_TRACE_INTERACTIVE = 0,
+       CAIF_DEBUG_TRACE,
+       CAIF_DEBUG_INTERACTIVE,
+};
+
+/**
+ * enum caif_debug_service - Debug Service Endpoint
+ * @CAIF_RADIO_DEBUG_SERVICE:  Debug service on the Radio sub-system
+ * @CAIF_APP_DEBUG_SERVICE:    Debug for the applications sub-system
+ */
+enum caif_debug_service {
+       CAIF_RADIO_DEBUG_SERVICE = 1,
+       CAIF_APP_DEBUG_SERVICE
+};
+
+/**
+ * struct sockaddr_caif - the sockaddr structure for CAIF sockets.
+ * @family:                 Address family number, must be AF_CAIF.
+ * @u:                      Union of address data 'switched' by family.
+ * :
+ * @u.at:                    Applies when family = CAIFPROTO_AT.
+ *
+ * @u.at.type:               Type of AT link to set up (enum caif_at_type).
+ *
+ * @u.util:                  Applies when family = CAIFPROTO_UTIL
+ *
+ * @u.util.service:          Utility service name.
+ *
+ * @u.dgm:                   Applies when family = CAIFPROTO_DATAGRAM
+ *
+ * @u.dgm.connection_id:     Datagram connection id.
+ *
+ * @u.dgm.nsapi:             NSAPI of the PDP-Context.
+ *
+ * @u.rfm:                   Applies when family = CAIFPROTO_RFM
+ *
+ * @u.rfm.connection_id:     Connection ID for RFM.
+ *
+ * @u.rfm.volume:            Volume to mount.
+ *
+ * @u.dbg:                   Applies when family = CAIFPROTO_DEBUG.
+ *
+ * @u.dbg.type:                             Type of debug connection to set up
+ *                           (caif_debug_type).
+ *
+ * @u.dbg.service:           Service sub-system to connect (caif_debug_service
+ * Description:
+ * This structure holds the connect parameters used for setting up a
+ * CAIF Channel. It defines the service to connect to on the modem.
+ */
+struct sockaddr_caif {
+       sa_family_t  family;
+       union {
+               struct {
+                       __u8  type;             /* type: enum caif_at_type */
+               } at;                           /* CAIFPROTO_AT */
+               struct {
+                       char      service[16];
+               } util;                         /* CAIFPROTO_UTIL */
+               union {
+                       __u32 connection_id;
+                       __u8  nsapi;
+               } dgm;                          /* CAIFPROTO_DATAGRAM(_LOOP)*/
+               struct {
+                       __u32 connection_id;
+                       char      volume[16];
+               } rfm;                          /* CAIFPROTO_RFM */
+               struct {
+                       __u8  type;             /* type:enum caif_debug_type */
+                       __u8  service;          /* service:caif_debug_service */
+               } dbg;                          /* CAIFPROTO_DEBUG */
+       } u;
+};
+
+/**
+ * enum caif_socket_opts - CAIF option values for getsockopt and setsockopt.
+ *
+ * @CAIFSO_LINK_SELECT:                Selector used if multiple CAIF Link layers are
+ *                             available. Either a high bandwidth
+ *                             link can be selected (CAIF_LINK_HIGH_BANDW) or
+ *                             or a low latency link (CAIF_LINK_LOW_LATENCY).
+ *                              This option is of type __u32.
+ *                             Alternatively SO_BINDTODEVICE can be used.
+ *
+ * @CAIFSO_REQ_PARAM:          Used to set the request parameters for a
+ *                             utility channel. (maximum 256 bytes). This
+ *                             option must be set before connecting.
+ *
+ * @CAIFSO_RSP_PARAM:          Gets the response parameters for a utility
+ *                             channel. (maximum 256 bytes). This option
+ *                             is valid after a successful connect.
+ *
+ *
+ * This enum defines the CAIF Socket options to be used on a socket
+ * of type PF_CAIF.
+ *
+ */
+enum caif_socket_opts {
+       CAIFSO_LINK_SELECT      = 127,
+       CAIFSO_REQ_PARAM        = 128,
+       CAIFSO_RSP_PARAM        = 129,
+};
+
+#endif /* _LINUX_CAIF_SOCKET_H */
diff --git a/drivers/stemodem/gprs-context.c b/drivers/stemodem/gprs-context.c
new file mode 100644 (file)
index 0000000..18b2bfa
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+#include <ofono/gprs.h>
+
+#include <linux/types.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "stemodem.h"
+#include "caif_socket.h"
+#include "if_caif.h"
+#include "caif_rtnl.h"
+#include "common.h"
+
+#define MAX_DNS 2
+#define IP_ADDR_LEN 20
+
+#define AUTH_BUF_LENGTH (OFONO_GPRS_MAX_USERNAME_LENGTH + \
+                       OFONO_GPRS_MAX_PASSWORD_LENGTH + 128)
+
+static const char *none_prefix[] = { NULL };
+
+struct gprs_context_data {
+       GAtChat *chat;
+       unsigned int active_context;
+       /* Id used by CAIF and EPPSD to identify the CAIF channel*/
+       unsigned int channel_id;
+       /* Linux Interface Id */
+       unsigned int ifindex;
+       /* Linux Interface name */
+       char interface[IF_NAMESIZE];
+       gboolean created;
+};
+
+struct eppsd_response {
+       char *current;
+       char ip_address[IP_ADDR_LEN];
+       char subnet_mask[IP_ADDR_LEN];
+       char mtu[IP_ADDR_LEN];
+       char dns_server1[IP_ADDR_LEN];
+       char dns_server2[IP_ADDR_LEN];
+       char p_cscf_server[IP_ADDR_LEN];
+};
+
+static void start_element_handler(GMarkupParseContext *context,
+               const gchar *element_name, const gchar **attribute_names,
+               const gchar **attribute_values, gpointer user_data,
+               GError **error)
+{
+       struct eppsd_response *rsp = user_data;
+       rsp->current = NULL;
+
+       if (!strcmp(element_name, "ip_address"))
+               rsp->current = rsp->ip_address;
+       else if (!strcmp(element_name, "subnet_mask"))
+               rsp->current = rsp->subnet_mask;
+       else if (!strcmp(element_name, "mtu"))
+               rsp->current = rsp->mtu;
+       else if (!strcmp(element_name, "dns_server") &&
+                                       rsp->dns_server1[0] == '\0')
+               rsp->current = rsp->dns_server1;
+       else if (!strcmp(element_name, "dns_server"))
+               rsp->current = rsp->dns_server2;
+       else if (!strcmp(element_name, "p_cscf_server"))
+               rsp->current = rsp->p_cscf_server;
+}
+
+static void end_element_handler(GMarkupParseContext *context,
+                               const gchar *element_name, gpointer user_data,
+                               GError **error)
+{
+       struct eppsd_response *rsp = user_data;
+       rsp->current = NULL;
+}
+
+static void text_handler(GMarkupParseContext *context,
+                               const gchar *text, gsize text_len,
+                               gpointer user_data, GError **error)
+{
+       struct eppsd_response *rsp = user_data;
+
+       if (rsp->current) {
+               strncpy(rsp->current, text, IP_ADDR_LEN);
+               rsp->current[IP_ADDR_LEN] = '\0';
+       }
+}
+
+static void error_handler(GMarkupParseContext *context,
+                               GError *error, gpointer user_data)
+{
+       DBG("Error parsing xml response from eppsd: %s",
+               error->message);
+}
+
+static GMarkupParser parser = {
+       start_element_handler,
+       end_element_handler,
+       text_handler,
+       NULL,
+       error_handler
+};
+
+static void rtnl_callback(int ifindex, const char *ifname, void *user_data)
+{
+       struct gprs_context_data *gcd = user_data;
+
+       if (ifindex < 0) {
+               gcd->created = FALSE;
+               ofono_error("Failed to create caif interface");
+               return;
+       }
+
+       strncpy(gcd->interface, ifname, sizeof(gcd->interface));
+       gcd->ifindex = ifindex;
+       gcd->created = TRUE;
+}
+
+static void ste_eppsd_down_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       if (!ok) {
+               struct ofono_error error;
+
+               decode_at_error(&error, g_at_result_final_response(result));
+               cb(&error, cbd->data);
+               return;
+       }
+
+       gcd->active_context = 0;
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static void ste_eppsd_up_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtResultIter iter;
+       int i;
+       gsize length;
+       const char *res_string;
+       const char *dns[MAX_DNS + 1];
+       struct eppsd_response rsp;
+       GMarkupParseContext *context;
+
+       if (!ok) {
+               struct ofono_error error;
+
+               gcd->active_context = 0;
+               decode_at_error(&error, g_at_result_final_response(result));
+               cb(&error, cbd->data);
+               return;
+       }
+
+       rsp.current = NULL;
+       context = g_markup_parse_context_new(&parser, 0, &rsp, NULL);
+       memset(&rsp, 0, sizeof(rsp));
+
+       g_at_result_iter_init(&iter, result);
+
+       for (i = 0; i < g_at_result_num_response_lines(result); i++) {
+               g_at_result_iter_next(&iter, NULL);
+               res_string = g_at_result_iter_raw_line(&iter);
+               length = strlen(res_string);
+
+               if (!g_markup_parse_context_parse(context, res_string,
+                                                       length, NULL))
+                       goto error;
+       }
+
+       if (!g_markup_parse_context_end_parse(context, NULL))
+               goto error;
+
+       g_markup_parse_context_free(context);
+
+       dns[0] = rsp.dns_server1;
+       dns[1] = rsp.dns_server2;
+       dns[2] = NULL;
+
+       ofono_gprs_context_set_interface(gc, gcd->interface);
+       ofono_gprs_context_set_ipv4_address(gc, rsp.ip_address, TRUE);
+       ofono_gprs_context_set_ipv4_netmask(gc, rsp.subnet_mask);
+       ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       return;
+
+error:
+       DBG("ste_eppsd_up_cb error");
+
+       if (context)
+               g_markup_parse_context_free(context);
+
+       gcd->active_context = 0;
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ste_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *ncbd;
+       char buf[128];
+
+       if (!ok) {
+               struct ofono_error error;
+
+               gcd->active_context = 0;
+               decode_at_error(&error, g_at_result_final_response(result));
+               cb(&error, cbd->data);
+               return;
+       }
+
+       snprintf(buf, sizeof(buf), "AT*EPPSD=1,%x,%u",
+                       gcd->channel_id, gcd->active_context);
+
+       ncbd = g_memdup(cbd, sizeof(struct cb_data));
+
+       if (g_at_chat_send(gcd->chat, buf, NULL,
+                               ste_eppsd_up_cb, ncbd, g_free) > 0)
+               return;
+
+       g_free(ncbd);
+       gcd->active_context = 0;
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ste_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[AUTH_BUF_LENGTH];
+       int len;
+
+       /* IPv6 support not implemented */
+       if (ctx->proto != OFONO_GPRS_PROTO_IP)
+               goto error;
+
+       gcd->active_context = ctx->cid;
+       cbd->user = gc;
+
+       if (!gcd->created) {
+               DBG("CAIF interface not created (rtnl error?)");
+               goto error;
+       }
+
+       len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len, ",\"%s\"",
+                               ctx->apn);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               ste_cgdcont_cb, cbd, g_free) == 0)
+               goto error;
+
+       /*
+        * Set username and password, this should be done after CGDCONT
+        * or an error can occur.  We don't bother with error checking
+        * here
+        */
+       snprintf(buf, sizeof(buf), "AT*EIAAUW=%d,1,\"%s\",\"%s\"",
+                       ctx->cid, ctx->username, ctx->password);
+
+       g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+
+       return;
+
+error:
+       gcd->active_context = 0;
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int id,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[64];
+
+       cbd->user = gc;
+
+       snprintf(buf, sizeof(buf), "AT*EPPSD=0,%x,%u", gcd->channel_id, id);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               ste_eppsd_down_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       GAtResultIter iter;
+       const char *event;
+       int cid;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CGEV:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+               return;
+
+       if (g_str_has_prefix(event, "NW DEACT") == FALSE)
+               return;
+
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &cid))
+               return;
+
+       if ((unsigned int) cid != gcd->active_context)
+               return;
+
+       ofono_gprs_context_deactivated(gc, gcd->active_context);
+       gcd->active_context = 0;
+}
+
+static int ste_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+       int err;
+
+       gcd = g_new0(struct gprs_context_data, 1);
+       gcd->chat = g_at_chat_clone(chat);
+
+       g_at_chat_register(gcd->chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+       /* Need a unique channel id */
+       gcd->channel_id = (unsigned int)(unsigned long)gc;
+
+       ofono_gprs_context_set_data(gc, gcd);
+
+       err = caif_rtnl_create_interface(IFLA_CAIF_IPV4_CONNID,
+                                       gcd->channel_id, FALSE,
+                                       rtnl_callback, gcd);
+       if (err < 0) {
+               DBG("Failed to create IP interface for CAIF");
+               return err;
+       }
+
+       return 0;
+}
+
+static void ste_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       /*
+        * Removes IP interface for CAIF.
+        */
+       if (!gcd->created)
+               goto out;
+
+       if (caif_rtnl_delete_interface(gcd->ifindex) < 0) {
+               ofono_error("Failed to delete caif interface %s",
+                               gcd->interface);
+               goto out;
+       }
+
+       DBG("removed CAIF interface ch:%d ifname:%s ifindex:%d\n",
+                       gcd->channel_id, gcd->interface, gcd->ifindex);
+
+out:
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "stemodem",
+       .probe                  = ste_gprs_context_probe,
+       .remove                 = ste_gprs_context_remove,
+       .activate_primary       = ste_gprs_activate_primary,
+       .deactivate_primary     = ste_gprs_deactivate_primary,
+};
+
+void ste_gprs_context_init(void)
+{
+       caif_rtnl_init();
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void ste_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+       caif_rtnl_exit();
+}
diff --git a/drivers/stemodem/if_caif.h b/drivers/stemodem/if_caif.h
new file mode 100644 (file)
index 0000000..5e7eed4
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ * Author:     Sjur Brendeland/ sjur.brandeland@stericsson.com
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef IF_CAIF_H_
+#define IF_CAIF_H_
+#include <linux/sockios.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/**
+ * enum ifla_caif - CAIF NetlinkRT parameters.
+ * @IFLA_CAIF_IPV4_CONNID:  Connection ID for IPv4 PDP Context.
+ *                         The type of attribute is NLA_U32.
+ * @IFLA_CAIF_IPV6_CONNID:  Connection ID for IPv6 PDP Context.
+ *                         The type of attribute is NLA_U32.
+ * @IFLA_CAIF_LOOPBACK:            If different from zero, device is doing loopback
+ *                         The type of attribute is NLA_U8.
+ *
+ * When using RT Netlink to create, destroy or configure a CAIF IP interface,
+ * enum ifla_caif is used to specify the configuration attributes.
+ */
+enum ifla_caif {
+       __IFLA_CAIF_UNSPEC,
+       IFLA_CAIF_IPV4_CONNID,
+       IFLA_CAIF_IPV6_CONNID,
+       IFLA_CAIF_LOOPBACK,
+       __IFLA_CAIF_MAX
+};
+#define        IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1)
+
+#endif /*IF_CAIF_H_*/
diff --git a/drivers/stemodem/radio-settings.c b/drivers/stemodem/radio-settings.c
new file mode 100644 (file)
index 0000000..15d09b7
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "stemodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *cfun_prefix[] = { "+CFUN:", NULL };
+
+struct radio_settings_data {
+       GAtChat *chat;
+};
+
+enum ste_radio_mode {
+       STE_RADIO_OFF = 0,
+       STE_RADIO_ON =          1,
+       STE_RADIO_FLIGHT_MODE = 4,
+       STE_RADIO_GSM_ONLY =    5,
+       STE_RADIO_WCDMA_ONLY =  6
+};
+
+static gboolean ste_mode_to_ofono_mode(enum ste_radio_mode stemode,
+                               enum ofono_radio_access_mode *mode)
+{
+       switch (stemode) {
+       case STE_RADIO_ON:
+               *mode = OFONO_RADIO_ACCESS_MODE_ANY;
+               return TRUE;
+       case STE_RADIO_GSM_ONLY:
+               *mode = OFONO_RADIO_ACCESS_MODE_GSM;
+               return TRUE;
+       case STE_RADIO_WCDMA_ONLY:
+               *mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+               return TRUE;
+       case STE_RADIO_OFF:
+       case STE_RADIO_FLIGHT_MODE:
+               break;
+       }
+
+       return FALSE;
+}
+
+static gboolean ofono_mode_to_ste_mode(enum ofono_radio_access_mode mode,
+                               enum ste_radio_mode *stemode)
+{
+       switch (mode) {
+       case OFONO_RADIO_ACCESS_MODE_ANY:
+               *stemode = STE_RADIO_ON;
+               return TRUE;
+       case OFONO_RADIO_ACCESS_MODE_GSM:
+               *stemode = STE_RADIO_GSM_ONLY;
+               return TRUE;
+       case OFONO_RADIO_ACCESS_MODE_UMTS:
+               *stemode = STE_RADIO_WCDMA_ONLY;
+               return TRUE;
+       case OFONO_RADIO_ACCESS_MODE_LTE:
+               break;
+       }
+
+       return FALSE;
+}
+
+static void rat_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+       enum ofono_radio_access_mode mode;
+       struct ofono_error error;
+       GAtResultIter iter;
+       int value;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CFUN:"))
+               goto err;
+
+       if (!g_at_result_iter_next_number(&iter, &value))
+               goto err;
+
+       if (!ste_mode_to_ofono_mode(value, &mode))
+               goto err;
+
+       CALLBACK_WITH_SUCCESS(cb, mode, cbd->data);
+
+       return;
+
+err:
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void ste_query_rat_mode(struct ofono_radio_settings *rs,
+                               ofono_radio_settings_rat_mode_query_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       if (g_at_chat_send(rsd->chat, "AT+CFUN?", cfun_prefix,
+                                       rat_query_cb, cbd, g_free) == 0) {
+               CALLBACK_WITH_FAILURE(cb, -1, data);
+               g_free(cbd);
+       }
+}
+
+static void rat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, cbd->data);
+               return;
+       }
+
+       CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static void ste_set_rat_mode(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode,
+                               ofono_radio_settings_rat_mode_set_cb_t cb,
+                               void *data)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[20];
+       enum ste_radio_mode value;
+
+       if (!ofono_mode_to_ste_mode(mode, &value)) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               g_free(cbd);
+               return;
+       }
+
+       snprintf(buf, sizeof(buf), "AT+CFUN=%u", value);
+
+       if (g_at_chat_send(rsd->chat, buf, none_prefix,
+                                       rat_modify_cb, cbd, g_free) == 0) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               g_free(cbd);
+       }
+}
+
+static gboolean ste_radio_settings_register(gpointer user)
+{
+       struct ofono_radio_settings *rs = user;
+
+       ofono_radio_settings_register(rs);
+
+       return FALSE;
+}
+
+static int ste_radio_settings_probe(struct ofono_radio_settings *rs,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct radio_settings_data *rsd;
+
+       rsd = g_try_new0(struct radio_settings_data, 1);
+       if (rsd == NULL)
+               return -ENOMEM;
+
+       rsd->chat = g_at_chat_clone(chat);
+
+       ofono_radio_settings_set_data(rs, rsd);
+       g_idle_add(ste_radio_settings_register, rs);
+
+       return 0;
+}
+
+static void ste_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+       struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+       ofono_radio_settings_set_data(rs, NULL);
+
+       g_at_chat_unref(rsd->chat);
+       g_free(rsd);
+}
+
+static struct ofono_radio_settings_driver driver = {
+       .name           = "stemodem",
+       .probe          = ste_radio_settings_probe,
+       .remove         = ste_radio_settings_remove,
+       .query_rat_mode = ste_query_rat_mode,
+       .set_rat_mode   = ste_set_rat_mode
+};
+
+void ste_radio_settings_init(void)
+{
+       ofono_radio_settings_driver_register(&driver);
+}
+
+void ste_radio_settings_exit(void)
+{
+       ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/stemodem/stemodem.c b/drivers/stemodem/stemodem.c
new file mode 100644 (file)
index 0000000..6d0b771
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "stemodem.h"
+
+static int stemodem_init(void)
+{
+       ste_voicecall_init();
+       ste_gprs_context_init();
+       ste_radio_settings_init();
+
+       return 0;
+}
+
+static void stemodem_exit(void)
+{
+       ste_voicecall_exit();
+       ste_gprs_context_exit();
+       ste_radio_settings_exit();
+}
+
+OFONO_PLUGIN_DEFINE(stemodem, "STE modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       stemodem_init, stemodem_exit)
diff --git a/drivers/stemodem/stemodem.h b/drivers/stemodem/stemodem.h
new file mode 100644 (file)
index 0000000..6bb22f2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void ste_gprs_context_init(void);
+extern void ste_gprs_context_exit(void);
+
+extern void ste_voicecall_init(void);
+extern void ste_voicecall_exit(void);
+
+extern void ste_radio_settings_init(void);
+extern void ste_radio_settings_exit(void);
diff --git a/drivers/stemodem/voicecall.c b/drivers/stemodem/voicecall.c
new file mode 100644 (file)
index 0000000..1cbf51a
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "common.h"
+
+#include "stemodem.h"
+
+enum call_status_ste {
+       STE_CALL_STATUS_IDLE =          0,
+       STE_CALL_STATUS_CALLING =       1,
+       STE_CALL_STATUS_CONNECTING =    2,
+       STE_CALL_STATUS_ACTIVE =        3,
+       STE_CALL_STATUS_HOLD =          4,
+       STE_CALL_STATUS_WAITING =       5,
+       STE_CALL_STATUS_ALERTING =      6,
+       STE_CALL_STATUS_BUSY =          7,
+       STE_CALL_STATUS_RELEASED =      8,
+};
+
+static const char *none_prefix[] = { NULL };
+
+struct voicecall_data {
+       GSList *calls;
+       unsigned int local_release;
+       GAtChat *chat;
+};
+
+struct release_id_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int id;
+};
+
+struct change_state_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int affected_types;
+};
+
+/* Translate from the ECAV-based STE-status to CLCC based status */
+static int call_status_ste_to_ofono(enum call_status_ste status)
+{
+       switch (status) {
+       case STE_CALL_STATUS_IDLE:
+       case STE_CALL_STATUS_RELEASED:
+               return CALL_STATUS_DISCONNECTED;
+       case STE_CALL_STATUS_CALLING:
+               return CALL_STATUS_DIALING;
+       case STE_CALL_STATUS_CONNECTING:
+               return CALL_STATUS_ALERTING;
+       case STE_CALL_STATUS_ACTIVE:
+               return CALL_STATUS_ACTIVE;
+       case STE_CALL_STATUS_HOLD:
+               return CALL_STATUS_HELD;
+       case STE_CALL_STATUS_WAITING:
+               return CALL_STATUS_WAITING;
+       case STE_CALL_STATUS_ALERTING:
+               return CALL_STATUS_INCOMING;
+       case STE_CALL_STATUS_BUSY:
+               return CALL_STATUS_DISCONNECTED;
+       }
+
+       return CALL_STATUS_DISCONNECTED;
+}
+
+static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
+                                       int direction, int status,
+                                       const char *num, int num_type, int clip)
+{
+       struct voicecall_data *d = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* Generate a call structure for the waiting call */
+       call = g_try_new(struct ofono_call, 1);
+       if (call == NULL)
+               return NULL;
+
+       ofono_call_init(call);
+
+       call->type = type;
+       call->direction = direction;
+       call->status = status;
+
+       if (clip != CLIP_VALIDITY_NOT_AVAILABLE) {
+               strncpy(call->phone_number.number, num,
+                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = num_type;
+       }
+
+       call->clip_validity = clip;
+
+       d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
+
+       return call;
+}
+
+static void ste_generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct change_state_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok && req->affected_types) {
+               GSList *l;
+               struct ofono_call *call;
+
+               for (l = vd->calls; l; l = l->next) {
+                       call = l->data;
+
+                       if (req->affected_types & (1 << call->status))
+                               vd->local_release |= (1 << call->id);
+               }
+       }
+
+       req->cb(&error, req->data);
+}
+
+static void release_id_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct release_id_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok)
+               vd->local_release = 1 << req->id;
+
+       req->cb(&error, req->data);
+}
+
+static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_error error;
+       ofono_voicecall_cb_t cb = cbd->cb;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void ste_dial(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+                       void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[256];
+
+       cbd->user = vc;
+
+       if (ph->type == 145)
+               snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
+       else
+               snprintf(buf, sizeof(buf), "ATD%s", ph->number);
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_DEFAULT:
+               break;
+       case OFONO_CLIR_OPTION_INVOCATION:
+               strcat(buf, "I");
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               strcat(buf, "i");
+               break;
+       }
+
+       strcat(buf, ";");
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               atd_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_template(const char *cmd, struct ofono_voicecall *vc,
+                       GAtResultFunc result_cb, unsigned int affected_types,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->affected_types = affected_types;
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               result_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_answer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       ste_template("ATA", vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_hangup(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int active_dial_alert_or_incoming =
+                       (1 << CALL_STATUS_ACTIVE) |
+                       (1 << CALL_STATUS_DIALING) |
+                       (1 << CALL_STATUS_ALERTING) |
+                       (1 << CALL_STATUS_INCOMING);
+
+       ste_template("AT+CHUP", vc, ste_generic_cb,
+                       active_dial_alert_or_incoming, cb, data);
+}
+
+static void ste_hold_all_active(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       ste_template("AT+CHLD=2", vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_release_all_held(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int held = 1 << CALL_STATUS_HELD;
+
+       ste_template("AT+CHLD=0", vc, ste_generic_cb, held, cb, data);
+}
+
+static void ste_set_udub(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int incoming_or_waiting =
+                       (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
+
+       ste_template("AT+CHLD=0", vc, ste_generic_cb, incoming_or_waiting,
+                       cb, data);
+}
+
+static void ste_release_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int active = 1 << CALL_STATUS_ACTIVE;
+
+       ste_template("AT+CHLD=1", vc, ste_generic_cb, active, cb, data);
+}
+
+static void ste_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct release_id_req *req = g_try_new0(struct release_id_req, 1);
+       char buf[32];
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->id = id;
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id);
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               release_id_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_private_chat(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id);
+       ste_template(buf, vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_create_multiparty(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       ste_template("AT+CHLD=3", vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_transfer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Held & Active */
+       unsigned int transfer = 0x1 | 0x2;
+
+       /* Transfer can puts held & active calls together and disconnects
+        * from both.  However, some networks support transferring of
+        * dialing/ringing calls as well.
+        */
+       transfer |= 0x4 | 0x8;
+
+       ste_template("AT+CHLD=4", vc, ste_generic_cb, transfer, cb, data);
+}
+
+static void ste_deflect(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[128];
+       unsigned int incoming_or_waiting =
+               (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
+
+       snprintf(buf, sizeof(buf), "AT+CTFR=\"%s\",%d", ph->number, ph->type);
+       ste_template(buf, vc, ste_generic_cb, incoming_or_waiting, cb, data);
+}
+
+static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void ste_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       int s;
+       char *buf;
+
+       /* strlen("AT+VTS=) = 7 + NULL */
+       buf = g_try_new(char, strlen(dtmf) + 8);
+       if (buf == NULL)
+               goto error;
+
+       sprintf(buf, "AT+VTS=%s", dtmf);
+
+       s = g_at_chat_send(vd->chat, buf, none_prefix,
+                               vts_cb, cbd, g_free);
+
+       g_free(buf);
+
+       if (s > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ecav_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int id;
+       int status;
+       int call_type;
+       int num_type;
+       struct ofono_call *new_call;
+       struct ofono_call *existing_call = NULL;
+       GSList *l;
+
+       /* Parse ECAV */
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "*ECAV:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &id))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &status))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &call_type))
+               return;
+
+       if (call_type != BEARER_CLASS_VOICE)
+               return;
+
+       /* Skip process id and exit cause */
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+
+       status = call_status_ste_to_ofono(status);
+
+       if (status == CALL_STATUS_DIALING ||
+                       status == CALL_STATUS_WAITING ||
+                       status == CALL_STATUS_INCOMING) {
+               /*
+                * If caller uses hidden id, the number and
+                * number type might not be present. Don't
+                * look for type if number is not present.
+                */
+               if (!g_at_result_iter_next_string(&iter, &num)) {
+                       num = "";
+                       num_type = 128;
+               } else if (!g_at_result_iter_next_number(&iter, &num_type))
+                       return;
+       }
+
+       /*
+        * Handle the call according to the status.
+        * If it doesn't exists we make a new one
+        */
+       l = g_slist_find_custom(vd->calls, GUINT_TO_POINTER(id),
+                               at_util_call_compare_by_id);
+
+       if (l)
+               existing_call = l->data;
+
+       if (l == NULL && status != CALL_STATUS_DIALING &&
+                               status != CALL_STATUS_WAITING &&
+                               status != CALL_STATUS_INCOMING) {
+               ofono_error("ECAV notification for unknown call."
+                               " id: %d, status: %d", id, status);
+               return;
+       }
+
+       switch (status) {
+       case CALL_STATUS_DISCONNECTED: {
+               enum ofono_disconnect_reason reason;
+
+               existing_call->status = status;
+
+               if (vd->local_release & (1 << existing_call->id))
+                       reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+               else
+                       reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+
+               ofono_voicecall_disconnected(vc, existing_call->id,
+                                               reason, NULL);
+
+               vd->local_release &= ~(1 << existing_call->id);
+               vd->calls = g_slist_remove(vd->calls, l->data);
+               g_free(existing_call);
+               break;
+       }
+
+       case CALL_STATUS_DIALING:
+       case CALL_STATUS_WAITING:
+       case CALL_STATUS_INCOMING: {
+               int clip_validity;
+               int direction;
+
+               if (status == CALL_STATUS_DIALING)
+                       direction = CALL_DIRECTION_MOBILE_ORIGINATED;
+               else
+                       direction = CALL_DIRECTION_MOBILE_TERMINATED;
+
+               if (strlen(num) > 0)
+                       clip_validity = CLIP_VALIDITY_VALID;
+               else
+                       clip_validity = CLIP_VALIDITY_NOT_AVAILABLE;
+
+               new_call = create_call(vc, call_type, direction, status,
+                                       num, num_type, clip_validity);
+               if (new_call == NULL) {
+                       ofono_error("Unable to malloc. "
+                                       "Call management is fubar");
+                       return;
+               }
+
+               new_call->id = id;
+               ofono_voicecall_notify(vc, new_call);
+               break;
+       }
+
+       case CALL_STATUS_ALERTING:
+       case CALL_STATUS_ACTIVE:
+       case CALL_STATUS_HELD:
+               existing_call->status = status;
+               ofono_voicecall_notify(vc, existing_call);
+               break;
+       }
+}
+
+static void ste_voicecall_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       if (!ok) {
+               ofono_error("*ECAV not enabled. "
+                               "Do not have proper call handling");
+               ofono_voicecall_remove(vc);
+               return;
+       }
+
+       g_at_chat_register(vd->chat, "*ECAV:", ecav_notify, FALSE, vc, NULL);
+       ofono_voicecall_register(vc);
+}
+
+static int ste_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_try_new0(struct voicecall_data, 1);
+       if (vd == NULL)
+               return -ENOMEM;
+
+       vd->chat = g_at_chat_clone(chat);
+
+       ofono_voicecall_set_data(vc, vd);
+
+       g_at_chat_send(vd->chat, "AT*ECAM=2", none_prefix,
+                       ste_voicecall_initialized, vc, NULL);
+
+       return 0;
+}
+
+static void ste_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+       g_slist_free(vd->calls);
+
+       ofono_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "stemodem",
+       .probe                  = ste_voicecall_probe,
+       .remove                 = ste_voicecall_remove,
+       .dial                   = ste_dial,
+       .answer                 = ste_answer,
+       .hangup_active          = ste_hangup,
+       .hold_all_active        = ste_hold_all_active,
+       .release_all_held       = ste_release_all_held,
+       .set_udub               = ste_set_udub,
+       .release_all_active     = ste_release_all_active,
+       .release_specific       = ste_release_specific,
+       .private_chat           = ste_private_chat,
+       .create_multiparty      = ste_create_multiparty,
+       .transfer               = ste_transfer,
+       .deflect                = ste_deflect,
+       .swap_without_accept    = NULL,
+       .send_tones             = ste_send_dtmf
+};
+
+void ste_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void ste_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/examples/emulator.c b/examples/emulator.c
new file mode 100644 (file)
index 0000000..5c92bd6
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/emulator.h>
+
+#include "ofono.h"
+
+#define DUN_PORT 12346
+#define HFP_PORT 12347
+
+static unsigned int modemwatch_id;
+guint server_watch;
+static GList *modems;
+
+static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user)
+{
+       struct sockaddr saddr;
+       unsigned int len = sizeof(saddr);
+       int fd;
+       struct ofono_emulator *em;
+       struct ofono_modem *modem;
+
+       if (cond != G_IO_IN)
+               return FALSE;
+
+       fd = accept(g_io_channel_unix_get_fd(chan), &saddr, &len);
+       if (fd == -1)
+               return FALSE;
+
+       /* Pick the first powered modem */
+       modem = modems->data;
+       DBG("Picked modem %p for emulator", modem);
+
+       em = ofono_emulator_create(modem, GPOINTER_TO_INT(user));
+       if (em == NULL)
+               close(fd);
+       else
+               ofono_emulator_register(em, fd);
+
+       return TRUE;
+}
+
+static gboolean create_tcp(short port, enum ofono_emulator_type type)
+{
+       struct sockaddr_in addr;
+       int sk;
+       int reuseaddr = 1;
+       GIOChannel *server;
+
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0)
+               return FALSE;
+
+       memset(&addr, 0, sizeof(addr));
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       addr.sin_port = htons(port);
+
+       setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(struct sockaddr)) < 0)
+               goto err;
+
+       if (listen(sk, 1) < 0)
+               goto err;
+
+       server = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(server, TRUE);
+
+       server_watch = g_io_add_watch_full(server, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               on_socket_connected, GINT_TO_POINTER(type),
+                               NULL);
+
+       g_io_channel_unref(server);
+
+       DBG("Created server_watch: %u", server_watch);
+
+       return TRUE;
+
+err:
+       close(sk);
+       return FALSE;
+}
+
+static void powered_watch(struct ofono_modem *modem, gboolean powered,
+                               void *user)
+{
+       if (powered == FALSE) {
+               DBG("Removing modem %p from the list", modem);
+               modems = g_list_remove(modems, modem);
+
+               if (modems == NULL && server_watch > 0) {
+                       DBG("Removing server watch: %u", server_watch);
+                       g_source_remove(server_watch);
+                       server_watch = 0;
+               }
+       } else {
+               DBG("Adding modem %p to the list", modem);
+               modems = g_list_append(modems, modem);
+
+               if (modems->next == NULL) {
+                       create_tcp(DUN_PORT, OFONO_EMULATOR_TYPE_DUN);
+                       create_tcp(HFP_PORT, OFONO_EMULATOR_TYPE_HFP);
+               }
+       }
+}
+
+static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
+{
+       DBG("modem: %p, added: %d", modem, added);
+
+       if (added == FALSE) {
+               DBG("Removing modem %p from the list", modem);
+               modems = g_list_remove(modems, modem);
+               return;
+       }
+
+       if (ofono_modem_get_powered(modem) == TRUE) {
+               DBG("Adding modem %p to the list", modem);
+               modems = g_list_append(modems, modem);
+
+               if (modems->next == NULL) {
+                       create_tcp(DUN_PORT, OFONO_EMULATOR_TYPE_DUN);
+                       create_tcp(HFP_PORT, OFONO_EMULATOR_TYPE_HFP);
+               }
+       }
+
+       __ofono_modem_add_powered_watch(modem, powered_watch, NULL, NULL);
+}
+
+static void call_modemwatch(struct ofono_modem *modem, void *user)
+{
+       modem_watch(modem, TRUE, user);
+}
+
+static int example_emulator_init(void)
+{
+       DBG("");
+
+       modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
+
+       __ofono_modem_foreach(call_modemwatch, NULL);
+
+       return 0;
+}
+
+static void example_emulator_exit(void)
+{
+       DBG("");
+
+       __ofono_modemwatch_remove(modemwatch_id);
+
+       g_list_free(modems);
+
+       if (server_watch)
+               g_source_remove(server_watch);
+}
+
+OFONO_PLUGIN_DEFINE(example_emulator, "Example AT Modem Emulator Plugin",
+                       VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       example_emulator_init, example_emulator_exit)
diff --git a/examples/history.c b/examples/history.c
new file mode 100644 (file)
index 0000000..f976783
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/history.h>
+#include <ofono/types.h>
+
+#include "common.h"
+
+static int example_history_probe(struct ofono_history_context *context)
+{
+       ofono_debug("Example History Probe for modem: %p", context->modem);
+       return 0;
+}
+
+static void example_history_remove(struct ofono_history_context *context)
+{
+       ofono_debug("Example History Remove for modem: %p", context->modem);
+}
+
+static void example_history_call_ended(struct ofono_history_context *context,
+                                       const struct ofono_call *call,
+                                       time_t start, time_t end)
+{
+       const char *from = "Unknown";
+       char buf[128];
+
+       ofono_debug("Call Ended on modem: %p", context->modem);
+
+       if (call->type != 0)
+               return;
+
+       ofono_debug("Voice Call, %s",
+                       call->direction ? "Incoming" : "Outgoing");
+
+       if (call->clip_validity == 0)
+               from = phone_number_to_string(&call->phone_number);
+
+       if (call->direction == 0)
+               ofono_debug("To: %s", from);
+       else
+               ofono_debug("From: %s", from);
+
+       if (call->cnap_validity == 0)
+               ofono_debug("Name from Network: %s\n", call->name);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&start));
+       buf[127] = '\0';
+       ofono_debug("StartTime: %s", buf);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&end));
+       buf[127] = '\0';
+       ofono_debug("EndTime: %s", buf);
+}
+
+static void example_history_call_missed(struct ofono_history_context *context,
+                                       const struct ofono_call *call,
+                                       time_t when)
+{
+       const char *from = "Unknown";
+       char buf[128];
+
+       ofono_debug("Call Missed on modem: %p", context->modem);
+
+       if (call->type != 0)
+               return;
+
+       ofono_debug("Voice Call, %s",
+                       call->direction ? "Incoming" : "Outgoing");
+
+       if (call->clip_validity == 0)
+               from = phone_number_to_string(&call->phone_number);
+
+       ofono_debug("From: %s", from);
+
+       if (call->cnap_validity == 0)
+               ofono_debug("Name from Network: %s\n", call->name);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&when));
+       buf[127] = '\0';
+       ofono_debug("When: %s", buf);
+}
+
+static void example_history_sms_received(struct ofono_history_context *context,
+                                               const struct ofono_uuid *uuid,
+                                               const char *from,
+                                               const struct tm *remote,
+                                               const struct tm *local,
+                                               const char *text)
+{
+       char buf[128];
+
+       ofono_debug("Incoming SMS on modem: %p", context->modem);
+       ofono_debug("InternalMessageId: %s", ofono_uuid_to_str(uuid));
+       ofono_debug("From: %s", from);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", local);
+       buf[127] = '\0';
+       ofono_debug("Local Sent Time: %s", buf);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", remote);
+       buf[127] = '\0';
+       ofono_debug("Remote Sent Time: %s", buf);
+
+       ofono_debug("Text: %s", text);
+}
+
+static void example_history_sms_send_pending(struct ofono_history_context *context,
+                                               const struct ofono_uuid *uuid,
+                                               const char *to, time_t when,
+                                               const char *text)
+{
+       char buf[128];
+
+       ofono_debug("Sending SMS on modem: %p", context->modem);
+       ofono_debug("InternalMessageId: %s", ofono_uuid_to_str(uuid));
+       ofono_debug("To: %s:", to);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&when));
+       buf[127] = '\0';
+       ofono_debug("Local Time: %s", buf);
+       ofono_debug("Text: %s", text);
+}
+
+static void example_history_sms_send_status(
+                                       struct ofono_history_context *context,
+                                       const struct ofono_uuid *uuid,
+                                       time_t when,
+                                       enum ofono_history_sms_status s)
+{
+       char buf[128];
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&when));
+       buf[127] = '\0';
+
+       switch (s) {
+       case OFONO_HISTORY_SMS_STATUS_PENDING:
+               break;
+       case OFONO_HISTORY_SMS_STATUS_SUBMITTED:
+               ofono_debug("SMS %s submitted successfully",
+                                       ofono_uuid_to_str(uuid));
+               ofono_debug("Submission Time: %s", buf);
+               break;
+       case OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED:
+               ofono_debug("Sending SMS %s failed", ofono_uuid_to_str(uuid));
+               ofono_debug("Failure Time: %s", buf);
+               break;
+       case OFONO_HISTORY_SMS_STATUS_SUBMIT_CANCELLED:
+               ofono_debug("Submission of SMS %s was canceled",
+                                       ofono_uuid_to_str(uuid));
+               ofono_debug("Cancel time: %s", buf);
+               break;
+       case OFONO_HISTORY_SMS_STATUS_DELIVERED:
+               ofono_debug("SMS delivered, msg_id: %s, time: %s",
+                                       ofono_uuid_to_str(uuid), buf);
+               break;
+       case OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED:
+               ofono_debug("SMS undeliverable, msg_id: %s, time: %s",
+                                       ofono_uuid_to_str(uuid), buf);
+               break;
+       default:
+               break;
+       }
+}
+
+static struct ofono_history_driver example_driver = {
+       .name = "Example Call History",
+       .probe = example_history_probe,
+       .remove = example_history_remove,
+       .call_ended = example_history_call_ended,
+       .call_missed = example_history_call_missed,
+       .sms_received = example_history_sms_received,
+       .sms_send_pending = example_history_sms_send_pending,
+       .sms_send_status = example_history_sms_send_status,
+};
+
+static int example_history_init(void)
+{
+       return ofono_history_driver_register(&example_driver);
+}
+
+static void example_history_exit(void)
+{
+       ofono_history_driver_unregister(&example_driver);
+}
+
+OFONO_PLUGIN_DEFINE(example_history, "Example Call History Plugin",
+                       VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       example_history_init, example_history_exit)
diff --git a/examples/nettime.c b/examples/nettime.c
new file mode 100644 (file)
index 0000000..f586f07
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/nettime.h>
+#include <ofono/types.h>
+
+#include "common.h"
+
+static int example_nettime_probe(struct ofono_nettime_context *context)
+{
+       ofono_debug("Example Network Time Probe for modem: %p",
+                       context->modem);
+       return 0;
+}
+
+static void example_nettime_remove(struct ofono_nettime_context *context)
+{
+       ofono_debug("Example Network Time Remove for modem: %p",
+                       context->modem);
+}
+
+static void example_nettime_info_received(struct ofono_nettime_context *context,
+                                               struct ofono_network_time *info)
+{
+       if (info == NULL)
+               return;
+
+       ofono_debug("Received a network time notification on modem: %p",
+                       context->modem);
+       ofono_debug("Time: %04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d (DST=%d)",
+                       info->year, info->mon, info->mday, info->hour,
+                       info->min, info->sec, info->utcoff > 0 ? '+' : '-',
+                       info->utcoff / 3600, (info->utcoff % 3600) / 60,
+                       info->dst);
+}
+
+static struct ofono_nettime_driver example_driver = {
+       .name           = "Example Network Time",
+       .probe          = example_nettime_probe,
+       .remove         = example_nettime_remove,
+       .info_received  = example_nettime_info_received,
+};
+
+static int example_nettime_init(void)
+{
+       return ofono_nettime_driver_register(&example_driver);
+}
+
+static void example_nettime_exit(void)
+{
+       ofono_nettime_driver_unregister(&example_driver);
+}
+
+OFONO_PLUGIN_DEFINE(example_nettime, "Example Network Time Plugin",
+                       VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       example_nettime_init, example_nettime_exit)
diff --git a/examples/private-network.c b/examples/private-network.c
new file mode 100644 (file)
index 0000000..5d5705d
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+
+#include <ofono/modem.h>
+#include <ofono/types.h>
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/private-network.h>
+
+#define SERVER_ADDRESS         "192.168.1.1"
+#define DNS_SERVER_1           "10.10.10.10"
+#define DNS_SERVER_2           "10.10.10.11"
+#define PEER_ADDRESS_PREFIX    "192.168.1."
+
+static int next_peer = 2;
+
+struct req_data {
+       ofono_private_network_cb_t cb;
+       void *userdata;
+};
+
+static gboolean request_cb(gpointer data)
+{
+       struct req_data *rd = data;
+       struct ofono_private_network_settings pns;
+       struct ifreq ifr;
+       int fd, err;
+       char ip[16];
+
+       fd = open("/dev/net/tun", O_RDWR);
+       if (fd < 0)
+               goto error;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+       strcpy(ifr.ifr_name, "ppp%d");
+
+       err = ioctl(fd, TUNSETIFF, (void *) &ifr);
+       if (err < 0)
+               goto error;
+
+       sprintf(ip, "%s%d", PEER_ADDRESS_PREFIX, next_peer++);
+
+       pns.fd = fd;
+       pns.server_ip = SERVER_ADDRESS;
+       pns.peer_ip = ip;
+       pns.primary_dns = DNS_SERVER_1;
+       pns.secondary_dns = DNS_SERVER_2;
+
+       rd->cb(&pns, rd->userdata);
+
+       return FALSE;
+
+error:
+       if (fd >= 0)
+               close(fd);
+
+       rd->cb(NULL, rd->userdata);
+
+       return FALSE;
+}
+
+static int example_request(ofono_private_network_cb_t cb, void *data)
+{
+       struct req_data *rd = g_new0(struct req_data, 1);
+
+       DBG("");
+
+       rd->cb = cb;
+       rd->userdata = data;
+
+       return g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 2, request_cb,
+                                               rd, (GDestroyNotify) g_free);
+}
+
+static void example_release(int id)
+{
+       DBG("");
+
+       g_source_remove(id);
+}
+
+static struct ofono_private_network_driver example_driver = {
+       .name           = "Example Private Network Driver",
+       .request        = example_request,
+       .release        = example_release,
+};
+
+static int example_private_network_init(void)
+{
+       return ofono_private_network_driver_register(&example_driver);
+}
+
+static void example_private_network_exit(void)
+{
+       ofono_private_network_driver_unregister(&example_driver);
+}
+
+OFONO_PLUGIN_DEFINE(example_private_network, "Example Private Network Plugin",
+                       VERSION, OFONO_PLUGIN_PRIORITY_LOW,
+                       example_private_network_init,
+                       example_private_network_exit)
diff --git a/examples/provision.c b/examples/provision.c
new file mode 100644 (file)
index 0000000..3f9d124
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+
+#include <errno.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+
+#include <ofono/modem.h>
+#include <ofono/gprs-provision.h>
+#include <ofono/types.h>
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+
+static int example_provision_get_settings(const char *mcc, const char *mnc,
+                               const char *spn,
+                               struct ofono_gprs_provision_data **settings,
+                               int *count)
+{
+       ofono_debug("Provisioning...");
+       *count = 0;
+       *settings = NULL;
+
+       ofono_debug("Finding settings for MCC %s, MNC %s, SPN '%s'",
+                       mcc, mnc, spn);
+
+       if (strcmp(mcc, "246") != 0 || strcmp(mnc, "81") != 0 ||
+                                               g_strcmp0(spn, "oFono") != 0)
+               return -ENOENT;
+
+       ofono_debug("Creating example settings for phonesim");
+
+       *settings = g_try_new0(struct ofono_gprs_provision_data, 2);
+       if (*settings == NULL)
+               return -ENOMEM;
+
+       *count = 2;
+
+       /* Internet context settings */
+       (*settings)[0].proto = OFONO_GPRS_PROTO_IP;
+       (*settings)[0].type = OFONO_GPRS_CONTEXT_TYPE_INTERNET;
+       (*settings)[0].name = g_strdup("Phonesim Internet");
+       (*settings)[0].apn = g_strdup("internetapn");
+
+       /* MMS context settings */
+       (*settings)[1].proto = OFONO_GPRS_PROTO_IP;
+       (*settings)[1].type = OFONO_GPRS_CONTEXT_TYPE_MMS;
+       (*settings)[1].name = g_strdup("Phonesim MMS");
+       (*settings)[1].apn = g_strdup("mmsapn");
+       (*settings)[1].username = g_strdup("mmsuser");
+       (*settings)[1].password = g_strdup("mmspass");
+       (*settings)[1].message_proxy = g_strdup("10.11.12.13:8080");
+       (*settings)[1].message_center = g_strdup("http://mms.example.com:8000");
+
+       return 0;
+}
+
+static struct ofono_gprs_provision_driver example_driver = {
+       .name           = "Example GPRS context provisioning",
+       .priority       = OFONO_PLUGIN_PRIORITY_LOW,
+       .get_settings   = example_provision_get_settings,
+};
+
+static int example_provision_init(void)
+{
+       return ofono_gprs_provision_driver_register(&example_driver);
+}
+
+static void example_provision_exit(void)
+{
+       ofono_gprs_provision_driver_unregister(&example_driver);
+}
+
+OFONO_PLUGIN_DEFINE(example_provision, "Example Provisioning Plugin",
+                       VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       example_provision_init,
+                       example_provision_exit)
diff --git a/gatchat/crc-ccitt.c b/gatchat/crc-ccitt.c
new file mode 100644 (file)
index 0000000..654695d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "crc-ccitt.h"
+
+const guint16 crc_ccitt_table[256] = {
+       0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+       0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+       0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+       0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+       0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+       0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+       0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+       0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+       0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+       0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+       0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+       0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+       0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+       0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+       0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+       0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+       0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+       0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+       0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+       0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+       0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+       0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+       0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+       0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+       0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+       0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+       0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+       0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+       0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+       0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+       0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+       0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
diff --git a/gatchat/crc-ccitt.h b/gatchat/crc-ccitt.h
new file mode 100644 (file)
index 0000000..56eaffd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+extern guint16 const crc_ccitt_table[256];
+
+static inline guint16 crc_ccitt_byte(guint16 crc, const guint8 c)
+{
+       return (crc >> 8) ^ crc_ccitt_table[(crc ^ c) & 0xff];
+}
diff --git a/gatchat/gat.h b/gatchat/gat.h
new file mode 100644 (file)
index 0000000..3a3791c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GAT_H
+#define __GAT_H
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*GAtDisconnectFunc)(gpointer user_data);
+typedef void (*GAtReceiveFunc)(const unsigned char *data, gsize size,
+                                                       gpointer user_data);
+typedef void (*GAtDebugFunc)(const char *str, gpointer user_data);
+typedef void (*GAtSuspendFunc)(gpointer user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GAT_H */
diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c
new file mode 100644 (file)
index 0000000..7a0ef35
--- /dev/null
@@ -0,0 +1,1563 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+#include "gatchat.h"
+#include "gatio.h"
+
+/* #define WRITE_SCHEDULER_DEBUG 1 */
+
+#define COMMAND_FLAG_EXPECT_PDU                        0x1
+#define COMMAND_FLAG_EXPECT_SHORT_PROMPT       0x2
+
+struct at_chat;
+static void chat_wakeup_writer(struct at_chat *chat);
+
+static const char *none_prefix[] = { NULL };
+
+struct at_command {
+       char *cmd;
+       char **prefixes;
+       guint flags;
+       guint id;
+       guint gid;
+       GAtResultFunc callback;
+       GAtNotifyFunc listing;
+       gpointer user_data;
+       GDestroyNotify notify;
+};
+
+struct at_notify_node {
+       guint id;
+       guint gid;
+       GAtNotifyFunc callback;
+       gpointer user_data;
+       GDestroyNotify notify;
+       gboolean destroyed;
+};
+
+typedef gboolean (*node_remove_func)(struct at_notify_node *node,
+                                       gpointer user_data);
+
+struct at_notify {
+       GSList *nodes;
+       gboolean pdu;
+};
+
+struct at_chat {
+       gint ref_count;                         /* Ref count */
+       guint next_cmd_id;                      /* Next command id */
+       guint next_notify_id;                   /* Next notify id */
+       guint next_gid;                         /* Next group id */
+       GAtIO *io;                              /* AT IO */
+       GQueue *command_queue;                  /* Command queue */
+       guint cmd_bytes_written;                /* bytes written from cmd */
+       GHashTable *notify_list;                /* List of notification reg */
+       GAtDisconnectFunc user_disconnect;      /* user disconnect func */
+       gpointer user_disconnect_data;          /* user disconnect data */
+       guint read_so_far;                      /* Number of bytes processed */
+       gboolean suspended;                     /* Are we suspended? */
+       GAtDebugFunc debugf;                    /* debugging output function */
+       gpointer debug_data;                    /* Data to pass to debug func */
+       char *pdu_notify;                       /* Unsolicited Resp w/ PDU */
+       GSList *response_lines;                 /* char * lines of the response */
+       char *wakeup;                           /* command sent to wakeup modem */
+       gint timeout_source;
+       gdouble inactivity_time;                /* Period of inactivity */
+       guint wakeup_timeout;                   /* How long to wait for resp */
+       GTimer *wakeup_timer;                   /* Keep track of elapsed time */
+       GAtSyntax *syntax;
+       gboolean destroyed;                     /* Re-entrancy guard */
+       gboolean in_read_handler;               /* Re-entrancy guard */
+       gboolean in_notify;
+       GSList *terminator_list;                /* Non-standard terminator */
+       guint16 terminator_blacklist;           /* Blacklisted terinators */
+};
+
+struct _GAtChat {
+       gint ref_count;
+       struct at_chat *parent;
+       guint group;
+       GAtChat *slave;
+};
+
+struct terminator_info {
+       char *terminator;
+       int len;
+       gboolean success;
+};
+
+static gboolean node_is_destroyed(struct at_notify_node *node, gpointer user)
+{
+       return node->destroyed;
+}
+
+static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct at_notify_node *node = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       if (node->id < id)
+               return -1;
+
+       if (node->id > id)
+               return 1;
+
+       return 0;
+}
+
+static void at_notify_node_destroy(gpointer data, gpointer user_data)
+{
+       struct at_notify_node *node = data;
+
+       if (node->notify)
+               node->notify(node->user_data);
+
+       g_free(node);
+}
+
+static void at_notify_destroy(gpointer user_data)
+{
+       struct at_notify *notify = user_data;
+
+       g_slist_foreach(notify->nodes, at_notify_node_destroy, NULL);
+       g_slist_free(notify->nodes);
+       g_free(notify);
+}
+
+static gint at_command_compare_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct at_command *command = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       if (command->id < id)
+               return -1;
+
+       if (command->id > id)
+               return 1;
+
+       return 0;
+}
+
+static gboolean at_chat_unregister_all(struct at_chat *chat,
+                                       gboolean mark_only,
+                                       node_remove_func func,
+                                       gpointer userdata)
+{
+       GHashTableIter iter;
+       struct at_notify *notify;
+       struct at_notify_node *node;
+       gpointer key, value;
+       GSList *p;
+       GSList *c;
+       GSList *t;
+
+       if (chat->notify_list == NULL)
+               return FALSE;
+
+       g_hash_table_iter_init(&iter, chat->notify_list);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               notify = value;
+
+               p = NULL;
+               c = notify->nodes;
+
+               while (c) {
+                       node = c->data;
+
+                       if (func(node, userdata) != TRUE) {
+                               p = c;
+                               c = c->next;
+                               continue;
+                       }
+
+                       if (mark_only) {
+                               node->destroyed = TRUE;
+                               p = c;
+                               c = c->next;
+                               continue;
+                       }
+
+                       if (p)
+                               p->next = c->next;
+                       else
+                               notify->nodes = c->next;
+
+                       at_notify_node_destroy(node, NULL);
+
+                       t = c;
+                       c = c->next;
+                       g_slist_free_1(t);
+               }
+
+               if (notify->nodes == NULL)
+                       g_hash_table_iter_remove(&iter);
+       }
+
+       return TRUE;
+}
+
+static struct at_command *at_command_create(guint gid, const char *cmd,
+                                               const char **prefix_list,
+                                               guint flags,
+                                               GAtNotifyFunc listing,
+                                               GAtResultFunc func,
+                                               gpointer user_data,
+                                               GDestroyNotify notify,
+                                               gboolean wakeup)
+{
+       struct at_command *c;
+       gsize len;
+       char **prefixes = NULL;
+
+       if (prefix_list) {
+               int num_prefixes = 0;
+               int i;
+
+               while (prefix_list[num_prefixes])
+                       num_prefixes += 1;
+
+               prefixes = g_new(char *, num_prefixes + 1);
+
+               for (i = 0; i < num_prefixes; i++)
+                       prefixes[i] = strdup(prefix_list[i]);
+
+               prefixes[num_prefixes] = NULL;
+       }
+
+       c = g_try_new0(struct at_command, 1);
+       if (c == NULL)
+               return 0;
+
+       len = strlen(cmd);
+       c->cmd = g_try_new(char, len + 2);
+       if (c->cmd == NULL) {
+               g_free(c);
+               return 0;
+       }
+
+       memcpy(c->cmd, cmd, len);
+
+       /* If we have embedded '\r' then this is a command expecting a prompt
+        * from the modem.  Embed Ctrl-Z at the very end automatically
+        */
+       if (wakeup == FALSE) {
+               if (strchr(cmd, '\r'))
+                       c->cmd[len] = 26;
+               else
+                       c->cmd[len] = '\r';
+
+               len += 1;
+       }
+
+       c->cmd[len] = '\0';
+
+       c->gid = gid;
+       c->flags = flags;
+       c->prefixes = prefixes;
+       c->callback = func;
+       c->listing = listing;
+       c->user_data = user_data;
+       c->notify = notify;
+
+       return c;
+}
+
+static void at_command_destroy(struct at_command *cmd)
+{
+       if (cmd->notify)
+               cmd->notify(cmd->user_data);
+
+       g_strfreev(cmd->prefixes);
+       g_free(cmd->cmd);
+       g_free(cmd);
+}
+
+static void free_terminator(struct terminator_info *info)
+{
+       g_free(info->terminator);
+       info->terminator = NULL;
+       g_free(info);
+       info = NULL;
+}
+
+static void chat_cleanup(struct at_chat *chat)
+{
+       struct at_command *c;
+
+       /* Cleanup pending commands */
+       while ((c = g_queue_pop_head(chat->command_queue)))
+               at_command_destroy(c);
+
+       g_queue_free(chat->command_queue);
+       chat->command_queue = NULL;
+
+       /* Cleanup any response lines we have pending */
+       g_slist_foreach(chat->response_lines, (GFunc)g_free, NULL);
+       g_slist_free(chat->response_lines);
+       chat->response_lines = NULL;
+
+       /* Cleanup registered notifications */
+       g_hash_table_destroy(chat->notify_list);
+       chat->notify_list = NULL;
+
+       if (chat->pdu_notify) {
+               g_free(chat->pdu_notify);
+               chat->pdu_notify = NULL;
+       }
+
+       if (chat->wakeup) {
+               g_free(chat->wakeup);
+               chat->wakeup = NULL;
+       }
+
+       if (chat->wakeup_timer) {
+               g_timer_destroy(chat->wakeup_timer);
+               chat->wakeup_timer = 0;
+       }
+
+       if (chat->timeout_source) {
+               g_source_remove(chat->timeout_source);
+               chat->timeout_source = 0;
+       }
+
+       g_at_syntax_unref(chat->syntax);
+       chat->syntax = NULL;
+
+       if (chat->terminator_list) {
+               g_slist_foreach(chat->terminator_list,
+                                       (GFunc)free_terminator, NULL);
+               g_slist_free(chat->terminator_list);
+               chat->terminator_list = NULL;
+       }
+}
+
+static void io_disconnect(gpointer user_data)
+{
+       struct at_chat *chat = user_data;
+
+       chat_cleanup(chat);
+       g_at_io_unref(chat->io);
+       chat->io = NULL;
+
+       if (chat->user_disconnect)
+               chat->user_disconnect(chat->user_disconnect_data);
+}
+
+static void at_notify_call_callback(gpointer data, gpointer user_data)
+{
+       struct at_notify_node *node = data;
+       GAtResult *result = user_data;
+
+       node->callback(result, node->user_data);
+}
+
+static gboolean at_chat_match_notify(struct at_chat *chat, char *line)
+{
+       GHashTableIter iter;
+       struct at_notify *notify;
+       gpointer key, value;
+       gboolean ret = FALSE;
+       GAtResult result;
+
+       g_hash_table_iter_init(&iter, chat->notify_list);
+       result.lines = 0;
+       result.final_or_pdu = 0;
+
+       chat->in_notify = TRUE;
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               notify = value;
+
+               if (!g_str_has_prefix(line, key))
+                       continue;
+
+               if (notify->pdu) {
+                       chat->pdu_notify = line;
+
+                       if (chat->syntax->set_hint)
+                               chat->syntax->set_hint(chat->syntax,
+                                                       G_AT_SYNTAX_EXPECT_PDU);
+                       return TRUE;
+               }
+
+               if (result.lines == NULL)
+                       result.lines = g_slist_prepend(NULL, line);
+
+               g_slist_foreach(notify->nodes, at_notify_call_callback,
+                                       &result);
+               ret = TRUE;
+       }
+
+       chat->in_notify = FALSE;
+
+       if (ret) {
+               g_slist_free(result.lines);
+               g_free(line);
+
+               at_chat_unregister_all(chat, FALSE, node_is_destroyed, NULL);
+       }
+
+       return ret;
+}
+
+static void at_chat_finish_command(struct at_chat *p, gboolean ok, char *final)
+{
+       struct at_command *cmd = g_queue_pop_head(p->command_queue);
+       GSList *response_lines;
+
+       /* Cannot happen, but lets be paranoid */
+       if (cmd == NULL)
+               return;
+
+       p->cmd_bytes_written = 0;
+
+       if (g_queue_peek_head(p->command_queue))
+               chat_wakeup_writer(p);
+
+       response_lines = p->response_lines;
+       p->response_lines = NULL;
+
+       if (cmd->callback) {
+               GAtResult result;
+
+               response_lines = g_slist_reverse(response_lines);
+
+               result.final_or_pdu = final;
+               result.lines = response_lines;
+
+               cmd->callback(ok, &result, cmd->user_data);
+       }
+
+       g_slist_foreach(response_lines, (GFunc)g_free, NULL);
+       g_slist_free(response_lines);
+
+       g_free(final);
+       at_command_destroy(cmd);
+}
+
+static struct terminator_info terminator_table[] = {
+       { "OK", -1, TRUE },
+       { "ERROR", -1, FALSE },
+       { "NO DIALTONE", -1, FALSE },
+       { "BUSY", -1, FALSE },
+       { "NO CARRIER", -1, FALSE },
+       { "CONNECT", 7, TRUE },
+       { "NO ANSWER", -1, FALSE },
+       { "+CMS ERROR:", 11, FALSE },
+       { "+CME ERROR:", 11, FALSE },
+       { "+EXT ERROR:", 11, FALSE }
+};
+
+static void at_chat_add_terminator(struct at_chat *chat, char *terminator,
+                                       int len, gboolean success)
+{
+       struct terminator_info *info = g_new0(struct terminator_info, 1);
+       info->terminator = g_strdup(terminator);
+       info->len = len;
+       info->success = success;
+       chat->terminator_list = g_slist_prepend(chat->terminator_list, info);
+}
+
+static void at_chat_blacklist_terminator(struct at_chat *chat,
+                                               GAtChatTerminator terminator)
+{
+       chat->terminator_blacklist |= 1 << terminator;
+}
+
+static gboolean check_terminator(struct terminator_info *info, char *line)
+{
+       if (info->len == -1 && !strcmp(line, info->terminator))
+               return TRUE;
+
+       if (info->len > 0 && !strncmp(line, info->terminator, info->len))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean at_chat_handle_command_response(struct at_chat *p,
+                                                       struct at_command *cmd,
+                                                       char *line)
+{
+       int i;
+       int size = sizeof(terminator_table) / sizeof(struct terminator_info);
+       int hint;
+       GSList *l;
+
+       for (i = 0; i < size; i++) {
+               struct terminator_info *info = &terminator_table[i];
+               if (check_terminator(info, line) &&
+                               (p->terminator_blacklist & 1 << i) == 0) {
+                       at_chat_finish_command(p, info->success, line);
+                       return TRUE;
+               }
+       }
+
+       for (l = p->terminator_list; l; l = l->next) {
+               struct terminator_info *info = l->data;
+               if (check_terminator(info, line)) {
+                       at_chat_finish_command(p, info->success, line);
+                       return TRUE;
+               }
+       }
+
+       if (cmd->prefixes) {
+               int i;
+
+               for (i = 0; cmd->prefixes[i]; i++)
+                       if (g_str_has_prefix(line, cmd->prefixes[i]))
+                               goto out;
+
+               return FALSE;
+       }
+
+out:
+       if (cmd->listing && (cmd->flags & COMMAND_FLAG_EXPECT_PDU))
+               hint = G_AT_SYNTAX_EXPECT_PDU;
+       else
+               hint = G_AT_SYNTAX_EXPECT_MULTILINE;
+
+       if (p->syntax->set_hint)
+               p->syntax->set_hint(p->syntax, hint);
+
+       if (cmd->listing && (cmd->flags & COMMAND_FLAG_EXPECT_PDU)) {
+               p->pdu_notify = line;
+               return TRUE;
+       }
+
+       if (cmd->listing) {
+               GAtResult result;
+
+               result.lines = g_slist_prepend(NULL, line);
+               result.final_or_pdu = NULL;
+
+               cmd->listing(&result, cmd->user_data);
+
+               g_slist_free(result.lines);
+               g_free(line);
+       } else
+               p->response_lines = g_slist_prepend(p->response_lines, line);
+
+       return TRUE;
+}
+
+static void have_line(struct at_chat *p, char *str)
+{
+       /* We're not going to copy terminal <CR><LF> */
+       struct at_command *cmd;
+
+       if (str == NULL)
+               return;
+
+       /* Check for echo, this should not happen, but lets be paranoid */
+       if (!strncmp(str, "AT", 2) == TRUE)
+               goto done;
+
+       cmd = g_queue_peek_head(p->command_queue);
+
+       if (cmd && p->cmd_bytes_written > 0) {
+               char c = cmd->cmd[p->cmd_bytes_written - 1];
+
+               /* We check that we have submitted a terminator, in which case
+                * a command might have failed or completed successfully
+                *
+                * In the generic case, \r is at the end of the command, so we
+                * know the entire command has been submitted.  In the case of
+                * commands like CMGS, every \r or Ctrl-Z might result in a
+                * final response from the modem, so we check this as well.
+                */
+               if ((c == '\r' || c == 26) &&
+                               at_chat_handle_command_response(p, cmd, str))
+                       return;
+       }
+
+       if (at_chat_match_notify(p, str) == TRUE)
+               return;
+
+done:
+       /* No matches & no commands active, ignore line */
+       g_free(str);
+}
+
+static void have_notify_pdu(struct at_chat *p, char *pdu, GAtResult *result)
+{
+       GHashTableIter iter;
+       struct at_notify *notify;
+       char *prefix;
+       gpointer key, value;
+       gboolean called = FALSE;
+
+       p->in_notify = TRUE;
+
+       g_hash_table_iter_init(&iter, p->notify_list);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               prefix = key;
+               notify = value;
+
+               if (!g_str_has_prefix(p->pdu_notify, prefix))
+                       continue;
+
+               if (!notify->pdu)
+                       continue;
+
+               g_slist_foreach(notify->nodes, at_notify_call_callback, result);
+               called = TRUE;
+       }
+
+       p->in_notify = FALSE;
+
+       if (called)
+               at_chat_unregister_all(p, FALSE, node_is_destroyed, NULL);
+}
+
+static void have_pdu(struct at_chat *p, char *pdu)
+{
+       struct at_command *cmd;
+       GAtResult result;
+       gboolean listing_pdu = FALSE;
+
+       if (pdu == NULL)
+               goto error;
+
+       result.lines = g_slist_prepend(NULL, p->pdu_notify);
+       result.final_or_pdu = pdu;
+
+       cmd = g_queue_peek_head(p->command_queue);
+
+       if (cmd && (cmd->flags & COMMAND_FLAG_EXPECT_PDU) &&
+                       p->cmd_bytes_written > 0) {
+               char c = cmd->cmd[p->cmd_bytes_written - 1];
+
+               if (c == '\r')
+                       listing_pdu = TRUE;
+       }
+
+       if (listing_pdu) {
+               cmd->listing(&result, cmd->user_data);
+
+               if (p->syntax->set_hint)
+                       p->syntax->set_hint(p->syntax,
+                                               G_AT_SYNTAX_EXPECT_MULTILINE);
+       } else
+               have_notify_pdu(p, pdu, &result);
+
+       g_slist_free(result.lines);
+
+error:
+       g_free(p->pdu_notify);
+       p->pdu_notify = NULL;
+
+       if (pdu)
+               g_free(pdu);
+}
+
+static char *extract_line(struct at_chat *p, struct ring_buffer *rbuf)
+{
+       unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
+       unsigned int pos = 0;
+       unsigned char *buf = ring_buffer_read_ptr(rbuf, pos);
+       gboolean in_string = FALSE;
+       int strip_front = 0;
+       int line_length = 0;
+       char *line;
+
+       while (pos < p->read_so_far) {
+               if (in_string == FALSE && (*buf == '\r' || *buf == '\n')) {
+                       if (!line_length)
+                               strip_front += 1;
+                       else
+                               break;
+               } else {
+                       if (*buf == '"')
+                               in_string = !in_string;
+
+                       line_length += 1;
+               }
+
+               buf += 1;
+               pos += 1;
+
+               if (pos == wrap)
+                       buf = ring_buffer_read_ptr(rbuf, pos);
+       }
+
+       line = g_try_new(char, line_length + 1);
+       if (line == NULL) {
+               ring_buffer_drain(rbuf, p->read_so_far);
+               return NULL;
+       }
+
+       ring_buffer_drain(rbuf, strip_front);
+       ring_buffer_read(rbuf, line, line_length);
+       ring_buffer_drain(rbuf, p->read_so_far - strip_front - line_length);
+
+       line[line_length] = '\0';
+
+       return line;
+}
+
+static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
+{
+       struct at_chat *p = user_data;
+       unsigned int len = ring_buffer_len(rbuf);
+       unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
+       unsigned char *buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
+
+       GAtSyntaxResult result;
+
+       p->in_read_handler = TRUE;
+
+       while (p->suspended == FALSE && (p->read_so_far < len)) {
+               gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
+               result = p->syntax->feed(p->syntax, (char *)buf, &rbytes);
+
+               buf += rbytes;
+               p->read_so_far += rbytes;
+
+               if (p->read_so_far == wrap) {
+                       buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
+                       wrap = len;
+               }
+
+               if (result == G_AT_SYNTAX_RESULT_UNSURE)
+                       continue;
+
+               switch (result) {
+               case G_AT_SYNTAX_RESULT_LINE:
+               case G_AT_SYNTAX_RESULT_MULTILINE:
+                       have_line(p, extract_line(p, rbuf));
+                       break;
+
+               case G_AT_SYNTAX_RESULT_PDU:
+                       have_pdu(p, extract_line(p, rbuf));
+                       break;
+
+               case G_AT_SYNTAX_RESULT_PROMPT:
+                       chat_wakeup_writer(p);
+                       ring_buffer_drain(rbuf, p->read_so_far);
+                       break;
+
+               default:
+                       ring_buffer_drain(rbuf, p->read_so_far);
+                       break;
+               }
+
+               len -= p->read_so_far;
+               wrap -= p->read_so_far;
+               p->read_so_far = 0;
+       }
+
+       p->in_read_handler = FALSE;
+
+       if (p->destroyed)
+               g_free(p);
+}
+
+static void wakeup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct at_chat *chat = user_data;
+
+       if (ok == FALSE)
+               return;
+
+       if (chat->debugf)
+               chat->debugf("Finally woke up the modem\n", chat->debug_data);
+
+       g_source_remove(chat->timeout_source);
+       chat->timeout_source = 0;
+}
+
+static gboolean wakeup_no_response(gpointer user_data)
+{
+       struct at_chat *chat = user_data;
+       struct at_command *cmd = g_queue_peek_head(chat->command_queue);
+
+       if (chat->debugf)
+               chat->debugf("Wakeup got no response\n", chat->debug_data);
+
+       if (cmd == NULL)
+               return FALSE;
+
+       at_chat_finish_command(chat, FALSE, NULL);
+
+       cmd = at_command_create(0, chat->wakeup, none_prefix, 0,
+                               NULL, wakeup_cb, chat, NULL, TRUE);
+       if (cmd == NULL) {
+               chat->timeout_source = 0;
+               return FALSE;
+       }
+
+       g_queue_push_head(chat->command_queue, cmd);
+
+       return TRUE;
+}
+
+static gboolean can_write_data(gpointer data)
+{
+       struct at_chat *chat = data;
+       struct at_command *cmd;
+       gsize bytes_written;
+       gsize towrite;
+       gsize len;
+       char *cr;
+       gboolean wakeup_first = FALSE;
+
+       /* Grab the first command off the queue and write as
+        * much of it as we can
+        */
+       cmd = g_queue_peek_head(chat->command_queue);
+
+       /* For some reason command queue is empty, cancel write watcher */
+       if (cmd == NULL)
+               return FALSE;
+
+       len = strlen(cmd->cmd);
+
+       /* For some reason write watcher fired, but we've already
+        * written the entire command out to the io channel,
+        * cancel write watcher
+        */
+       if (chat->cmd_bytes_written >= len)
+               return FALSE;
+
+       if (chat->wakeup) {
+               if (chat->wakeup_timer == NULL) {
+                       wakeup_first = TRUE;
+                       chat->wakeup_timer = g_timer_new();
+
+               } else if (g_timer_elapsed(chat->wakeup_timer, NULL) >
+                               chat->inactivity_time)
+                       wakeup_first = TRUE;
+       }
+
+       if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) {
+               cmd = at_command_create(0, chat->wakeup, none_prefix, 0,
+                                       NULL, wakeup_cb, chat, NULL, TRUE);
+               if (cmd == NULL)
+                       return FALSE;
+
+               g_queue_push_head(chat->command_queue, cmd);
+
+               len = strlen(chat->wakeup);
+
+               chat->timeout_source = g_timeout_add(chat->wakeup_timeout,
+                                               wakeup_no_response, chat);
+       }
+
+       towrite = len - chat->cmd_bytes_written;
+
+       cr = strchr(cmd->cmd + chat->cmd_bytes_written, '\r');
+
+       if (cr)
+               towrite = cr - (cmd->cmd + chat->cmd_bytes_written) + 1;
+
+#ifdef WRITE_SCHEDULER_DEBUG
+       if (towrite > 5)
+               towrite = 5;
+#endif
+
+       bytes_written = g_at_io_write(chat->io,
+                                       cmd->cmd + chat->cmd_bytes_written,
+                                       towrite);
+
+       if (bytes_written == 0)
+               return FALSE;
+
+       chat->cmd_bytes_written += bytes_written;
+
+       if (bytes_written < towrite)
+               return TRUE;
+
+       /*
+        * If we're expecting a short prompt, set the hint for all lines
+        * sent to the modem except the last
+        */
+       if ((cmd->flags & COMMAND_FLAG_EXPECT_SHORT_PROMPT) &&
+                       chat->cmd_bytes_written < len &&
+                       chat->syntax->set_hint)
+               chat->syntax->set_hint(chat->syntax,
+                                       G_AT_SYNTAX_EXPECT_SHORT_PROMPT);
+
+       /* Full command submitted, update timer */
+       if (chat->wakeup_timer)
+               g_timer_start(chat->wakeup_timer);
+
+       return FALSE;
+}
+
+static void chat_wakeup_writer(struct at_chat *chat)
+{
+       g_at_io_set_write_handler(chat->io, can_write_data, chat);
+}
+
+static void at_chat_suspend(struct at_chat *chat)
+{
+       chat->suspended = TRUE;
+
+       g_at_io_set_write_handler(chat->io, NULL, NULL);
+       g_at_io_set_read_handler(chat->io, NULL, NULL);
+       g_at_io_set_debug(chat->io, NULL, NULL);
+}
+
+static void at_chat_resume(struct at_chat *chat)
+{
+       chat->suspended = FALSE;
+
+       if (g_at_io_get_channel(chat->io) == NULL) {
+               io_disconnect(chat);
+               return;
+       }
+
+       g_at_io_set_disconnect_function(chat->io, io_disconnect, chat);
+
+       g_at_io_set_debug(chat->io, chat->debugf, chat->debug_data);
+       g_at_io_set_read_handler(chat->io, new_bytes, chat);
+
+       if (g_queue_get_length(chat->command_queue) > 0)
+               chat_wakeup_writer(chat);
+}
+
+static void at_chat_unref(struct at_chat *chat)
+{
+       gboolean is_zero;
+
+       is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
+
+       if (is_zero == FALSE)
+               return;
+
+       if (chat->io) {
+               at_chat_suspend(chat);
+               g_at_io_unref(chat->io);
+               chat->io = NULL;
+               chat_cleanup(chat);
+       }
+
+       if (chat->in_read_handler)
+               chat->destroyed = TRUE;
+       else
+               g_free(chat);
+}
+
+static gboolean at_chat_set_disconnect_function(struct at_chat *chat,
+                                               GAtDisconnectFunc disconnect,
+                                               gpointer user_data)
+{
+       chat->user_disconnect = disconnect;
+       chat->user_disconnect_data = user_data;
+
+       return TRUE;
+}
+
+static gboolean at_chat_set_debug(struct at_chat *chat,
+                                       GAtDebugFunc func, gpointer user_data)
+{
+
+       chat->debugf = func;
+       chat->debug_data = user_data;
+
+       if (chat->io)
+               g_at_io_set_debug(chat->io, func, user_data);
+
+       return TRUE;
+}
+
+static gboolean at_chat_set_wakeup_command(struct at_chat *chat,
+                                               const char *cmd,
+                                               unsigned int timeout,
+                                               unsigned int msec)
+{
+       if (chat->wakeup)
+               g_free(chat->wakeup);
+
+       chat->wakeup = g_strdup(cmd);
+       chat->inactivity_time = (gdouble)msec / 1000;
+       chat->wakeup_timeout = timeout;
+
+       return TRUE;
+}
+
+static guint at_chat_send_common(struct at_chat *chat, guint gid,
+                                       const char *cmd,
+                                       const char **prefix_list,
+                                       guint flags,
+                                       GAtNotifyFunc listing,
+                                       GAtResultFunc func,
+                                       gpointer user_data,
+                                       GDestroyNotify notify)
+{
+       struct at_command *c;
+
+       if (chat == NULL || chat->command_queue == NULL)
+               return 0;
+
+       c = at_command_create(gid, cmd, prefix_list, flags, listing, func,
+                               user_data, notify, FALSE);
+       if (c == NULL)
+               return 0;
+
+       c->id = chat->next_cmd_id++;
+
+       g_queue_push_tail(chat->command_queue, c);
+
+       if (g_queue_get_length(chat->command_queue) == 1)
+               chat_wakeup_writer(chat);
+
+       return c->id;
+}
+
+static struct at_notify *at_notify_create(struct at_chat *chat,
+                                               const char *prefix,
+                                               gboolean pdu)
+{
+       struct at_notify *notify;
+       char *key;
+
+       key = g_strdup(prefix);
+       if (key == NULL)
+               return 0;
+
+       notify = g_try_new0(struct at_notify, 1);
+       if (notify == NULL) {
+               g_free(key);
+               return 0;
+       }
+
+       notify->pdu = pdu;
+
+       g_hash_table_insert(chat->notify_list, key, notify);
+
+       return notify;
+}
+
+static gboolean at_chat_cancel(struct at_chat *chat, guint group, guint id)
+{
+       GList *l;
+       struct at_command *c;
+
+       if (chat->command_queue == NULL)
+               return FALSE;
+
+       l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id),
+                               at_command_compare_by_id);
+
+       if (l == NULL)
+               return FALSE;
+
+       c = l->data;
+
+       if (c->gid != group)
+               return FALSE;
+
+       if (c == g_queue_peek_head(chat->command_queue) &&
+                       chat->cmd_bytes_written > 0) {
+               /* We can't actually remove it since it is most likely
+                * already in progress, just null out the callback
+                * so it won't be called
+                */
+               c->callback = NULL;
+       } else {
+               at_command_destroy(c);
+               g_queue_remove(chat->command_queue, c);
+       }
+
+       return TRUE;
+}
+
+static gboolean at_chat_cancel_group(struct at_chat *chat, guint group)
+{
+       int n = 0;
+       struct at_command *c;
+
+       if (chat->command_queue == NULL)
+               return FALSE;
+
+       while ((c = g_queue_peek_nth(chat->command_queue, n)) != NULL) {
+               if (c->id == 0 || c->gid != group) {
+                       n += 1;
+                       continue;
+               }
+
+               if (n == 0 && chat->cmd_bytes_written > 0) {
+                       c->callback = NULL;
+                       n += 1;
+                       continue;
+               }
+
+               at_command_destroy(c);
+               g_queue_remove(chat->command_queue, c);
+       }
+
+       return TRUE;
+}
+
+static guint at_chat_register(struct at_chat *chat, guint group,
+                               const char *prefix, GAtNotifyFunc func,
+                               gboolean expect_pdu, gpointer user_data,
+                               GDestroyNotify destroy_notify)
+{
+       struct at_notify *notify;
+       struct at_notify_node *node;
+
+       if (chat->notify_list == NULL)
+               return 0;
+
+       if (func == NULL)
+               return 0;
+
+       if (prefix == NULL || strlen(prefix) == 0)
+               return 0;
+
+       notify = g_hash_table_lookup(chat->notify_list, prefix);
+
+       if (notify == NULL)
+               notify = at_notify_create(chat, prefix, expect_pdu);
+
+       if (notify == NULL || notify->pdu != expect_pdu)
+               return 0;
+
+       node = g_try_new0(struct at_notify_node, 1);
+       if (node == NULL)
+               return 0;
+
+       node->id = chat->next_notify_id++;
+       node->gid = group;
+       node->callback = func;
+       node->user_data = user_data;
+       node->notify = destroy_notify;
+
+       notify->nodes = g_slist_prepend(notify->nodes, node);
+
+       return node->id;
+}
+
+static gboolean at_chat_unregister(struct at_chat *chat, gboolean mark_only,
+                                       guint group, guint id)
+{
+       GHashTableIter iter;
+       struct at_notify *notify;
+       struct at_notify_node *node;
+       gpointer key, value;
+       GSList *l;
+
+       if (chat->notify_list == NULL)
+               return FALSE;
+
+       g_hash_table_iter_init(&iter, chat->notify_list);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               notify = value;
+
+               l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id),
+                                       at_notify_node_compare_by_id);
+
+               if (l == NULL)
+                       continue;
+
+               node = l->data;
+
+               if (node->gid != group)
+                       return FALSE;
+
+               if (mark_only) {
+                       node->destroyed = TRUE;
+                       return TRUE;
+               }
+
+               at_notify_node_destroy(node, NULL);
+               notify->nodes = g_slist_remove(notify->nodes, node);
+
+               if (notify->nodes == NULL)
+                       g_hash_table_iter_remove(&iter);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean node_compare_by_group(struct at_notify_node *node,
+                                       gpointer userdata)
+{
+       guint group = GPOINTER_TO_UINT(userdata);
+
+       if (node->gid == group)
+               return TRUE;
+
+       return FALSE;
+}
+
+static struct at_chat *create_chat(GIOChannel *channel, GIOFlags flags,
+                                       GAtSyntax *syntax)
+{
+       struct at_chat *chat;
+
+       if (channel == NULL)
+               return NULL;
+
+       if (syntax == NULL)
+               return NULL;
+
+       chat = g_try_new0(struct at_chat, 1);
+       if (chat == NULL)
+               return chat;
+
+       chat->ref_count = 1;
+       chat->next_cmd_id = 1;
+       chat->next_notify_id = 1;
+       chat->debugf = NULL;
+
+       if (flags & G_IO_FLAG_NONBLOCK)
+               chat->io = g_at_io_new(channel);
+       else
+               chat->io = g_at_io_new_blocking(channel);
+
+       if (chat->io == NULL)
+               goto error;
+
+       g_at_io_set_disconnect_function(chat->io, io_disconnect, chat);
+
+       chat->command_queue = g_queue_new();
+       if (chat->command_queue == NULL)
+               goto error;
+
+       chat->notify_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, at_notify_destroy);
+
+       g_at_io_set_read_handler(chat->io, new_bytes, chat);
+
+       chat->syntax = g_at_syntax_ref(syntax);
+
+       return chat;
+
+error:
+       g_at_io_unref(chat->io);
+
+       if (chat->command_queue)
+               g_queue_free(chat->command_queue);
+
+       if (chat->notify_list)
+               g_hash_table_destroy(chat->notify_list);
+
+       g_free(chat);
+       return NULL;
+}
+
+static GAtChat *g_at_chat_new_common(GIOChannel *channel, GIOFlags flags,
+                                       GAtSyntax *syntax)
+{
+       GAtChat *chat;
+
+       chat = g_try_new0(GAtChat, 1);
+       if (chat == NULL)
+               return NULL;
+
+       chat->parent = create_chat(channel, flags, syntax);
+       if (chat->parent == NULL) {
+               g_free(chat);
+               return NULL;
+       }
+
+       chat->group = chat->parent->next_gid++;
+       chat->ref_count = 1;
+
+       return chat;
+}
+
+GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax)
+{
+       return g_at_chat_new_common(channel, G_IO_FLAG_NONBLOCK, syntax);
+}
+
+GAtChat *g_at_chat_new_blocking(GIOChannel *channel, GAtSyntax *syntax)
+{
+       return g_at_chat_new_common(channel, 0, syntax);
+}
+
+GAtChat *g_at_chat_clone(GAtChat *clone)
+{
+       GAtChat *chat;
+
+       if (clone == NULL)
+               return NULL;
+
+       chat = g_try_new0(GAtChat, 1);
+       if (chat == NULL)
+               return NULL;
+
+       chat->parent = clone->parent;
+       chat->group = chat->parent->next_gid++;
+       chat->ref_count = 1;
+       g_atomic_int_inc(&chat->parent->ref_count);
+
+       if (clone->slave != NULL)
+               chat->slave = g_at_chat_clone(clone->slave);
+
+       return chat;
+}
+
+GAtChat *g_at_chat_set_slave(GAtChat *chat, GAtChat *slave)
+{
+       if (chat == NULL)
+               return NULL;
+
+       if (chat->slave != NULL)
+               g_at_chat_unref(chat->slave);
+
+       if (slave != NULL)
+               chat->slave = g_at_chat_ref(slave);
+       else
+               chat->slave = NULL;
+
+       return chat->slave;
+}
+
+GAtChat *g_at_chat_get_slave(GAtChat *chat)
+{
+       if (chat == NULL)
+               return NULL;
+
+       return chat->slave;
+}
+
+GIOChannel *g_at_chat_get_channel(GAtChat *chat)
+{
+       if (chat == NULL || chat->parent->io == NULL)
+               return NULL;
+
+       return g_at_io_get_channel(chat->parent->io);
+}
+
+GAtIO *g_at_chat_get_io(GAtChat *chat)
+{
+       if (chat == NULL)
+               return NULL;
+
+       return chat->parent->io;
+}
+
+GAtChat *g_at_chat_ref(GAtChat *chat)
+{
+       if (chat == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&chat->ref_count);
+
+       return chat;
+}
+
+void g_at_chat_suspend(GAtChat *chat)
+{
+       if (chat == NULL)
+               return;
+
+       at_chat_suspend(chat->parent);
+}
+
+void g_at_chat_resume(GAtChat *chat)
+{
+       if (chat == NULL)
+               return;
+
+       at_chat_resume(chat->parent);
+}
+
+void g_at_chat_unref(GAtChat *chat)
+{
+       gboolean is_zero;
+
+       if (chat == NULL)
+               return;
+
+       is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
+
+       if (is_zero == FALSE)
+               return;
+
+       if (chat->slave != NULL)
+               g_at_chat_unref(chat->slave);
+
+       at_chat_cancel_group(chat->parent, chat->group);
+       g_at_chat_unregister_all(chat);
+       at_chat_unref(chat->parent);
+
+       g_free(chat);
+}
+
+gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
+                       GAtDisconnectFunc disconnect, gpointer user_data)
+{
+       if (chat == NULL || chat->group != 0)
+               return FALSE;
+
+       return at_chat_set_disconnect_function(chat->parent, disconnect,
+                                               user_data);
+}
+
+gboolean g_at_chat_set_debug(GAtChat *chat,
+                               GAtDebugFunc func, gpointer user_data)
+{
+
+       if (chat == NULL || chat->group != 0)
+               return FALSE;
+
+       return at_chat_set_debug(chat->parent, func, user_data);
+}
+
+void g_at_chat_add_terminator(GAtChat *chat, char *terminator,
+                                       int len, gboolean success)
+{
+       if (chat == NULL || chat->group != 0)
+               return;
+
+       at_chat_add_terminator(chat->parent, terminator, len, success);
+}
+
+void g_at_chat_blacklist_terminator(GAtChat *chat,
+                                               GAtChatTerminator terminator)
+{
+       if (chat == NULL || chat->group != 0)
+               return;
+
+       at_chat_blacklist_terminator(chat->parent, terminator);
+}
+
+gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
+                                       unsigned int timeout, unsigned int msec)
+{
+       if (chat == NULL || chat->group != 0)
+               return FALSE;
+
+       return at_chat_set_wakeup_command(chat->parent, cmd, timeout, msec);
+}
+
+guint g_at_chat_send(GAtChat *chat, const char *cmd,
+                       const char **prefix_list, GAtResultFunc func,
+                       gpointer user_data, GDestroyNotify notify)
+{
+       return at_chat_send_common(chat->parent, chat->group,
+                                       cmd, prefix_list, 0, NULL,
+                                       func, user_data, notify);
+}
+
+guint g_at_chat_send_listing(GAtChat *chat, const char *cmd,
+                               const char **prefix_list,
+                               GAtNotifyFunc listing, GAtResultFunc func,
+                               gpointer user_data, GDestroyNotify notify)
+{
+       if (listing == NULL)
+               return 0;
+
+       return at_chat_send_common(chat->parent, chat->group,
+                                       cmd, prefix_list, 0,
+                                       listing, func, user_data, notify);
+}
+
+guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd,
+                               const char **prefix_list,
+                               GAtNotifyFunc listing, GAtResultFunc func,
+                               gpointer user_data, GDestroyNotify notify)
+{
+       if (listing == NULL)
+               return 0;
+
+       return at_chat_send_common(chat->parent, chat->group,
+                                       cmd, prefix_list,
+                                       COMMAND_FLAG_EXPECT_PDU,
+                                       listing, func, user_data, notify);
+}
+
+guint g_at_chat_send_and_expect_short_prompt(GAtChat *chat, const char *cmd,
+                                               const char **prefix_list,
+                                               GAtResultFunc func,
+                                               gpointer user_data,
+                                               GDestroyNotify notify)
+{
+       return at_chat_send_common(chat->parent, chat->group,
+                                       cmd, prefix_list,
+                                       COMMAND_FLAG_EXPECT_SHORT_PROMPT,
+                                       NULL, func, user_data, notify);
+}
+
+gboolean g_at_chat_cancel(GAtChat *chat, guint id)
+{
+       /* We use id 0 for wakeup commands */
+       if (chat == NULL || id == 0)
+               return FALSE;
+
+       return at_chat_cancel(chat->parent, chat->group, id);
+}
+
+gboolean g_at_chat_cancel_all(GAtChat *chat)
+{
+       if (chat == NULL)
+               return FALSE;
+
+       return at_chat_cancel_group(chat->parent, chat->group);
+}
+
+guint g_at_chat_register(GAtChat *chat, const char *prefix,
+                               GAtNotifyFunc func, gboolean expect_pdu,
+                               gpointer user_data,
+                               GDestroyNotify destroy_notify)
+{
+       if (chat == NULL)
+               return 0;
+
+       return at_chat_register(chat->parent, chat->group, prefix,
+                               func, expect_pdu, user_data, destroy_notify);
+}
+
+gboolean g_at_chat_unregister(GAtChat *chat, guint id)
+{
+       if (chat == NULL)
+               return FALSE;
+
+       return at_chat_unregister(chat->parent, chat->parent->in_notify,
+                                       chat->group, id);
+}
+
+gboolean g_at_chat_unregister_all(GAtChat *chat)
+{
+       if (chat == NULL)
+               return FALSE;
+
+       return at_chat_unregister_all(chat->parent,
+                                       chat->parent->in_notify,
+                                       node_compare_by_group,
+                                       GUINT_TO_POINTER(chat->group));
+}
diff --git a/gatchat/gatchat.h b/gatchat/gatchat.h
new file mode 100644 (file)
index 0000000..367581e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATCHAT_H
+#define __GATCHAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gatresult.h"
+#include "gatsyntax.h"
+#include "gatutil.h"
+#include "gatio.h"
+
+struct _GAtChat;
+
+typedef struct _GAtChat GAtChat;
+
+typedef void (*GAtResultFunc)(gboolean success, GAtResult *result,
+                               gpointer user_data);
+typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data);
+
+enum _GAtChatTerminator {
+       G_AT_CHAT_TERMINATOR_OK,
+       G_AT_CHAT_TERMINATOR_ERROR,
+       G_AT_CHAT_TERMINATOR_NO_DIALTONE,
+       G_AT_CHAT_TERMINATOR_BUSY,
+       G_AT_CHAT_TERMINATOR_NO_CARRIER,
+       G_AT_CHAT_TERMINATOR_CONNECT,
+       G_AT_CHAT_TERMINATOR_NO_ANSWER,
+       G_AT_CHAT_TERMINATOR_CMS_ERROR,
+       G_AT_CHAT_TERMINATOR_CME_ERROR,
+       G_AT_CHAT_TERMINATOR_EXT_ERROR,
+};
+
+typedef enum _GAtChatTerminator GAtChatTerminator;
+
+GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax);
+GAtChat *g_at_chat_new_blocking(GIOChannel *channel, GAtSyntax *syntax);
+
+GIOChannel *g_at_chat_get_channel(GAtChat *chat);
+GAtIO *g_at_chat_get_io(GAtChat *chat);
+
+GAtChat *g_at_chat_ref(GAtChat *chat);
+void g_at_chat_unref(GAtChat *chat);
+
+GAtChat *g_at_chat_clone(GAtChat *chat);
+
+GAtChat *g_at_chat_set_slave(GAtChat *chat, GAtChat *slave);
+GAtChat *g_at_chat_get_slave(GAtChat *chat);
+
+void g_at_chat_suspend(GAtChat *chat);
+void g_at_chat_resume(GAtChat *chat);
+
+gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
+                       GAtDisconnectFunc disconnect, gpointer user_data);
+
+/*!
+ * If the function is not NULL, then on every read/write from the GIOChannel
+ * provided to GAtChat the logging function will be called with the
+ * input/output string and user data
+ */
+gboolean g_at_chat_set_debug(GAtChat *chat,
+                               GAtDebugFunc func, gpointer user_data);
+
+/*!
+ * Queue an AT command for execution.  The command contents are given
+ * in cmd.  Once the command executes, the callback function given by
+ * func is called with user provided data in user_data.
+ *
+ * Returns an id of the queued command which can be canceled using
+ * g_at_chat_cancel.  If an error occurred, an id of 0 is returned.
+ *
+ * This function can be used in three ways:
+ *     - Send a simple command such as g_at_chat_send(p, "AT+CGMI?", ...
+ *
+ *     - Send a compound command: g_at_chat_send(p, "AT+CMD1;+CMD2", ...
+ *
+ *     - Send a command requiring a prompt.  The command up to '\r' is sent
+ *       after which time a '> ' prompt is expected from the modem.  Further
+ *       contents of the command are sent until a '\r' or end of string is
+ *       encountered.  If end of string is encountered, the Ctrl-Z character
+ *       is sent automatically.  There is no need to include the Ctrl-Z
+ *       by the caller.
+ *
+ * The valid_resp field can be used to send an array of strings which will
+ * be accepted as a valid response for this command.  This is treated as a
+ * simple prefix match.  If a response line comes in from the modem and it
+ * does not match any of the prefixes in valid_resp, it is treated as an
+ * unsolicited notification.  If valid_resp is NULL, then all response
+ * lines after command submission and final response line are treated as
+ * part of the command response.  This can be used to get around broken
+ * modems which send unsolicited notifications during command processing.
+ */
+guint g_at_chat_send(GAtChat *chat, const char *cmd,
+                               const char **valid_resp, GAtResultFunc func,
+                               gpointer user_data, GDestroyNotify notify);
+
+/*!
+ * Same as the above command, except that the caller wishes to receive the
+ * intermediate responses immediately through the GAtNotifyFunc callback.
+ * The final response will still be sent to GAtResultFunc callback.  The
+ * final GAtResult will not contain any lines from the intermediate responses.
+ * This is useful for listing commands such as CPBR.
+ */
+guint g_at_chat_send_listing(GAtChat *chat, const char *cmd,
+                               const char **valid_resp,
+                               GAtNotifyFunc listing, GAtResultFunc func,
+                               gpointer user_data, GDestroyNotify notify);
+
+/*!
+ * Same as g_at_chat_send_listing except every response line in valid_resp
+ * is expected to be followed by a PDU.  The listing function will be called
+ * with the intermediate response and the following PDU line.
+ *
+ * This is useful for PDU listing commands like the +CMGL
+ */
+guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd,
+                               const char **valid_resp,
+                               GAtNotifyFunc listing, GAtResultFunc func,
+                               gpointer user_data, GDestroyNotify notify);
+
+/*!
+ * Same as g_at_chat_send except parser will know to expect short prompt syntax
+ * used with +CPOS.
+ */
+guint g_at_chat_send_and_expect_short_prompt(GAtChat *chat, const char *cmd,
+                               const char **valid_resp, GAtResultFunc func,
+                               gpointer user_data, GDestroyNotify notify);
+
+gboolean g_at_chat_cancel(GAtChat *chat, guint id);
+gboolean g_at_chat_cancel_all(GAtChat *chat);
+
+guint g_at_chat_register(GAtChat *chat, const char *prefix,
+                               GAtNotifyFunc func, gboolean expect_pdu,
+                               gpointer user_data, GDestroyNotify notify);
+
+gboolean g_at_chat_unregister(GAtChat *chat, guint id);
+gboolean g_at_chat_unregister_all(GAtChat *chat);
+
+gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
+                                       guint timeout, guint msec);
+
+void g_at_chat_add_terminator(GAtChat *chat, char *terminator,
+                               int len, gboolean success);
+void g_at_chat_blacklist_terminator(GAtChat *chat,
+                                               GAtChatTerminator terminator);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATCHAT_H */
diff --git a/gatchat/gathdlc.c b/gatchat/gathdlc.c
new file mode 100644 (file)
index 0000000..8a7c5b1
--- /dev/null
@@ -0,0 +1,679 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "crc-ccitt.h"
+#include "ringbuffer.h"
+#include "gatio.h"
+#include "gatutil.h"
+#include "gathdlc.h"
+
+#define BUFFER_SIZE    (2 * 2048)
+#define MAX_BUFFERS    64      /* Maximum number of in-flight write buffers */
+#define HDLC_OVERHEAD  256     /* Rough estimate of HDLC protocol overhead */
+
+#define HDLC_FLAG      0x7e    /* Flag sequence */
+#define HDLC_ESCAPE    0x7d    /* Asynchronous control escape */
+#define HDLC_TRANS     0x20    /* Asynchronous transparency modifier */
+
+#define HDLC_INITFCS   0xffff  /* Initial FCS value */
+#define HDLC_GOODFCS   0xf0b8  /* Good final FCS value */
+
+#define HDLC_FCS(fcs, c) crc_ccitt_byte(fcs, c)
+
+#define GUARD_TIMEOUT  1000    /* Pause time before and after '+++' sequence */
+
+struct _GAtHDLC {
+       gint ref_count;
+       GAtIO *io;
+       GQueue *write_queue;    /* Write buffer queue */
+       unsigned char *decode_buffer;
+       guint decode_offset;
+       guint16 decode_fcs;
+       gboolean decode_escape;
+       guint32 xmit_accm[8];
+       guint32 recv_accm;
+       GAtReceiveFunc receive_func;
+       gpointer receive_data;
+       GAtDebugFunc debugf;
+       gpointer debug_data;
+       int record_fd;
+       gboolean in_read_handler;
+       gboolean destroyed;
+       gboolean wakeup_sent;
+       gboolean start_frame_marker;
+       gboolean no_carrier_detect;
+       GAtSuspendFunc suspend_func;
+       gpointer suspend_data;
+       guint suspend_source;
+       GTimer *timer;
+       guint num_plus;
+};
+
+static inline void hdlc_record(GAtHDLC *hdlc, gboolean in,
+                                       guint8 *data, guint16 length)
+{
+       guint16 len = htons(length);
+       guint32 ts;
+       struct timeval now;
+       unsigned char id;
+       int err;
+
+       g_at_util_debug_hexdump(in, data, length,
+                                       hdlc->debugf, hdlc->debug_data);
+
+       if (hdlc->record_fd < 0)
+               return;
+
+       if (length == 0)
+               return;
+
+       gettimeofday(&now, NULL);
+       ts = htonl(now.tv_sec & 0xffffffff);
+
+       id = 0x07;
+
+       err = write(hdlc->record_fd, &id, 1);
+       if (err < 0)
+               return;
+
+       err = write(hdlc->record_fd, &ts, 4);
+       if (err < 0)
+               return;
+
+       id = in ? 0x02 : 0x01;
+
+       err = write(hdlc->record_fd, &id, 1);
+       if (err < 0)
+               return;
+
+       err = write(hdlc->record_fd, &len, 2);
+       if (err < 0)
+               return;
+
+       err = write(hdlc->record_fd, data, length);
+       if (err < 0)
+               return;
+}
+
+void g_at_hdlc_set_recording(GAtHDLC *hdlc, const char *filename)
+{
+       if (hdlc == NULL)
+               return;
+
+       if (hdlc->record_fd > fileno(stderr)) {
+               close(hdlc->record_fd);
+               hdlc->record_fd = -1;
+       }
+
+       if (filename == NULL)
+               return;
+
+       hdlc->record_fd = open(filename, O_WRONLY | O_CREAT | O_APPEND,
+                                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+}
+
+void g_at_hdlc_set_recv_accm(GAtHDLC *hdlc, guint32 accm)
+{
+       if (hdlc == NULL)
+               return;
+
+       hdlc->recv_accm = accm;
+}
+
+guint32 g_at_hdlc_get_recv_accm(GAtHDLC *hdlc)
+{
+       if (hdlc == NULL)
+               return 0;
+
+       return hdlc->recv_accm;
+}
+
+void g_at_hdlc_set_suspend_function(GAtHDLC *hdlc, GAtSuspendFunc func,
+                                                       gpointer user_data)
+{
+       if (hdlc == NULL)
+               return;
+
+       if (func == NULL) {
+               if (hdlc->timer) {
+                       g_timer_destroy(hdlc->timer);
+                       hdlc->timer = NULL;
+               }
+
+               if (hdlc->suspend_source > 0) {
+                       g_source_remove(hdlc->suspend_source);
+                       hdlc->suspend_source = 0;
+               }
+       } else
+               hdlc->timer = g_timer_new();
+
+       hdlc->suspend_func = func;
+       hdlc->suspend_data = user_data;
+}
+
+static gboolean hdlc_suspend(gpointer user_data)
+{
+       GAtHDLC *hdlc = user_data;
+
+       g_at_io_drain_ring_buffer(hdlc->io, 3);
+
+       g_at_io_set_write_handler(hdlc->io, NULL, NULL);
+       g_at_io_set_read_handler(hdlc->io, NULL, NULL);
+
+       if (hdlc->suspend_func)
+               hdlc->suspend_func(hdlc->suspend_data);
+
+       hdlc->suspend_source = 0;
+
+       return FALSE;
+}
+
+static gboolean check_escape(GAtHDLC *hdlc, struct ring_buffer *rbuf)
+{
+       unsigned int len = ring_buffer_len(rbuf);
+       unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
+       unsigned char *buf = ring_buffer_read_ptr(rbuf, 0);
+       unsigned int pos = 0;
+       unsigned int elapsed = g_timer_elapsed(hdlc->timer, NULL) * 1000;
+       unsigned int num_plus = 0;
+       gboolean guard_timeout = FALSE;
+
+       if (elapsed >= GUARD_TIMEOUT)
+               guard_timeout = TRUE;
+
+       while (pos < len && pos < 3) {
+               if (*buf != '+')
+                       break;
+
+               num_plus++;
+               buf++;
+               pos++;
+
+               if (pos == wrap)
+                       buf = ring_buffer_read_ptr(rbuf, pos);
+       }
+
+       if (num_plus != len)
+               return FALSE;
+
+       /* We got some escape chars, but no guard timeout first */
+       if (guard_timeout == FALSE && hdlc->num_plus == 0)
+               return FALSE;
+
+       if (num_plus != 3) {
+               hdlc->num_plus = num_plus;
+               return TRUE;
+       }
+
+       hdlc->num_plus = 0;
+       hdlc->suspend_source = g_timeout_add(GUARD_TIMEOUT, hdlc_suspend, hdlc);
+
+       return TRUE;
+}
+
+static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
+{
+       GAtHDLC *hdlc = user_data;
+       unsigned int len = ring_buffer_len(rbuf);
+       unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
+       unsigned char *buf = ring_buffer_read_ptr(rbuf, 0);
+       unsigned int pos = 0;
+
+       /*
+        * We delete the the paused_timeout_cb or hdlc_suspend as soons as
+        * we read a data.
+        */
+       if (hdlc->suspend_source > 0) {
+               g_source_remove(hdlc->suspend_source);
+               hdlc->suspend_source = 0;
+               g_timer_start(hdlc->timer);
+       } else if (hdlc->timer) {
+               gboolean escaping = check_escape(hdlc, rbuf);
+
+               g_timer_start(hdlc->timer);
+
+               if (escaping)
+                       return;
+       }
+
+       hdlc_record(hdlc, TRUE, buf, wrap);
+
+       hdlc->in_read_handler = TRUE;
+
+       while (pos < len) {
+               /*
+                * We try to detect NO CARRIER conditions here.  We
+                * (ab) use the fact that a HDLC_FLAG must be followed
+                * by the Address or Protocol fields, depending on whether
+                * ACFC is enabled.
+                */
+               if (hdlc->no_carrier_detect &&
+                               hdlc->decode_offset == 0 && *buf == '\r')
+                       break;
+
+               if (hdlc->decode_escape == TRUE) {
+                       unsigned char val = *buf ^ HDLC_TRANS;
+
+                       hdlc->decode_buffer[hdlc->decode_offset++] = val;
+                       hdlc->decode_fcs = HDLC_FCS(hdlc->decode_fcs, val);
+
+                       hdlc->decode_escape = FALSE;
+               } else if (*buf == HDLC_ESCAPE) {
+                       hdlc->decode_escape = TRUE;
+               } else if (*buf == HDLC_FLAG) {
+                       if (hdlc->receive_func && hdlc->decode_offset > 2 &&
+                                       hdlc->decode_fcs == HDLC_GOODFCS) {
+                               hdlc->receive_func(hdlc->decode_buffer,
+                                                       hdlc->decode_offset - 2,
+                                                       hdlc->receive_data);
+
+                               if (hdlc->destroyed)
+                                       goto out;
+                       }
+
+                       hdlc->decode_fcs = HDLC_INITFCS;
+                       hdlc->decode_offset = 0;
+               } else if (*buf >= 0x20 ||
+                                       (hdlc->recv_accm & (1 << *buf)) == 0) {
+                       hdlc->decode_buffer[hdlc->decode_offset++] = *buf;
+                       hdlc->decode_fcs = HDLC_FCS(hdlc->decode_fcs, *buf);
+               }
+
+               buf++;
+               pos++;
+
+               if (pos == wrap) {
+                       buf = ring_buffer_read_ptr(rbuf, pos);
+                       hdlc_record(hdlc, TRUE, buf, len - wrap);
+               }
+       }
+
+out:
+       ring_buffer_drain(rbuf, pos);
+
+       hdlc->in_read_handler = FALSE;
+
+       if (hdlc->destroyed)
+               g_free(hdlc);
+}
+
+GAtHDLC *g_at_hdlc_new_from_io(GAtIO *io)
+{
+       GAtHDLC *hdlc;
+       struct ring_buffer* write_buffer;
+
+       if (io == NULL)
+               return NULL;
+
+       hdlc = g_try_new0(GAtHDLC, 1);
+       if (hdlc == NULL)
+               return NULL;
+
+       hdlc->ref_count = 1;
+       hdlc->decode_fcs = HDLC_INITFCS;
+       hdlc->decode_offset = 0;
+       hdlc->decode_escape = FALSE;
+
+       hdlc->xmit_accm[0] = ~0U;
+       hdlc->xmit_accm[3] = 0x60000000; /* 0x7d, 0x7e */
+       hdlc->recv_accm = ~0U;
+
+       write_buffer = ring_buffer_new(BUFFER_SIZE);
+       if (!write_buffer)
+               goto error;
+
+       hdlc->write_queue = g_queue_new();
+       if (!hdlc->write_queue)
+               goto error;
+
+       g_queue_push_tail(hdlc->write_queue, write_buffer);
+
+       hdlc->decode_buffer = g_try_malloc(BUFFER_SIZE);
+       if (!hdlc->decode_buffer)
+               goto error;
+
+       hdlc->record_fd = -1;
+
+       hdlc->io = g_at_io_ref(io);
+       g_at_io_set_read_handler(hdlc->io, new_bytes, hdlc);
+
+       return hdlc;
+
+error:
+       if (hdlc->write_queue)
+               g_queue_free(hdlc->write_queue);
+
+       if (write_buffer)
+               ring_buffer_free(write_buffer);
+
+       g_free(hdlc->decode_buffer);
+
+       g_free(hdlc);
+
+       return NULL;
+}
+
+GAtHDLC *g_at_hdlc_new(GIOChannel *channel)
+{
+       GAtIO *io;
+       GAtHDLC *hdlc;
+
+       io = g_at_io_new(channel);
+       if (io == NULL)
+               return NULL;
+
+       hdlc = g_at_hdlc_new_from_io(io);
+       g_at_io_unref(io);
+
+       return hdlc;
+}
+
+GAtHDLC *g_at_hdlc_ref(GAtHDLC *hdlc)
+{
+       if (hdlc == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&hdlc->ref_count);
+
+       return hdlc;
+}
+
+void g_at_hdlc_unref(GAtHDLC *hdlc)
+{
+       struct ring_buffer *write_buffer;
+
+       if (hdlc == NULL)
+               return;
+
+       if (g_atomic_int_dec_and_test(&hdlc->ref_count) == FALSE)
+               return;
+
+       if (hdlc->record_fd > fileno(stderr)) {
+               close(hdlc->record_fd);
+               hdlc->record_fd = -1;
+       }
+
+       g_at_io_set_write_handler(hdlc->io, NULL, NULL);
+       g_at_io_set_read_handler(hdlc->io, NULL, NULL);
+
+       if (hdlc->suspend_source > 0)
+               g_source_remove(hdlc->suspend_source);
+
+       g_at_io_unref(hdlc->io);
+       hdlc->io = NULL;
+
+       while ((write_buffer = g_queue_pop_head(hdlc->write_queue)))
+               ring_buffer_free(write_buffer);
+
+       g_queue_free(hdlc->write_queue);
+
+       g_free(hdlc->decode_buffer);
+
+       g_timer_destroy(hdlc->timer);
+
+       if (hdlc->in_read_handler)
+               hdlc->destroyed = TRUE;
+       else
+               g_free(hdlc);
+}
+
+void g_at_hdlc_set_debug(GAtHDLC *hdlc, GAtDebugFunc func, gpointer user_data)
+{
+       if (hdlc == NULL)
+               return;
+
+       hdlc->debugf = func;
+       hdlc->debug_data = user_data;
+}
+
+void g_at_hdlc_set_receive(GAtHDLC *hdlc, GAtReceiveFunc func,
+                                                       gpointer user_data)
+{
+       if (hdlc == NULL)
+               return;
+
+       hdlc->receive_func = func;
+       hdlc->receive_data = user_data;
+}
+
+static gboolean can_write_data(gpointer data)
+{
+       GAtHDLC *hdlc = data;
+       unsigned int len;
+       unsigned char *buf;
+       gsize bytes_written;
+       struct ring_buffer* write_buffer;
+
+       /* Write data out from the head of the queue */
+       write_buffer = g_queue_peek_head(hdlc->write_queue);
+
+       len = ring_buffer_len_no_wrap(write_buffer);
+       buf = ring_buffer_read_ptr(write_buffer, 0);
+
+       bytes_written = g_at_io_write(hdlc->io, (gchar *) buf, len);
+       hdlc_record(hdlc, FALSE, buf, bytes_written);
+       ring_buffer_drain(write_buffer, bytes_written);
+
+       if (ring_buffer_len(write_buffer) > 0)
+               return TRUE;
+
+       /* All data in current buffer is written, free it
+        * unless it's the last buffer in the queue.
+        */
+       if ((ring_buffer_len(write_buffer) == 0) &&
+                       (g_queue_get_length(hdlc->write_queue) > 1)) {
+               write_buffer = g_queue_pop_head(hdlc->write_queue);
+               ring_buffer_free(write_buffer);
+               write_buffer = g_queue_peek_head(hdlc->write_queue);
+       }
+
+       if (ring_buffer_len(write_buffer) > 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+void g_at_hdlc_set_xmit_accm(GAtHDLC *hdlc, guint32 accm)
+{
+       if (hdlc == NULL)
+               return;
+
+       hdlc->xmit_accm[0] = accm;
+}
+
+guint32 g_at_hdlc_get_xmit_accm(GAtHDLC *hdlc)
+{
+       if (hdlc == NULL)
+               return 0;
+
+       return hdlc->xmit_accm[0];
+}
+
+GAtIO *g_at_hdlc_get_io(GAtHDLC *hdlc)
+{
+       if (hdlc == NULL)
+               return NULL;
+
+       return hdlc->io;
+}
+
+#define NEED_ESCAPE(xmit_accm, c) xmit_accm[c >> 5] & (1 << (c & 0x1f))
+
+gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *data, gsize size)
+{
+       struct ring_buffer* write_buffer = g_queue_peek_tail(hdlc->write_queue);
+
+       unsigned int avail = ring_buffer_avail(write_buffer);
+       unsigned int wrap = ring_buffer_avail_no_wrap(write_buffer);
+       unsigned char *buf;
+       unsigned char tail[2];
+       unsigned int i = 0;
+       guint16 fcs = HDLC_INITFCS;
+       gboolean escape = FALSE;
+       gsize pos = 0;
+
+       if (avail < size + HDLC_OVERHEAD) {
+               if (g_queue_get_length(hdlc->write_queue) > MAX_BUFFERS)
+                       return FALSE;   /* Too many pending buffers */
+
+               write_buffer = ring_buffer_new(BUFFER_SIZE);
+               if (write_buffer == NULL)
+                       return FALSE;
+
+               g_queue_push_tail(hdlc->write_queue, write_buffer);
+
+               avail = ring_buffer_avail(write_buffer);
+               wrap = ring_buffer_avail_no_wrap(write_buffer);
+       }
+
+       i = 0;
+       buf = ring_buffer_write_ptr(write_buffer, 0);
+
+       if (hdlc->start_frame_marker == TRUE) {
+               /* Protocol requires 0x7e as start marker */
+               if (pos + 1 > avail)
+                       return FALSE;
+
+               *buf++ = HDLC_FLAG;
+               pos++;
+
+               if (pos == wrap)
+                       buf = ring_buffer_write_ptr(write_buffer, pos);
+       } else if (hdlc->wakeup_sent == FALSE) {
+               /* Write an initial 0x7e as wakeup character */
+               *buf++ = HDLC_FLAG;
+               pos++;
+
+               hdlc->wakeup_sent = TRUE;
+       }
+
+       while (pos < avail && i < size) {
+               if (escape == TRUE) {
+                       fcs = HDLC_FCS(fcs, data[i]);
+                       *buf = data[i++] ^ HDLC_TRANS;
+                       escape = FALSE;
+               } else if (NEED_ESCAPE(hdlc->xmit_accm, data[i])) {
+                       *buf = HDLC_ESCAPE;
+                       escape = TRUE;
+               } else {
+                       fcs = HDLC_FCS(fcs, data[i]);
+                       *buf = data[i++];
+               }
+
+               buf++;
+               pos++;
+
+               if (pos == wrap)
+                       buf = ring_buffer_write_ptr(write_buffer, pos);
+       }
+
+       if (i < size)
+               return FALSE;
+
+       fcs ^= HDLC_INITFCS;
+       tail[0] = fcs & 0xff;
+       tail[1] = fcs >> 8;
+
+       i = 0;
+
+       while (pos < avail && i < sizeof(tail)) {
+               if (escape == TRUE) {
+                       *buf = tail[i++] ^ HDLC_TRANS;
+                       escape = FALSE;
+               } else if (NEED_ESCAPE(hdlc->xmit_accm, tail[i])) {
+                       *buf = HDLC_ESCAPE;
+                       escape = TRUE;
+               } else {
+                       *buf = tail[i++];
+               }
+
+               buf++;
+               pos++;
+
+               if (pos == wrap)
+                       buf = ring_buffer_write_ptr(write_buffer, pos);
+       }
+
+       if (i < sizeof(tail))
+               return FALSE;
+
+       if (pos + 1 > avail)
+               return FALSE;
+
+       /* Add 0x7e as end marker */
+       *buf = HDLC_FLAG;
+       pos++;
+
+       ring_buffer_write_advance(write_buffer, pos);
+
+       g_at_io_set_write_handler(hdlc->io, can_write_data, hdlc);
+
+       return TRUE;
+}
+
+void g_at_hdlc_set_start_frame_marker(GAtHDLC *hdlc, gboolean marker)
+{
+       if (hdlc == NULL)
+               return;
+
+       hdlc->start_frame_marker = marker;
+}
+
+void g_at_hdlc_set_no_carrier_detect(GAtHDLC *hdlc, gboolean detect)
+{
+       if (hdlc == NULL)
+               return;
+
+       hdlc->no_carrier_detect = detect;
+}
+
+void g_at_hdlc_suspend(GAtHDLC *hdlc)
+{
+       if (hdlc == NULL)
+               return;
+
+       g_at_io_set_write_handler(hdlc->io, NULL, NULL);
+       g_at_io_set_read_handler(hdlc->io, NULL, NULL);
+}
+
+void g_at_hdlc_resume(GAtHDLC *hdlc)
+{
+       if (hdlc == NULL)
+               return;
+
+       g_at_io_set_read_handler(hdlc->io, new_bytes, hdlc);
+
+       if (g_queue_get_length(hdlc->write_queue) > 0)
+               g_at_io_set_write_handler(hdlc->io, can_write_data, hdlc);
+}
diff --git a/gatchat/gathdlc.h b/gatchat/gathdlc.h
new file mode 100644 (file)
index 0000000..b3aafc8
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __G_AT_HDLC_H
+#define __G_AT_HDLC_H
+
+#include "gat.h"
+#include "gatio.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _GAtHDLC;
+
+typedef struct _GAtHDLC GAtHDLC;
+
+GAtHDLC *g_at_hdlc_new(GIOChannel *channel);
+GAtHDLC *g_at_hdlc_new_from_io(GAtIO *io);
+
+GAtHDLC *g_at_hdlc_ref(GAtHDLC *hdlc);
+void g_at_hdlc_unref(GAtHDLC *hdlc);
+
+void g_at_hdlc_set_debug(GAtHDLC *hdlc, GAtDebugFunc func, gpointer user_data);
+
+void g_at_hdlc_set_xmit_accm(GAtHDLC *hdlc, guint32 accm);
+guint32 g_at_hdlc_get_xmit_accm(GAtHDLC *hdlc);
+
+void g_at_hdlc_set_recv_accm(GAtHDLC *hdlc, guint32 accm);
+guint32 g_at_hdlc_get_recv_accm(GAtHDLC *hdlc);
+
+void g_at_hdlc_set_receive(GAtHDLC *hdlc, GAtReceiveFunc func,
+                                                       gpointer user_data);
+gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *data, gsize size);
+
+void g_at_hdlc_set_recording(GAtHDLC *hdlc, const char *filename);
+
+GAtIO *g_at_hdlc_get_io(GAtHDLC *hdlc);
+
+void g_at_hdlc_set_start_frame_marker(GAtHDLC *hdlc, gboolean marker);
+void g_at_hdlc_set_no_carrier_detect(GAtHDLC *hdlc, gboolean detect);
+
+void g_at_hdlc_set_suspend_function(GAtHDLC *hdlc, GAtSuspendFunc func,
+                                                       gpointer user_data);
+
+void g_at_hdlc_suspend(GAtHDLC *hdlc);
+void g_at_hdlc_resume(GAtHDLC *hdlc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_AT_HDLC_H */
diff --git a/gatchat/gatio.c b/gatchat/gatio.c
new file mode 100644 (file)
index 0000000..4cd553f
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+#include "gatio.h"
+#include "gatutil.h"
+
+struct _GAtIO {
+       gint ref_count;                         /* Ref count */
+       guint read_watch;                       /* GSource read id, 0 if no */
+       guint write_watch;                      /* GSource write id, 0 if no */
+       GIOChannel *channel;                    /* comms channel */
+       GAtDisconnectFunc user_disconnect;      /* user disconnect func */
+       gpointer user_disconnect_data;          /* user disconnect data */
+       struct ring_buffer *buf;                /* Current read buffer */
+       guint max_read_attempts;                /* max reads / select */
+       GAtIOReadFunc read_handler;             /* Read callback */
+       gpointer read_data;                     /* Read callback userdata */
+       gboolean use_write_watch;               /* Use write select */
+       GAtIOWriteFunc write_handler;           /* Write callback */
+       gpointer write_data;                    /* Write callback userdata */
+       GAtDebugFunc debugf;                    /* debugging output function */
+       gpointer debug_data;                    /* Data to pass to debug func */
+       GAtDisconnectFunc write_done_func;      /* tx empty notifier */
+       gpointer write_done_data;               /* tx empty data */
+       gboolean destroyed;                     /* Re-entrancy guard */
+};
+
+static void read_watcher_destroy_notify(gpointer user_data)
+{
+       GAtIO *io = user_data;
+
+       ring_buffer_free(io->buf);
+       io->buf = NULL;
+
+       io->debugf = NULL;
+       io->debug_data = NULL;
+
+       io->read_watch = 0;
+       io->read_handler = NULL;
+       io->read_data = NULL;
+
+       io->channel = NULL;
+
+       if (io->destroyed)
+               g_free(io);
+       else if (io->user_disconnect)
+               io->user_disconnect(io->user_disconnect_data);
+}
+
+static gboolean received_data(GIOChannel *channel, GIOCondition cond,
+                               gpointer data)
+{
+       unsigned char *buf;
+       GAtIO *io = data;
+       GIOStatus status;
+       gsize rbytes;
+       gsize toread;
+       gsize total_read = 0;
+       guint read_count = 0;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       /* Regardless of condition, try to read all the data available */
+       do {
+               toread = ring_buffer_avail_no_wrap(io->buf);
+
+               if (toread == 0)
+                       break;
+
+               rbytes = 0;
+               buf = ring_buffer_write_ptr(io->buf, 0);
+
+               status = g_io_channel_read_chars(channel, (char *) buf,
+                                                       toread, &rbytes, NULL);
+               g_at_util_debug_chat(TRUE, (char *)buf, rbytes,
+                                       io->debugf, io->debug_data);
+
+               read_count++;
+
+               total_read += rbytes;
+
+               if (rbytes > 0)
+                       ring_buffer_write_advance(io->buf, rbytes);
+
+       } while (status == G_IO_STATUS_NORMAL && rbytes > 0 &&
+                                       read_count < io->max_read_attempts);
+
+       if (total_read > 0 && io->read_handler)
+               io->read_handler(io->buf, io->read_data);
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       if (read_count > 0 && rbytes == 0 && status != G_IO_STATUS_AGAIN)
+               return FALSE;
+
+       /* We're overflowing the buffer, shutdown the socket */
+       if (ring_buffer_avail(io->buf) == 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+gsize g_at_io_write(GAtIO *io, const gchar *data, gsize count)
+{
+       GIOStatus status;
+       gsize bytes_written;
+
+       status = g_io_channel_write_chars(io->channel, data,
+                                               count, &bytes_written, NULL);
+
+       if (status != G_IO_STATUS_NORMAL) {
+               g_source_remove(io->read_watch);
+               return 0;
+       }
+
+       g_at_util_debug_chat(FALSE, data, bytes_written,
+                               io->debugf, io->debug_data);
+
+       return bytes_written;
+}
+
+static void write_watcher_destroy_notify(gpointer user_data)
+{
+       GAtIO *io = user_data;
+
+       io->write_watch = 0;
+       io->write_handler = NULL;
+       io->write_data = NULL;
+
+       if (io->write_done_func) {
+               io->write_done_func(io->write_done_data);
+               io->write_done_func = NULL;
+               io->write_done_data = NULL;
+       }
+}
+
+static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
+                               gpointer data)
+{
+       GAtIO *io = data;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       if (io->write_handler == NULL)
+               return FALSE;
+
+       return io->write_handler(io->write_data);
+}
+
+static GAtIO *create_io(GIOChannel *channel, GIOFlags flags)
+{
+       GAtIO *io;
+
+       if (channel == NULL)
+               return NULL;
+
+       io = g_try_new0(GAtIO, 1);
+       if (io == NULL)
+               return io;
+
+       io->ref_count = 1;
+       io->debugf = NULL;
+
+       if (flags & G_IO_FLAG_NONBLOCK) {
+               io->max_read_attempts = 3;
+               io->use_write_watch = TRUE;
+       } else {
+               io->max_read_attempts = 1;
+               io->use_write_watch = FALSE;
+       }
+
+       io->buf = ring_buffer_new(8192);
+
+       if (!io->buf)
+               goto error;
+
+       if (!g_at_util_setup_io(channel, flags))
+               goto error;
+
+       io->channel = channel;
+       io->read_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               received_data, io,
+                               read_watcher_destroy_notify);
+
+       return io;
+
+error:
+       if (io->buf)
+               ring_buffer_free(io->buf);
+
+       g_free(io);
+
+       return NULL;
+}
+
+GAtIO *g_at_io_new(GIOChannel *channel)
+{
+       return create_io(channel, G_IO_FLAG_NONBLOCK);
+}
+
+GAtIO *g_at_io_new_blocking(GIOChannel *channel)
+{
+       return create_io(channel, 0);
+}
+
+GIOChannel *g_at_io_get_channel(GAtIO *io)
+{
+       if (io == NULL)
+               return NULL;
+
+       return io->channel;
+}
+
+gboolean g_at_io_set_read_handler(GAtIO *io, GAtIOReadFunc read_handler,
+                                       gpointer user_data)
+{
+       if (io == NULL)
+               return FALSE;
+
+       io->read_handler = read_handler;
+       io->read_data = user_data;
+
+       if (read_handler && ring_buffer_len(io->buf) > 0)
+               read_handler(io->buf, user_data);
+
+       return TRUE;
+}
+
+static gboolean call_blocking_read(gpointer user_data)
+{
+       GAtIO *io = user_data;
+
+       while (can_write_data(io->channel, G_IO_OUT, io) == TRUE);
+       write_watcher_destroy_notify(io);
+
+       return FALSE;
+}
+
+gboolean g_at_io_set_write_handler(GAtIO *io, GAtIOWriteFunc write_handler,
+                                       gpointer user_data)
+{
+       if (io == NULL)
+               return FALSE;
+
+       if (io->write_watch > 0) {
+               if (write_handler == NULL) {
+                       g_source_remove(io->write_watch);
+                       return TRUE;
+               }
+
+               return FALSE;
+       }
+
+       if (write_handler == NULL)
+               return FALSE;
+
+       io->write_handler = write_handler;
+       io->write_data = user_data;
+
+       if (io->use_write_watch == TRUE)
+               io->write_watch = g_io_add_watch_full(io->channel,
+                               G_PRIORITY_HIGH,
+                               G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               can_write_data, io,
+                               write_watcher_destroy_notify);
+       else
+               io->write_watch = g_idle_add(call_blocking_read, io);
+
+       return TRUE;
+}
+
+GAtIO *g_at_io_ref(GAtIO *io)
+{
+       if (io == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&io->ref_count);
+
+       return io;
+}
+
+static gboolean io_shutdown(GAtIO *io)
+{
+       /* Don't trigger user disconnect on shutdown */
+       io->user_disconnect = NULL;
+       io->user_disconnect_data = NULL;
+
+       if (io->read_watch > 0)
+               g_source_remove(io->read_watch);
+
+       if (io->write_watch > 0)
+               g_source_remove(io->write_watch);
+
+       return TRUE;
+}
+
+void g_at_io_unref(GAtIO *io)
+{
+       gboolean is_zero;
+
+       if (io == NULL)
+               return;
+
+       is_zero = g_atomic_int_dec_and_test(&io->ref_count);
+
+       if (is_zero == FALSE)
+               return;
+
+       io_shutdown(io);
+
+       /* glib delays the destruction of the watcher until it exits, this
+        * means we can't free the data just yet, even though we've been
+        * destroyed already.  We have to wait until the read_watcher
+        * destroy function gets called
+        */
+       if (io->read_watch > 0)
+               io->destroyed = TRUE;
+       else
+               g_free(io);
+}
+
+gboolean g_at_io_set_disconnect_function(GAtIO *io,
+                       GAtDisconnectFunc disconnect, gpointer user_data)
+{
+       if (io == NULL)
+               return FALSE;
+
+       io->user_disconnect = disconnect;
+       io->user_disconnect_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_at_io_set_debug(GAtIO *io, GAtDebugFunc func, gpointer user_data)
+{
+       if (io == NULL)
+               return FALSE;
+
+       io->debugf = func;
+       io->debug_data = user_data;
+
+       return TRUE;
+}
+
+void g_at_io_set_write_done(GAtIO *io, GAtDisconnectFunc func,
+                               gpointer user_data)
+{
+       if (io == NULL)
+               return;
+
+       io->write_done_func = func;
+       io->write_done_data = user_data;
+}
+
+void g_at_io_drain_ring_buffer(GAtIO *io, guint len)
+{
+       ring_buffer_drain(io->buf, len);
+}
diff --git a/gatchat/gatio.h b/gatchat/gatio.h
new file mode 100644 (file)
index 0000000..ebe1ce2
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATIO_H
+#define __GATIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gat.h"
+
+struct _GAtIO;
+
+typedef struct _GAtIO GAtIO;
+
+struct ring_buffer;
+
+typedef void (*GAtIOReadFunc)(struct ring_buffer *buffer, gpointer user_data);
+typedef gboolean (*GAtIOWriteFunc)(gpointer user_data);
+
+GAtIO *g_at_io_new(GIOChannel *channel);
+GAtIO *g_at_io_new_blocking(GIOChannel *channel);
+
+GIOChannel *g_at_io_get_channel(GAtIO *io);
+
+GAtIO *g_at_io_ref(GAtIO *io);
+void g_at_io_unref(GAtIO *io);
+
+gboolean g_at_io_set_read_handler(GAtIO *io, GAtIOReadFunc read_handler,
+                                       gpointer user_data);
+gboolean g_at_io_set_write_handler(GAtIO *io, GAtIOWriteFunc write_handler,
+                                       gpointer user_data);
+void g_at_io_set_write_done(GAtIO *io, GAtDisconnectFunc func,
+                               gpointer user_data);
+
+void g_at_io_drain_ring_buffer(GAtIO *io, guint len);
+
+gsize g_at_io_write(GAtIO *io, const gchar *data, gsize count);
+
+gboolean g_at_io_set_disconnect_function(GAtIO *io,
+                       GAtDisconnectFunc disconnect, gpointer user_data);
+
+gboolean g_at_io_set_debug(GAtIO *io, GAtDebugFunc func, gpointer user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATIO_H */
diff --git a/gatchat/gatmux.c b/gatchat/gatmux.c
new file mode 100644 (file)
index 0000000..20224dc
--- /dev/null
@@ -0,0 +1,1273 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009  Trolltech ASA.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <alloca.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+#include "gatmux.h"
+#include "gsm0710.h"
+
+static const char *cmux_prefix[] = { "+CMUX:", NULL };
+static const char *none_prefix[] = { NULL };
+
+typedef struct _GAtMuxChannel GAtMuxChannel;
+typedef struct _GAtMuxWatch GAtMuxWatch;
+typedef void (*GAtMuxWriteFrame)(GAtMux *mux, guint8 dlc, guint8 control,
+                               const guint8 *data, int len);
+
+/* While 63 channels are theoretically possible, channel 62 and 63 is reserved
+ * by 27.010 for use as the beginning of frame and end of frame flags.
+ * Refer to Section 5.6 in 27.007
+ */
+#define MAX_CHANNELS 61
+#define BITMAP_SIZE 8
+#define MUX_CHANNEL_BUFFER_SIZE 4096
+#define MUX_BUFFER_SIZE 4096
+
+struct _GAtMuxChannel
+{
+       GIOChannel channel;
+       GAtMux *mux;
+       GIOCondition condition;
+       struct ring_buffer *buffer;
+       GSList *sources;
+       gboolean throttled;
+       guint dlc;
+};
+
+struct _GAtMuxWatch
+{
+       GSource source;
+       GIOChannel *channel;
+       GIOCondition condition;
+};
+
+struct _GAtMux {
+       gint ref_count;                         /* Ref count */
+       guint read_watch;                       /* GSource read id, 0 if none */
+       guint write_watch;                      /* GSource write id, 0 if none */
+       GIOChannel *channel;                    /* main serial channel */
+       GAtDisconnectFunc user_disconnect;      /* user disconnect func */
+       gpointer user_disconnect_data;          /* user disconnect data */
+       GAtDebugFunc debugf;                    /* debugging output function */
+       gpointer debug_data;                    /* Data to pass to debug func */
+       GAtMuxChannel *dlcs[MAX_CHANNELS];      /* DLCs opened by the MUX */
+       guint8 newdata[BITMAP_SIZE];            /* Channels that got new data */
+       const GAtMuxDriver *driver;             /* Driver functions */
+       void *driver_data;                      /* Driver data */
+       char buf[MUX_BUFFER_SIZE];              /* Buffer on the main mux */
+       int buf_used;                           /* Bytes of buf being used */
+       gboolean shutdown;
+};
+
+struct mux_setup_data {
+       GAtChat *chat;
+       GAtMuxSetupFunc func;
+       gpointer user;
+       GDestroyNotify destroy;
+       guint mode;
+       guint frame_size;
+};
+
+static inline void debug(GAtMux *mux, const char *format, ...)
+{
+       char str[256];
+       va_list ap;
+
+       if (mux->debugf == NULL)
+               return;
+
+       va_start(ap, format);
+
+       if (vsnprintf(str, sizeof(str), format, ap) > 0)
+               mux->debugf(str, mux->debug_data);
+
+       va_end(ap);
+}
+
+static void dispatch_sources(GAtMuxChannel *channel, GIOCondition condition)
+{
+       GAtMuxWatch *source;
+       GSList *c;
+       GSList *p;
+       GSList *t;
+
+       p = NULL;
+       c = channel->sources;
+
+       while (c) {
+               gboolean destroy = FALSE;
+
+               source = c->data;
+
+               debug(channel->mux, "checking source: %p", source);
+
+               if (condition & source->condition) {
+                       gpointer user_data = NULL;
+                       GSourceFunc callback = NULL;
+                       GSourceCallbackFuncs *cb_funcs;
+                       gpointer cb_data;
+                       gboolean (*dispatch) (GSource *, GSourceFunc, gpointer);
+
+                       debug(channel->mux, "dispatching source: %p", source);
+
+                       dispatch = source->source.source_funcs->dispatch;
+                       cb_funcs = source->source.callback_funcs;
+                       cb_data = source->source.callback_data;
+
+                       if (cb_funcs)
+                               cb_funcs->ref(cb_data);
+
+                       if (cb_funcs)
+                               cb_funcs->get(cb_data, (GSource *) source,
+                                               &callback, &user_data);
+
+                       destroy = !dispatch((GSource *) source, callback,
+                                               user_data);
+
+                       if (cb_funcs)
+                               cb_funcs->unref(cb_data);
+               }
+
+               if (destroy) {
+                       debug(channel->mux, "removing source: %p", source);
+
+                       g_source_destroy((GSource *) source);
+
+                       if (p)
+                               p->next = c->next;
+                       else
+                               channel->sources = c->next;
+
+                       t = c;
+                       c = c->next;
+                       g_slist_free_1(t);
+               } else {
+                       p = c;
+                       c = c->next;
+               }
+       }
+}
+
+static gboolean received_data(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer data)
+{
+       GAtMux *mux = data;
+       int i;
+       GIOStatus status;
+       gsize bytes_read;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       debug(mux, "received data");
+
+       bytes_read = 0;
+       status = g_io_channel_read_chars(mux->channel, mux->buf + mux->buf_used,
+                                       sizeof(mux->buf) - mux->buf_used,
+                                       &bytes_read, NULL);
+
+       mux->buf_used += bytes_read;
+
+       if (bytes_read > 0 && mux->driver->feed_data) {
+               int nread;
+
+               memset(mux->newdata, 0, BITMAP_SIZE);
+
+               nread = mux->driver->feed_data(mux, mux->buf, mux->buf_used);
+               mux->buf_used -= nread;
+
+               if (mux->buf_used > 0)
+                       memmove(mux->buf, mux->buf + nread, mux->buf_used);
+
+               for (i = 1; i <= MAX_CHANNELS; i++) {
+                       int offset = i / 8;
+                       int bit = i % 8;
+
+                       if (!(mux->newdata[offset] & (1 << bit)))
+                               continue;
+
+                       debug(mux, "dispatching sources for channel: %p",
+                               mux->dlcs[i-1]);
+
+                       dispatch_sources(mux->dlcs[i-1], G_IO_IN);
+               }
+       }
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+               return FALSE;
+
+       if (mux->buf_used == sizeof(mux->buf))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void write_watcher_destroy_notify(gpointer user_data)
+{
+       GAtMux *mux = user_data;
+
+       mux->write_watch = 0;
+}
+
+static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
+                               gpointer data)
+{
+       GAtMux *mux = data;
+       int dlc;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       debug(mux, "can write data");
+
+       for (dlc = 0; dlc < MAX_CHANNELS; dlc += 1) {
+               GAtMuxChannel *channel = mux->dlcs[dlc];
+
+               if (channel == NULL)
+                       continue;
+
+               debug(mux, "checking channel for write: %p", channel);
+
+               if (channel->throttled)
+                       continue;
+
+               debug(mux, "dispatching write sources: %p", channel);
+
+               dispatch_sources(channel, G_IO_OUT);
+       }
+
+       for (dlc = 0; dlc < MAX_CHANNELS; dlc += 1) {
+               GAtMuxChannel *channel = mux->dlcs[dlc];
+               GSList *l;
+               GAtMuxWatch *source;
+
+               if (channel == NULL)
+                       continue;
+
+               if (channel->throttled)
+                       continue;
+
+               for (l = channel->sources; l; l = l->next) {
+                       source = l->data;
+
+                       if (source->condition & G_IO_OUT)
+                               return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static void wakeup_writer(GAtMux *mux)
+{
+       if (mux->write_watch != 0)
+               return;
+
+       debug(mux, "waking up writer");
+
+       mux->write_watch = g_io_add_watch_full(mux->channel,
+                               G_PRIORITY_DEFAULT,
+                               G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               can_write_data, mux,
+                               write_watcher_destroy_notify);
+}
+
+int g_at_mux_raw_write(GAtMux *mux, const void *data, int towrite)
+{
+       gssize count = towrite;
+       gsize bytes_written;
+
+       g_io_channel_write_chars(mux->channel, (gchar *) data,
+                                       count, &bytes_written, NULL);
+
+       return bytes_written;
+}
+
+void g_at_mux_feed_dlc_data(GAtMux *mux, guint8 dlc,
+                               const void *data, int tofeed)
+{
+       GAtMuxChannel *channel;
+
+       int written;
+       int offset;
+       int bit;
+
+       debug(mux, "deliver_data: dlc: %hu", dlc);
+
+       if (dlc < 1 || dlc > MAX_CHANNELS)
+               return;
+
+       channel = mux->dlcs[dlc-1];
+
+       if (channel == NULL)
+               return;
+
+       written = ring_buffer_write(channel->buffer, data, tofeed);
+
+       if (written < 0)
+               return;
+
+       offset = dlc / 8;
+       bit = dlc % 8;
+
+       mux->newdata[offset] |= 1 << bit;
+       channel->condition |= G_IO_IN;
+}
+
+void g_at_mux_set_dlc_status(GAtMux *mux, guint8 dlc, int status)
+{
+       GAtMuxChannel *channel;
+
+       debug(mux, "got status %d, for channel %hu", status, dlc);
+
+       if (dlc < 1 || dlc > MAX_CHANNELS)
+               return;
+
+       channel = mux->dlcs[dlc-1];
+       if (channel == NULL)
+               return;
+
+       if (status & G_AT_MUX_DLC_STATUS_RTR) {
+               GSList *l;
+
+               mux->dlcs[dlc-1]->throttled = FALSE;
+               debug(mux, "setting throttled to FALSE");
+
+               for (l = mux->dlcs[dlc-1]->sources; l; l = l->next) {
+                       GAtMuxWatch *source = l->data;
+
+                       if (source->condition & G_IO_OUT) {
+                               wakeup_writer(mux);
+                               break;
+                       }
+               }
+       } else
+               mux->dlcs[dlc-1]->throttled = TRUE;
+}
+
+void g_at_mux_set_data(GAtMux *mux, void *data)
+{
+       if (mux == NULL)
+               return;
+
+       mux->driver_data = data;
+}
+
+void *g_at_mux_get_data(GAtMux *mux)
+{
+       if (mux == NULL)
+               return NULL;
+
+       return mux->driver_data;
+}
+
+static gboolean watch_check(GSource *source)
+{
+       return FALSE;
+}
+
+static gboolean watch_prepare(GSource *source, gint *timeout)
+{
+       *timeout = -1;
+       return FALSE;
+}
+
+static gboolean watch_dispatch(GSource *source, GSourceFunc callback,
+                                                       gpointer user_data)
+{
+       GIOFunc func = (GIOFunc) callback;
+       GAtMuxWatch *watch = (GAtMuxWatch *) source;
+       GAtMuxChannel *channel = (GAtMuxChannel *) watch->channel;
+
+       if (func == NULL)
+               return FALSE;
+
+       return func(watch->channel, channel->condition & watch->condition,
+                                                               user_data);
+}
+
+static void watch_finalize(GSource *source)
+{
+       GAtMuxWatch *watch = (GAtMuxWatch *) source;
+
+       g_io_channel_unref(watch->channel);
+}
+
+static GSourceFuncs watch_funcs = {
+       watch_prepare,
+       watch_check,
+       watch_dispatch,
+       watch_finalize
+};
+
+static GIOStatus channel_read(GIOChannel *channel, gchar *buf, gsize count,
+                                       gsize *bytes_read, GError **err)
+{
+       GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel;
+       unsigned int avail = ring_buffer_len_no_wrap(mux_channel->buffer);
+
+       if (avail > count)
+               avail = count;
+
+       *bytes_read = ring_buffer_read(mux_channel->buffer, buf, avail);
+
+       if (*bytes_read == 0)
+               return G_IO_STATUS_AGAIN;
+
+       return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus channel_write(GIOChannel *channel, const gchar *buf,
+                               gsize count, gsize *bytes_written, GError **err)
+{
+       GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel;
+       GAtMux *mux = mux_channel->mux;
+
+       if (mux->driver->write)
+               mux->driver->write(mux, mux_channel->dlc, buf, count);
+       *bytes_written = count;
+
+       return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus channel_seek(GIOChannel *channel, gint64 offset,
+                                               GSeekType type, GError **err)
+{
+       return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus channel_close(GIOChannel *channel, GError **err)
+{
+       GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel;
+       GAtMux *mux = mux_channel->mux;
+
+       debug(mux, "closing channel: %d", mux_channel->dlc);
+
+       dispatch_sources(mux_channel, G_IO_NVAL);
+
+       if (mux->driver->close_dlc)
+               mux->driver->close_dlc(mux, mux_channel->dlc);
+
+       mux->dlcs[mux_channel->dlc - 1] = NULL;
+
+       return G_IO_STATUS_NORMAL;
+}
+
+static void channel_free(GIOChannel *channel)
+{
+       GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel;
+
+       ring_buffer_free(mux_channel->buffer);
+
+       g_free(channel);
+}
+
+static GSource *channel_create_watch(GIOChannel *channel,
+                                               GIOCondition condition)
+{
+       GSource *source;
+       GAtMuxWatch *watch;
+       GAtMuxChannel *dlc = (GAtMuxChannel *) channel;
+       GAtMux *mux = dlc->mux;
+
+       source = g_source_new(&watch_funcs, sizeof(GAtMuxWatch));
+       watch = (GAtMuxWatch *) source;
+
+       watch->channel = channel;
+       g_io_channel_ref(channel);
+
+       watch->condition = condition;
+
+       if ((watch->condition & G_IO_OUT) && dlc->throttled == FALSE)
+               wakeup_writer(mux);
+
+       debug(mux, "creating source: %p, channel: %p, writer: %d, reader: %d",
+                       watch, channel,
+                       condition & G_IO_OUT,
+                       condition & G_IO_IN);
+
+       dlc->sources = g_slist_prepend(dlc->sources, watch);
+
+       return source;
+}
+
+static GIOStatus channel_set_flags(GIOChannel *channel, GIOFlags flags,
+                                                               GError **err)
+{
+       return G_IO_STATUS_NORMAL;
+}
+
+static GIOFlags channel_get_flags(GIOChannel *channel)
+{
+       GIOFlags flags = 0;
+
+       return flags;
+}
+
+static GIOFuncs channel_funcs = {
+       channel_read,
+       channel_write,
+       channel_seek,
+       channel_close,
+       channel_create_watch,
+       channel_free,
+       channel_set_flags,
+       channel_get_flags,
+};
+
+GAtMux *g_at_mux_new(GIOChannel *channel, const GAtMuxDriver *driver)
+{
+       GAtMux *mux;
+
+       if (channel == NULL)
+               return NULL;
+
+       mux = g_try_new0(GAtMux, 1);
+       if (mux == NULL)
+               return NULL;
+
+       mux->ref_count = 1;
+       mux->driver = driver;
+       mux->shutdown = TRUE;
+
+       mux->channel = channel;
+       g_io_channel_ref(channel);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       return mux;
+}
+
+GAtMux *g_at_mux_ref(GAtMux *mux)
+{
+       if (mux == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&mux->ref_count);
+
+       return mux;
+}
+
+void g_at_mux_unref(GAtMux *mux)
+{
+       if (mux == NULL)
+               return;
+
+       if (g_atomic_int_dec_and_test(&mux->ref_count)) {
+               g_at_mux_shutdown(mux);
+
+               g_io_channel_unref(mux->channel);
+
+               if (mux->driver->remove)
+                       mux->driver->remove(mux);
+
+               g_free(mux);
+       }
+}
+
+gboolean g_at_mux_start(GAtMux *mux)
+{
+       if (mux->channel == NULL)
+               return FALSE;
+
+       if (mux->driver->startup == NULL)
+               return FALSE;
+
+       if (mux->driver->startup(mux) == FALSE)
+               return FALSE;
+
+       mux->read_watch = g_io_add_watch_full(mux->channel, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                               received_data, mux, NULL);
+
+       mux->shutdown = FALSE;
+
+       return TRUE;
+}
+
+gboolean g_at_mux_shutdown(GAtMux *mux)
+{
+       int i;
+
+       if (mux->shutdown == TRUE)
+               return FALSE;
+
+       if (mux->channel == NULL)
+               return FALSE;
+
+       if (mux->read_watch > 0)
+               g_source_remove(mux->read_watch);
+
+       for (i = 0; i < MAX_CHANNELS; i++) {
+               if (mux->dlcs[i] == NULL)
+                       continue;
+
+               channel_close((GIOChannel *) mux->dlcs[i], NULL);
+       }
+
+       if (mux->driver->shutdown)
+               mux->driver->shutdown(mux);
+
+       mux->shutdown = TRUE;
+
+       return TRUE;
+}
+
+gboolean g_at_mux_set_disconnect_function(GAtMux *mux,
+                       GAtDisconnectFunc disconnect, gpointer user_data)
+{
+       if (mux == NULL)
+               return FALSE;
+
+       mux->user_disconnect = disconnect;
+       mux->user_disconnect_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_at_mux_set_debug(GAtMux *mux, GAtDebugFunc func, gpointer user_data)
+{
+       if (mux == NULL)
+               return FALSE;
+
+       mux->debugf = func;
+       mux->debug_data = user_data;
+
+       return TRUE;
+}
+
+GIOChannel *g_at_mux_create_channel(GAtMux *mux)
+{
+       GAtMuxChannel *mux_channel;
+       GIOChannel *channel;
+       int i;
+
+       for (i = 0; i < MAX_CHANNELS; i++) {
+               if (mux->dlcs[i] == NULL)
+                       break;
+       }
+
+       if (i == MAX_CHANNELS)
+               return NULL;
+
+       mux_channel = g_try_new0(GAtMuxChannel, 1);
+       if (mux_channel == NULL)
+               return NULL;
+
+       if (mux->driver->open_dlc)
+               mux->driver->open_dlc(mux, i+1);
+
+       channel = (GIOChannel *) mux_channel;
+
+       g_io_channel_init(channel);
+       channel->close_on_unref = TRUE;
+       channel->funcs = &channel_funcs;
+
+       channel->is_seekable = FALSE;
+       channel->is_readable = TRUE;
+       channel->is_writeable = TRUE;
+
+       channel->do_encode = FALSE;
+
+       mux_channel->mux = mux;
+       mux_channel->dlc = i+1;
+       mux_channel->buffer = ring_buffer_new(MUX_CHANNEL_BUFFER_SIZE);
+       mux_channel->throttled = FALSE;
+
+       mux->dlcs[i] = mux_channel;
+
+       debug(mux, "created channel %p, dlc: %d", channel, i+1);
+
+       return channel;
+}
+
+static void msd_free(gpointer user_data)
+{
+       struct mux_setup_data *msd = user_data;
+
+       if (msd->chat)
+               g_at_chat_unref(msd->chat);
+
+       g_free(msd);
+}
+
+static void mux_setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct mux_setup_data *msd = user_data;
+       GIOFlags flags;
+       GIOChannel *channel;
+       GAtMux *mux = NULL;
+
+       if (!ok)
+               goto error;
+
+       channel = g_at_chat_get_channel(msd->chat);
+       channel = g_io_channel_ref(channel);
+
+       g_at_chat_unref(msd->chat);
+       msd->chat = NULL;
+
+       flags = g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK;
+       g_io_channel_set_flags(channel, flags, NULL);
+
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       if (msd->mode == 0)
+               mux = g_at_mux_new_gsm0710_basic(channel, msd->frame_size);
+       else
+               mux = g_at_mux_new_gsm0710_advanced(channel, msd->frame_size);
+
+       g_io_channel_unref(channel);
+
+error:
+       msd->func(mux, msd->user);
+
+       if (msd->destroy)
+               msd->destroy(msd->user);
+}
+
+static void mux_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct mux_setup_data *msd = user_data;
+       struct mux_setup_data *nmsd;
+       GAtResultIter iter;
+       int min, max;
+       int speed;
+       char buf[64];
+
+       /* CMUX query not supported, abort */
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CMUX:"))
+               goto error;
+
+       /* Mode */
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       if (!g_at_result_iter_next_range(&iter, &min, &max))
+               goto error;
+
+       if (!g_at_result_iter_close_list(&iter))
+               goto error;
+
+       if (min <= 1 && 1 <= max)
+               msd->mode = 1;
+       else if (min <= 0 && 0 <= max)
+               msd->mode = 0;
+       else
+               goto error;
+
+       /* Subset */
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       if (!g_at_result_iter_next_range(&iter, &min, &max))
+               goto error;
+
+       if (!g_at_result_iter_close_list(&iter))
+               goto error;
+
+       if (min > 0)
+               goto error;
+
+       /* Speed, pick highest */
+       if (g_at_result_iter_open_list(&iter)) {
+               if (!g_at_result_iter_next_range(&iter, &min, &max))
+                       goto error;
+
+               if (!g_at_result_iter_close_list(&iter))
+                       goto error;
+
+               speed = max;
+       } else {
+               if (!g_at_result_iter_skip_next(&iter))
+                       goto error;
+
+               /* not available/used */
+               speed = -1;
+       }
+
+       /* Frame size, pick defaults */
+       if (!g_at_result_iter_open_list(&iter))
+               goto error;
+
+       if (!g_at_result_iter_next_range(&iter, &min, &max))
+               goto error;
+
+       if (!g_at_result_iter_close_list(&iter))
+               goto error;
+
+       if (msd->mode == 0) {
+               if (min > 31 || max < 31)
+                       goto error;
+
+               msd->frame_size = 31;
+       } else if (msd->mode == 1) {
+               if (min > 64 || max < 64)
+                       goto error;
+
+               msd->frame_size = 64;
+       } else
+               goto error;
+
+       nmsd = g_memdup(msd, sizeof(struct mux_setup_data));
+       g_at_chat_ref(nmsd->chat);
+
+       if (speed < 0)
+               sprintf(buf, "AT+CMUX=%u,0,,%u", msd->mode, msd->frame_size);
+       else
+               sprintf(buf, "AT+CMUX=%u,0,%u,%u", msd->mode, speed,
+                                                       msd->frame_size);
+
+       if (g_at_chat_send(msd->chat, buf, none_prefix,
+                               mux_setup_cb, nmsd, msd_free) > 0)
+               return;
+
+       msd_free(nmsd);
+
+error:
+       msd->func(NULL, msd->user);
+
+       if (msd->destroy)
+               msd->destroy(msd->user);
+}
+
+gboolean g_at_mux_setup_gsm0710(GAtChat *chat,
+                               GAtMuxSetupFunc notify, gpointer user_data,
+                               GDestroyNotify destroy)
+{
+       struct mux_setup_data *msd;
+
+       if (chat == NULL)
+               return FALSE;
+
+       if (notify == NULL)
+               return FALSE;
+
+       msd = g_new0(struct mux_setup_data, 1);
+
+       msd->chat = g_at_chat_ref(chat);
+       msd->func = notify;
+       msd->user = user_data;
+       msd->destroy = destroy;
+
+       if (g_at_chat_send(chat, "AT+CMUX=?", cmux_prefix,
+                               mux_query_cb, msd, msd_free) > 0)
+               return TRUE;
+
+       if (msd)
+               msd_free(msd);
+
+       return FALSE;
+}
+
+#define GSM0710_BUFFER_SIZE 4096
+
+struct gsm0710_data {
+       int frame_size;
+};
+
+/* Process an incoming GSM 07.10 packet */
+static gboolean gsm0710_packet(GAtMux *mux, int dlc, guint8 control,
+                               const unsigned char *data, int len,
+                               GAtMuxWriteFrame write_frame)
+{
+       if (control == 0xEF || control == 0x03) {
+               if (dlc >= 1 && dlc <= 63) {
+                       g_at_mux_feed_dlc_data(mux, dlc, data, len);
+                       return TRUE;
+               }
+
+               if (dlc == 0) {
+                       /* An embedded command or response on channel 0 */
+                       if (len >= 2 && data[0] == GSM0710_STATUS_SET) {
+                               return gsm0710_packet(mux, dlc,
+                                                       GSM0710_STATUS_ACK,
+                                                       data + 2, len - 2,
+                                                       write_frame);
+                       } else if (len >= 2 && data[0] == 0x43) {
+                               /* Test command from other side - send the same bytes back */
+                               unsigned char *resp = alloca(len);
+                               memcpy(resp, data, len);
+                               resp[0] = 0x41; /* Clear the C/R bit in the response */
+                               write_frame(mux, 0, GSM0710_DATA, resp, len);
+                       }
+               }
+       } else if (control == GSM0710_STATUS_ACK && dlc == 0) {
+               unsigned char resp[33];
+
+               /* Status change message */
+               if (len >= 2) {
+                       /* Handle status changes on other channels */
+                       dlc = ((data[0] & 0xFC) >> 2);
+
+                       if (dlc >= 1 && dlc <= 63)
+                               g_at_mux_set_dlc_status(mux, dlc, data[1]);
+               }
+
+               /* Send the response to the status change request to ACK it */
+               debug(mux, "received status line signal, sending response");
+               if (len > 31)
+                       len = 31;
+               resp[0] = GSM0710_STATUS_ACK;
+               resp[1] = ((len << 1) | 0x01);
+               memcpy(resp + 2, data, len);
+               write_frame(mux, 0, GSM0710_DATA, resp, len + 2);
+       }
+
+       return TRUE;
+}
+
+static void gsm0710_basic_write_frame(GAtMux *mux, guint8 dlc, guint8 control,
+                                       const guint8 *data, int towrite)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+       guint8 *frame = alloca(gd->frame_size + 7);
+       int frame_size;
+
+       frame_size = gsm0710_basic_fill_frame(frame, dlc, control,
+                                               data, towrite);
+       g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+#define COMPOSE_STATUS_FRAME(data, dlc, status)        \
+       guint8 data[4];                         \
+       data[0] = GSM0710_STATUS_SET;           \
+       data[1] = 0x03;                         \
+       data[2] = ((dlc << 2) | 0x03);          \
+       data[3] = status
+
+static void gsm0710_basic_remove(GAtMux *mux)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+
+       g_free(gd);
+       g_at_mux_set_data(mux, NULL);
+}
+
+static gboolean gsm0710_basic_startup(GAtMux *mux)
+{
+       guint8 frame[6];
+       int frame_size;
+
+       frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_OPEN_CHANNEL,
+                                               NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static gboolean gsm0710_basic_shutdown(GAtMux *mux)
+{
+       guint8 frame[6];
+       int frame_size;
+
+       frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_CLOSE_CHANNEL,
+                                               NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static gboolean gsm0710_basic_open_dlc(GAtMux *mux, guint8 dlc)
+{
+       guint8 frame[6];
+       int frame_size;
+
+       frame_size = gsm0710_basic_fill_frame(frame, dlc, GSM0710_OPEN_CHANNEL,
+                                               NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static gboolean gsm0710_basic_close_dlc(GAtMux *mux, guint8 dlc)
+{
+       guint8 frame[6];
+       int frame_size;
+
+       frame_size = gsm0710_basic_fill_frame(frame, dlc, GSM0710_CLOSE_CHANNEL,
+                                               NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static int gsm0710_basic_feed_data(GAtMux *mux, void *data, int len)
+{
+       int total = 0;
+       int nread;
+       guint8 dlc;
+       guint8 ctrl;
+       guint8 *frame;
+       int frame_len;
+
+       do {
+               frame = NULL;
+               nread = gsm0710_basic_extract_frame(data, len, &dlc, &ctrl,
+                                                       &frame, &frame_len);
+
+               total += nread;
+               data += nread;
+               len -= nread;
+
+               if (frame == NULL)
+                       break;
+
+               gsm0710_packet(mux, dlc, ctrl, frame, frame_len,
+                               gsm0710_basic_write_frame);
+       } while (nread > 0);
+
+       return total;
+}
+
+static void gsm0710_basic_set_status(GAtMux *mux, guint8 dlc, guint8 status)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+       guint8 *frame = alloca(gd->frame_size + 7);
+       int frame_size;
+
+       COMPOSE_STATUS_FRAME(data, dlc, status);
+       frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_DATA, data, 4);
+       g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+static void gsm0710_basic_write(GAtMux *mux, guint8 dlc,
+                               const void *data, int towrite)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+       guint8 *frame = alloca(gd->frame_size + 7);
+       int max;
+       int frame_size;
+
+       while (towrite > 0) {
+               max = MIN(towrite, gd->frame_size);
+               frame_size = gsm0710_basic_fill_frame(frame, dlc,
+                                               GSM0710_DATA, data, max);
+               g_at_mux_raw_write(mux, frame, frame_size);
+               data = data + max;
+               towrite -= max;
+       }
+}
+
+static GAtMuxDriver gsm0710_basic_driver = {
+       .remove = gsm0710_basic_remove,
+       .startup = gsm0710_basic_startup,
+       .shutdown = gsm0710_basic_shutdown,
+       .open_dlc = gsm0710_basic_open_dlc,
+       .close_dlc = gsm0710_basic_close_dlc,
+       .feed_data = gsm0710_basic_feed_data,
+       .set_status = gsm0710_basic_set_status,
+       .write = gsm0710_basic_write,
+};
+
+GAtMux *g_at_mux_new_gsm0710_basic(GIOChannel *channel, int frame_size)
+{
+       GAtMux *mux;
+       struct gsm0710_data *gd;
+
+       mux = g_at_mux_new(channel, &gsm0710_basic_driver);
+
+       if (mux == NULL)
+               return NULL;
+
+       gd = g_new0(struct gsm0710_data, 1);
+       gd->frame_size = frame_size;
+
+       g_at_mux_set_data(mux, gd);
+
+       return mux;
+}
+
+static void gsm0710_advanced_write_frame(GAtMux *mux, guint8 dlc, guint8 control,
+                                       const guint8 *data, int towrite)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+       guint8 *frame = alloca(gd->frame_size * 2 + 7);
+       int frame_size;
+
+       frame_size = gsm0710_advanced_fill_frame(frame, dlc, control,
+                                                       data, towrite);
+       g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+static void gsm0710_advanced_remove(GAtMux *mux)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+
+       g_free(gd);
+       g_at_mux_set_data(mux, NULL);
+}
+
+static gboolean gsm0710_advanced_startup(GAtMux *mux)
+{
+       guint8 frame[8]; /* Account for escapes */
+       int frame_size;
+
+       frame_size = gsm0710_advanced_fill_frame(frame, 0,
+                                               GSM0710_OPEN_CHANNEL, NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static gboolean gsm0710_advanced_shutdown(GAtMux *mux)
+{
+       guint8 frame[8]; /* Account for escapes */
+       int frame_size;
+
+       frame_size = gsm0710_advanced_fill_frame(frame, 0,
+                                               GSM0710_CLOSE_CHANNEL, NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static gboolean gsm0710_advanced_open_dlc(GAtMux *mux, guint8 dlc)
+{
+       guint8 frame[8]; /* Account for escapes */
+       int frame_size;
+
+       frame_size = gsm0710_advanced_fill_frame(frame, dlc,
+                                               GSM0710_OPEN_CHANNEL, NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static gboolean gsm0710_advanced_close_dlc(GAtMux *mux, guint8 dlc)
+{
+       guint8 frame[8]; /* Account for escapes */
+       int frame_size;
+
+       frame_size = gsm0710_advanced_fill_frame(frame, dlc,
+                                               GSM0710_CLOSE_CHANNEL, NULL, 0);
+       g_at_mux_raw_write(mux, frame, frame_size);
+
+       return TRUE;
+}
+
+static int gsm0710_advanced_feed_data(GAtMux *mux, void *data, int len)
+{
+       int total = 0;
+       int nread;
+       guint8 dlc;
+       guint8 ctrl;
+       guint8 *frame;
+       int frame_len;
+
+       do {
+               frame = NULL;
+               nread = gsm0710_advanced_extract_frame(data, len, &dlc, &ctrl,
+                                                       &frame, &frame_len);
+
+               total += nread;
+               data += nread;
+               len -= nread;
+
+               if (frame == NULL)
+                       break;
+
+               gsm0710_packet(mux, dlc, ctrl, frame, frame_len,
+                               gsm0710_advanced_write_frame);
+       } while (nread > 0);
+
+       return total;
+}
+
+static void gsm0710_advanced_set_status(GAtMux *mux, guint8 dlc, guint8 status)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+       guint8 *frame = alloca(gd->frame_size * 2 + 7);
+       int frame_size;
+
+       COMPOSE_STATUS_FRAME(data, dlc, status);
+       frame_size = gsm0710_advanced_fill_frame(frame, 0,
+                                                       GSM0710_DATA, data, 4);
+       g_at_mux_raw_write(mux, frame, frame_size);
+}
+
+static void gsm0710_advanced_write(GAtMux *mux, guint8 dlc,
+                                       const void *data, int towrite)
+{
+       struct gsm0710_data *gd = g_at_mux_get_data(mux);
+       guint8 *frame = alloca(gd->frame_size * 2 + 7);
+       int max;
+       int frame_size;
+
+       while (towrite > 0) {
+               max = MIN(towrite, gd->frame_size);
+               frame_size = gsm0710_advanced_fill_frame(frame, dlc,
+                                               GSM0710_DATA, data, max);
+               g_at_mux_raw_write(mux, frame, frame_size);
+               data = data + max;
+               towrite -= max;
+       }
+}
+
+static GAtMuxDriver gsm0710_advanced_driver = {
+       .remove = gsm0710_advanced_remove,
+       .startup = gsm0710_advanced_startup,
+       .shutdown = gsm0710_advanced_shutdown,
+       .open_dlc = gsm0710_advanced_open_dlc,
+       .close_dlc = gsm0710_advanced_close_dlc,
+       .feed_data = gsm0710_advanced_feed_data,
+       .set_status = gsm0710_advanced_set_status,
+       .write = gsm0710_advanced_write,
+};
+
+GAtMux *g_at_mux_new_gsm0710_advanced(GIOChannel *channel, int frame_size)
+{
+       GAtMux *mux;
+       struct gsm0710_data *gd;
+
+       mux = g_at_mux_new(channel, &gsm0710_advanced_driver);
+
+       if (mux == NULL)
+               return NULL;
+
+       gd = g_new0(struct gsm0710_data, 1);
+       gd->frame_size = frame_size;
+
+       g_at_mux_set_data(mux, gd);
+
+       return mux;
+}
diff --git a/gatchat/gatmux.h b/gatchat/gatmux.h
new file mode 100644 (file)
index 0000000..4d77c72
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009  Trolltech ASA.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATMUX_H
+#define __GATMUX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gatchat.h"
+
+struct _GAtMux;
+
+typedef struct _GAtMux GAtMux;
+typedef struct _GAtMuxDriver GAtMuxDriver;
+typedef enum _GAtMuxChannelStatus GAtMuxChannelStatus;
+typedef void (*GAtMuxSetupFunc)(GAtMux *mux, gpointer user_data);
+
+enum _GAtMuxDlcStatus {
+       G_AT_MUX_DLC_STATUS_RTC = 0x02,
+       G_AT_MUX_DLC_STATUS_RTR = 0x04,
+       G_AT_MUX_DLC_STATUS_IC = 0x08,
+       G_AT_MUX_DLC_STATUS_DV = 0x80,
+};
+
+struct _GAtMuxDriver {
+       void (*remove)(GAtMux *mux);
+       gboolean (*startup)(GAtMux *mux);
+       gboolean (*shutdown)(GAtMux *mux);
+       gboolean (*open_dlc)(GAtMux *mux, guint8 dlc);
+       gboolean (*close_dlc)(GAtMux *mux, guint8 dlc);
+       void (*set_status)(GAtMux *mux, guint8 dlc, guint8 status);
+       void (*write)(GAtMux *mux, guint8 dlc, const void *data, int towrite);
+       int (*feed_data)(GAtMux *mux, void *data, int len);
+};
+
+GAtMux *g_at_mux_new(GIOChannel *channel, const GAtMuxDriver *driver);
+GAtMux *g_at_mux_new_gsm0710_basic(GIOChannel *channel, int framesize);
+GAtMux *g_at_mux_new_gsm0710_advanced(GIOChannel *channel, int framesize);
+
+GAtMux *g_at_mux_ref(GAtMux *mux);
+void g_at_mux_unref(GAtMux *mux);
+
+gboolean g_at_mux_start(GAtMux *mux);
+gboolean g_at_mux_shutdown(GAtMux *mux);
+
+gboolean g_at_mux_set_disconnect_function(GAtMux *mux,
+                       GAtDisconnectFunc disconnect, gpointer user_data);
+
+gboolean g_at_mux_set_debug(GAtMux *mux, GAtDebugFunc func, gpointer user_data);
+
+GIOChannel *g_at_mux_create_channel(GAtMux *mux);
+
+/*!
+ * Multiplexer driver integration functions
+ */
+void g_at_mux_set_dlc_status(GAtMux *mux, guint8 dlc, int status);
+void g_at_mux_feed_dlc_data(GAtMux *mux, guint8 dlc,
+                               const void *data, int tofeed);
+
+int g_at_mux_raw_write(GAtMux *mux, const void *data, int towrite);
+
+void g_at_mux_set_data(GAtMux *mux, void *data);
+void *g_at_mux_get_data(GAtMux *mux);
+
+/*!
+ * Uses the passed in GAtChat to setup a GSM 07.10 style multiplexer on the
+ * channel used by GAtChat.  This function queries the multiplexer capability,
+ * preferring advanced mode over basic.  If supported, the best available
+ * multiplexer mode is entered.  If this is successful, the chat is
+ * shutdown and unrefed.  The chat's channel will be transferred to the
+ * resulting multiplexer object.
+ */
+gboolean g_at_mux_setup_gsm0710(GAtChat *chat,
+                               GAtMuxSetupFunc notify, gpointer user_data,
+                               GDestroyNotify destroy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATMUX_H */
diff --git a/gatchat/gatppp.c b/gatchat/gatppp.c
new file mode 100644 (file)
index 0000000..f767f4a
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include "gatutil.h"
+#include "gathdlc.h"
+#include "gatppp.h"
+#include "crc-ccitt.h"
+#include "ppp.h"
+
+#define DEFAULT_MRU    1500
+#define DEFAULT_MTU    1500
+
+#define PPP_ADDR_FIELD 0xff
+#define PPP_CTRL       0x03
+
+#define GUARD_TIMEOUTS 1500
+
+enum ppp_phase {
+       PPP_PHASE_DEAD = 0,             /* Link dead */
+       PPP_PHASE_ESTABLISHMENT,        /* LCP started */
+       PPP_PHASE_AUTHENTICATION,       /* Auth started */
+       PPP_PHASE_NETWORK,              /* IPCP started */
+       PPP_PHASE_LINK_UP,              /* IPCP negotiation ok, link up */
+       PPP_PHASE_TERMINATION,          /* LCP Terminate phase */
+};
+
+struct _GAtPPP {
+       gint ref_count;
+       enum ppp_phase phase;
+       struct pppcp_data *lcp;
+       struct pppcp_data *ipcp;
+       struct ppp_net *net;
+       struct ppp_chap *chap;
+       GAtHDLC *hdlc;
+       gint mru;
+       gint mtu;
+       char username[256];
+       char password[256];
+       GAtPPPConnectFunc connect_cb;
+       gpointer connect_data;
+       GAtPPPDisconnectFunc disconnect_cb;
+       gpointer disconnect_data;
+       GAtPPPDisconnectReason disconnect_reason;
+       GAtDebugFunc debugf;
+       gpointer debug_data;
+       gboolean sta_pending;
+       guint ppp_dead_source;
+       GAtSuspendFunc suspend_func;
+       gpointer suspend_data;
+       int fd;
+       guint guard_timeout_source;
+       gboolean suspended;
+       gboolean xmit_acfc;
+       gboolean xmit_pfc;
+};
+
+void ppp_debug(GAtPPP *ppp, const char *str)
+{
+       if (ppp == NULL || ppp->debugf == NULL)
+               return;
+
+       ppp->debugf(str, ppp->debug_data);
+}
+
+static gboolean ppp_dead(gpointer userdata)
+{
+       GAtPPP *ppp = userdata;
+
+       DBG(ppp, "");
+
+       ppp->ppp_dead_source = 0;
+
+       /* notify interested parties */
+       if (ppp->disconnect_cb)
+               ppp->disconnect_cb(ppp->disconnect_reason,
+                                       ppp->disconnect_data);
+
+       return FALSE;
+}
+
+static void sta_sent(gpointer userdata)
+{
+       GAtPPP *ppp = userdata;
+
+       DBG(ppp, "");
+
+       ppp->sta_pending = FALSE;
+
+       if (ppp->phase == PPP_PHASE_DEAD)
+               ppp_dead(ppp);
+}
+
+struct ppp_header *ppp_packet_new(gsize infolen, guint16 protocol)
+{
+       struct ppp_header *ppp_packet;
+
+       ppp_packet = g_try_malloc0(infolen + sizeof(*ppp_packet));
+       if (ppp_packet == NULL)
+               return NULL;
+
+       ppp_packet->proto = htons(protocol);
+       ppp_packet->address = PPP_ADDR_FIELD;
+       ppp_packet->control = PPP_CTRL;
+
+       return ppp_packet;
+}
+
+/*
+ * Silently discard packets which are received when they shouldn't be
+ */
+static inline gboolean ppp_drop_packet(GAtPPP *ppp, guint16 protocol)
+{
+       switch (ppp->phase) {
+       case PPP_PHASE_ESTABLISHMENT:
+       case PPP_PHASE_TERMINATION:
+               if (protocol != LCP_PROTOCOL)
+                       return TRUE;
+               break;
+       case PPP_PHASE_AUTHENTICATION:
+               if (protocol != LCP_PROTOCOL && protocol != CHAP_PROTOCOL)
+                       return TRUE;
+               break;
+       case PPP_PHASE_DEAD:
+               return TRUE;
+       case PPP_PHASE_NETWORK:
+               if (protocol != LCP_PROTOCOL && protocol != CHAP_PROTOCOL &&
+                                       protocol != IPCP_PROTO)
+                       return TRUE;
+               break;
+       case PPP_PHASE_LINK_UP:
+               break;
+       }
+
+       return FALSE;
+}
+
+static void ppp_receive(const unsigned char *buf, gsize len, void *data)
+{
+       GAtPPP *ppp = data;
+       unsigned int offset = 0;
+       guint16 protocol;
+       const guint8 *packet;
+
+       if (len == 0)
+               return;
+
+       if (buf[0] == PPP_ADDR_FIELD && len >= 2 && buf[1] == PPP_CTRL)
+               offset = 2;
+
+       if (len < offset + 1)
+               return;
+
+       /* From RFC 1661:
+        * the Protocol field uses an extension mechanism consistent with the
+        * ISO 3309 extension mechanism for the Address field; the Least
+        * Significant Bit (LSB) of each octet is used to indicate extension
+        * of the Protocol field.  A binary "0" as the LSB indicates that the
+        * Protocol field continues with the following octet.  The presence
+        * of a binary "1" as the LSB marks the last octet of the Protocol
+        * field.
+        *
+        * To check for compression we simply check the LSB of the first
+        * protocol byte.
+        */
+
+       if (buf[offset] & 0x1) {
+               protocol = buf[offset];
+               offset += 1;
+       } else {
+               if (len < offset + 2)
+                       return;
+
+               protocol = get_host_short(buf + offset);
+               offset += 2;
+       }
+
+       if (ppp_drop_packet(ppp, protocol))
+               return;
+
+       packet = buf + offset;
+
+       switch (protocol) {
+       case PPP_IP_PROTO:
+               ppp_net_process_packet(ppp->net, packet, len - offset);
+               break;
+       case LCP_PROTOCOL:
+               pppcp_process_packet(ppp->lcp, packet, len - offset);
+               break;
+       case IPCP_PROTO:
+               pppcp_process_packet(ppp->ipcp, packet, len - offset);
+               break;
+       case CHAP_PROTOCOL:
+               if (ppp->chap) {
+                       ppp_chap_process_packet(ppp->chap, packet,
+                                                       len - offset);
+                       break;
+               }
+               /* fall through */
+       default:
+               pppcp_send_protocol_reject(ppp->lcp, buf, len);
+               break;
+       };
+}
+
+static void ppp_send_lcp_frame(GAtPPP *ppp, guint8 *packet, guint infolen)
+{
+       struct ppp_header *header = (struct ppp_header *) packet;
+       guint8 code;
+       guint32 xmit_accm = 0;
+       gboolean sta = FALSE;
+       gboolean lcp;
+
+       /*
+        * all LCP Link Configuration, Link Termination, and Code-Reject
+        * packets must be sent with the default sending ACCM
+        */
+       code = pppcp_get_code(packet);
+       lcp = code > 0 && code < 8;
+
+       /*
+        * If we're going down, we try to make sure to send the final
+        * ack before informing the upper layers via the ppp_disconnect
+        * function.  Once we enter PPP_DEAD phase, no further packets
+        * will be sent
+        */
+       if (code == PPPCP_CODE_TYPE_TERMINATE_ACK)
+               sta = TRUE;
+
+       if (lcp) {
+               xmit_accm = g_at_hdlc_get_xmit_accm(ppp->hdlc);
+               g_at_hdlc_set_xmit_accm(ppp->hdlc, ~0U);
+       }
+
+       header->address = PPP_ADDR_FIELD;
+       header->control = PPP_CTRL;
+
+       if (g_at_hdlc_send(ppp->hdlc, packet,
+                       infolen + sizeof(*header)) == TRUE) {
+               if (sta) {
+                       GAtIO *io = g_at_hdlc_get_io(ppp->hdlc);
+
+                       ppp->sta_pending = TRUE;
+                       g_at_io_set_write_done(io, sta_sent, ppp);
+               }
+       } else
+               DBG(ppp, "Failed to send a frame\n");
+
+       if (lcp)
+               g_at_hdlc_set_xmit_accm(ppp->hdlc, xmit_accm);
+}
+
+static void ppp_send_acfc_frame(GAtPPP *ppp, guint8 *packet,
+                                       guint infolen)
+{
+       struct ppp_header *header = (struct ppp_header *) packet;
+       guint offset = 0;
+
+       if (ppp->xmit_acfc)
+               offset = 2;
+
+       /* We remove the only address and control field */
+       if (g_at_hdlc_send(ppp->hdlc, packet + offset,
+                               infolen + sizeof(*header) - offset)
+                       == FALSE)
+               DBG(ppp, "Failed to send a frame\n");
+}
+
+static void ppp_send_acfc_pfc_frame(GAtPPP *ppp, guint8 *packet,
+                                       guint infolen)
+{
+       struct ppp_header *header = (struct ppp_header *) packet;
+       guint offset = 0;
+
+       if (ppp->xmit_acfc && ppp->xmit_pfc)
+               offset = 3;
+       else if (ppp->xmit_acfc)
+               offset = 2;
+       else if (ppp->xmit_pfc) {
+               /* Shuffle AC bytes in place of the first protocol byte */
+               packet[2] = packet[1];
+               packet[1] = packet[0];
+               offset = 1;
+       }
+
+       if (g_at_hdlc_send(ppp->hdlc, packet + offset,
+                               infolen + sizeof(*header) - offset)
+                       == FALSE)
+               DBG(ppp, "Failed to send a frame\n");
+}
+
+/*
+ * transmit out through the lower layer interface
+ *
+ * infolen - length of the information part of the packet
+ */
+void ppp_transmit(GAtPPP *ppp, guint8 *packet, guint infolen)
+{
+       guint16 proto = ppp_proto(packet);
+
+       if (proto == LCP_PROTOCOL) {
+               ppp_send_lcp_frame(ppp, packet, infolen);
+               return;
+       }
+
+       /*
+        * If the upper 8 bits of the protocol are 0, then send
+        * with PFC if enabled
+        */
+       if ((proto & 0xff00) == 0)
+               ppp_send_acfc_pfc_frame(ppp, packet, infolen);
+       else
+               ppp_send_acfc_frame(ppp, packet, infolen);
+}
+
+static inline void ppp_enter_phase(GAtPPP *ppp, enum ppp_phase phase)
+{
+       DBG(ppp, "%d", phase);
+       ppp->phase = phase;
+
+       if (phase == PPP_PHASE_DEAD && ppp->sta_pending == FALSE)
+               ppp->ppp_dead_source = g_idle_add(ppp_dead, ppp);
+}
+
+void ppp_set_auth(GAtPPP *ppp, const guint8* auth_data)
+{
+       guint16 proto = get_host_short(auth_data);
+
+       switch (proto) {
+       case CHAP_PROTOCOL:
+               if (ppp->chap)
+                       ppp_chap_free(ppp->chap);
+
+               ppp->chap = ppp_chap_new(ppp, auth_data[2]);
+               break;
+       default:
+               DBG(ppp, "unknown authentication proto");
+               break;
+       }
+}
+
+void ppp_auth_notify(GAtPPP *ppp, gboolean success)
+{
+       if (success == FALSE) {
+               ppp->disconnect_reason = G_AT_PPP_REASON_AUTH_FAIL;
+               pppcp_signal_close(ppp->lcp);
+               return;
+       }
+
+       ppp_enter_phase(ppp, PPP_PHASE_NETWORK);
+
+       /* Send UP & OPEN events to the IPCP layer */
+       pppcp_signal_open(ppp->ipcp);
+       pppcp_signal_up(ppp->ipcp);
+}
+
+void ppp_ipcp_up_notify(GAtPPP *ppp, const char *local, const char *peer,
+                                       const char *dns1, const char *dns2)
+{
+       ppp->net = ppp_net_new(ppp, ppp->fd);
+
+       /*
+        * ppp_net_new took control over the fd, whatever happens is out of
+        * our hands now
+        */
+       ppp->fd = -1;
+
+       if (ppp->net == NULL) {
+               ppp->disconnect_reason = G_AT_PPP_REASON_NET_FAIL;
+               pppcp_signal_close(ppp->lcp);
+               return;
+       }
+
+       if (ppp_net_set_mtu(ppp->net, ppp->mtu) == FALSE)
+               DBG(ppp, "Unable to set MTU");
+
+       ppp_enter_phase(ppp, PPP_PHASE_LINK_UP);
+
+       if (ppp->connect_cb)
+               ppp->connect_cb(ppp_net_get_interface(ppp->net),
+                                       local, peer, dns1, dns2,
+                                       ppp->connect_data);
+}
+
+void ppp_ipcp_down_notify(GAtPPP *ppp)
+{
+       /* Most likely we failed to create the interface */
+       if (ppp->net == NULL)
+               return;
+
+       ppp_net_free(ppp->net);
+       ppp->net = NULL;
+}
+
+void ppp_ipcp_finished_notify(GAtPPP *ppp)
+{
+       if (ppp->phase != PPP_PHASE_NETWORK)
+               return;
+
+       /* Our IPCP parameter negotiation failed */
+       ppp->disconnect_reason = G_AT_PPP_REASON_IPCP_FAIL;
+       pppcp_signal_close(ppp->ipcp);
+       pppcp_signal_close(ppp->lcp);
+}
+
+void ppp_lcp_up_notify(GAtPPP *ppp)
+{
+       /* Wait for the peer to send us a challenge if we expect auth */
+       if (ppp->chap != NULL) {
+               ppp_enter_phase(ppp, PPP_PHASE_AUTHENTICATION);
+               return;
+       }
+
+       /* Otherwise proceed as if auth succeeded */
+       ppp_auth_notify(ppp, TRUE);
+}
+
+void ppp_lcp_down_notify(GAtPPP *ppp)
+{
+       if (ppp->phase == PPP_PHASE_NETWORK || ppp->phase == PPP_PHASE_LINK_UP)
+               pppcp_signal_down(ppp->ipcp);
+
+       if (ppp->disconnect_reason == G_AT_PPP_REASON_UNKNOWN)
+               ppp->disconnect_reason = G_AT_PPP_REASON_PEER_CLOSED;
+
+       ppp_enter_phase(ppp, PPP_PHASE_TERMINATION);
+}
+
+void ppp_lcp_finished_notify(GAtPPP *ppp)
+{
+       ppp_enter_phase(ppp, PPP_PHASE_DEAD);
+}
+
+void ppp_set_recv_accm(GAtPPP *ppp, guint32 accm)
+{
+       g_at_hdlc_set_recv_accm(ppp->hdlc, accm);
+}
+
+void ppp_set_xmit_accm(GAtPPP *ppp, guint32 accm)
+{
+       g_at_hdlc_set_xmit_accm(ppp->hdlc, accm);
+}
+
+/*
+ * The only time we use other than default MTU is when we are in
+ * the network phase.
+ */
+void ppp_set_mtu(GAtPPP *ppp, const guint8 *data)
+{
+       guint16 mtu = get_host_short(data);
+
+       ppp->mtu = mtu;
+}
+
+void ppp_set_xmit_acfc(GAtPPP *ppp, gboolean acfc)
+{
+       ppp->xmit_acfc = acfc;
+}
+
+void ppp_set_xmit_pfc(GAtPPP *ppp, gboolean pfc)
+{
+       ppp->xmit_pfc = pfc;
+}
+
+static void io_disconnect(gpointer user_data)
+{
+       GAtPPP *ppp = user_data;
+
+       if (ppp->phase == PPP_PHASE_DEAD)
+               return;
+
+       ppp->disconnect_reason = G_AT_PPP_REASON_LINK_DEAD;
+       pppcp_signal_down(ppp->lcp);
+       pppcp_signal_close(ppp->lcp);
+}
+
+static void ppp_proxy_suspend_net_interface(gpointer user_data)
+{
+       GAtPPP *ppp = user_data;
+
+       ppp->suspended = TRUE;
+       ppp_net_suspend_interface(ppp->net);
+
+       if (ppp->suspend_func)
+               ppp->suspend_func(ppp->suspend_data);
+}
+
+gboolean g_at_ppp_listen(GAtPPP *ppp, GAtIO *io)
+{
+       ppp->hdlc = g_at_hdlc_new_from_io(io);
+       if (ppp->hdlc == NULL)
+               return FALSE;
+
+       ppp->suspended = FALSE;
+       g_at_hdlc_set_receive(ppp->hdlc, ppp_receive, ppp);
+       g_at_hdlc_set_suspend_function(ppp->hdlc,
+                                       ppp_proxy_suspend_net_interface, ppp);
+       g_at_io_set_disconnect_function(io, io_disconnect, ppp);
+
+       ppp_enter_phase(ppp, PPP_PHASE_ESTABLISHMENT);
+
+       return TRUE;
+}
+
+/* Administrative Open */
+gboolean g_at_ppp_open(GAtPPP *ppp, GAtIO *io)
+{
+       ppp->hdlc = g_at_hdlc_new_from_io(io);
+       if (ppp->hdlc == NULL)
+               return FALSE;
+
+       ppp->suspended = FALSE;
+       g_at_hdlc_set_receive(ppp->hdlc, ppp_receive, ppp);
+       g_at_hdlc_set_suspend_function(ppp->hdlc,
+                                       ppp_proxy_suspend_net_interface, ppp);
+       g_at_hdlc_set_no_carrier_detect(ppp->hdlc, TRUE);
+       g_at_io_set_disconnect_function(io, io_disconnect, ppp);
+
+       /* send an UP & OPEN events to the lcp layer */
+       pppcp_signal_up(ppp->lcp);
+       pppcp_signal_open(ppp->lcp);
+
+       ppp_enter_phase(ppp, PPP_PHASE_ESTABLISHMENT);
+
+       return TRUE;
+}
+
+gboolean g_at_ppp_set_credentials(GAtPPP *ppp, const char *username,
+                                       const char *password)
+{
+       if (username && strlen(username) > 255)
+               return FALSE;
+
+       if (password && strlen(password) > 255)
+               return FALSE;
+
+       memset(ppp->username, 0, sizeof(ppp->username));
+       memset(ppp->password, 0, sizeof(ppp->password));
+
+       if (username)
+               strcpy(ppp->username, username);
+
+       if (password)
+               strcpy(ppp->password, password);
+
+       return TRUE;
+}
+
+const char *g_at_ppp_get_username(GAtPPP *ppp)
+{
+       return ppp->username;
+}
+
+const char *g_at_ppp_get_password(GAtPPP *ppp)
+{
+       return ppp->password;
+}
+
+void g_at_ppp_set_recording(GAtPPP *ppp, const char *filename)
+{
+       if (ppp == NULL)
+               return;
+
+       g_at_hdlc_set_recording(ppp->hdlc, filename);
+}
+
+void g_at_ppp_set_connect_function(GAtPPP *ppp, GAtPPPConnectFunc func,
+                                                       gpointer user_data)
+{
+       if (func == NULL)
+               return;
+
+       ppp->connect_cb = func;
+       ppp->connect_data = user_data;
+}
+
+void g_at_ppp_set_disconnect_function(GAtPPP *ppp, GAtPPPDisconnectFunc func,
+                                                       gpointer user_data)
+{
+       if (func == NULL)
+               return;
+
+       ppp->disconnect_cb = func;
+       ppp->disconnect_data = user_data;
+}
+
+void g_at_ppp_set_debug(GAtPPP *ppp, GAtDebugFunc func, gpointer user_data)
+{
+       if (ppp == NULL)
+               return;
+
+       ppp->debugf = func;
+       ppp->debug_data = user_data;
+}
+
+void g_at_ppp_set_suspend_function(GAtPPP *ppp, GAtSuspendFunc func,
+                                       gpointer user_data)
+{
+       if (ppp == NULL)
+               return;
+
+       ppp->suspend_func = func;
+       ppp->suspend_data = user_data;
+
+       if (ppp->hdlc != NULL)
+               g_at_hdlc_set_suspend_function(ppp->hdlc,
+                                       ppp_proxy_suspend_net_interface, ppp);
+}
+
+void g_at_ppp_shutdown(GAtPPP *ppp)
+{
+       if (ppp->phase == PPP_PHASE_DEAD || ppp->phase == PPP_PHASE_TERMINATION)
+               return;
+
+       ppp->disconnect_reason = G_AT_PPP_REASON_LOCAL_CLOSE;
+       pppcp_signal_close(ppp->lcp);
+}
+
+static gboolean call_suspend_cb(gpointer user_data)
+{
+       GAtPPP *ppp = user_data;
+
+       ppp->guard_timeout_source = 0;
+
+       if (ppp->suspend_func)
+               ppp->suspend_func(ppp->suspend_data);
+
+       return FALSE;
+}
+
+static gboolean send_escape_sequence(gpointer user_data)
+{
+       GAtPPP *ppp = user_data;
+       GAtIO *io = g_at_hdlc_get_io(ppp->hdlc);
+
+       g_at_io_write(io, "+++", 3);
+       ppp->guard_timeout_source  = g_timeout_add(GUARD_TIMEOUTS,
+                                               call_suspend_cb, ppp);
+
+       return FALSE;
+}
+
+void g_at_ppp_suspend(GAtPPP *ppp)
+{
+       if (ppp == NULL)
+               return;
+
+       ppp->suspended = TRUE;
+       ppp_net_suspend_interface(ppp->net);
+       g_at_hdlc_suspend(ppp->hdlc);
+       ppp->guard_timeout_source = g_timeout_add(GUARD_TIMEOUTS,
+                                               send_escape_sequence, ppp);
+}
+
+void g_at_ppp_resume(GAtPPP *ppp)
+{
+       if (ppp == NULL)
+               return;
+
+       if (g_at_hdlc_get_io(ppp->hdlc) == NULL) {
+               io_disconnect(ppp);
+               return;
+       }
+
+       ppp->suspended = FALSE;
+       g_at_io_set_disconnect_function(g_at_hdlc_get_io(ppp->hdlc),
+                                                       io_disconnect, ppp);
+       ppp_net_resume_interface(ppp->net);
+       g_at_hdlc_resume(ppp->hdlc);
+}
+
+void g_at_ppp_ref(GAtPPP *ppp)
+{
+       g_atomic_int_inc(&ppp->ref_count);
+}
+
+void g_at_ppp_unref(GAtPPP *ppp)
+{
+       gboolean is_zero;
+
+       if (ppp == NULL)
+               return;
+
+       is_zero = g_atomic_int_dec_and_test(&ppp->ref_count);
+
+       if (is_zero == FALSE)
+               return;
+
+       if (ppp->suspended == FALSE)
+               g_at_io_set_disconnect_function(g_at_hdlc_get_io(ppp->hdlc),
+                                                       NULL, NULL);
+
+       if (ppp->net)
+               ppp_net_free(ppp->net);
+       else if (ppp->fd >= 0)
+               close(ppp->fd);
+
+       if (ppp->chap)
+               ppp_chap_free(ppp->chap);
+
+       lcp_free(ppp->lcp);
+       ipcp_free(ppp->ipcp);
+
+       if (ppp->ppp_dead_source) {
+               g_source_remove(ppp->ppp_dead_source);
+               ppp->ppp_dead_source = 0;
+       }
+
+       if (ppp->guard_timeout_source) {
+               g_source_remove(ppp->guard_timeout_source);
+               ppp->guard_timeout_source = 0;
+       }
+
+       g_at_hdlc_unref(ppp->hdlc);
+
+       g_free(ppp);
+}
+
+void g_at_ppp_set_server_info(GAtPPP *ppp, const char *remote,
+                               const char *dns1, const char *dns2)
+{
+       guint32 r = 0;
+       guint32 d1 = 0;
+       guint32 d2 = 0;
+
+       inet_pton(AF_INET, remote, &r);
+       inet_pton(AF_INET, dns1, &d1);
+       inet_pton(AF_INET, dns2, &d2);
+
+       ipcp_set_server_info(ppp->ipcp, r, d1, d2);
+}
+
+void g_at_ppp_set_acfc_enabled(GAtPPP *ppp, gboolean enabled)
+{
+       lcp_set_acfc_enabled(ppp->lcp, enabled);
+}
+
+void g_at_ppp_set_pfc_enabled(GAtPPP *ppp, gboolean enabled)
+{
+       lcp_set_pfc_enabled(ppp->lcp, enabled);
+}
+
+static GAtPPP *ppp_init_common(gboolean is_server, guint32 ip)
+{
+       GAtPPP *ppp;
+
+       ppp = g_try_malloc0(sizeof(GAtPPP));
+       if (ppp == NULL)
+               return NULL;
+
+       ppp->ref_count = 1;
+       ppp->suspended = TRUE;
+       ppp->fd = -1;
+
+       /* set options to defaults */
+       ppp->mru = DEFAULT_MRU;
+       ppp->mtu = DEFAULT_MTU;
+
+       /* initialize the lcp state */
+       ppp->lcp = lcp_new(ppp, is_server);
+
+       /* initialize IPCP state */
+       ppp->ipcp = ipcp_new(ppp, is_server, ip);
+
+       return ppp;
+}
+
+GAtPPP *g_at_ppp_new(void)
+{
+       return ppp_init_common(FALSE, 0);
+}
+
+GAtPPP *g_at_ppp_server_new_full(const char *local, int fd)
+{
+       GAtPPP *ppp;
+       guint32 ip;
+
+       if (local == NULL)
+               ip = 0;
+       else if (inet_pton(AF_INET, local, &ip) != 1)
+               return NULL;
+
+       ppp = ppp_init_common(TRUE, ip);
+
+       if (ppp != NULL)
+               ppp->fd = fd;
+
+       return ppp;
+}
+
+GAtPPP *g_at_ppp_server_new(const char *local)
+{
+       return g_at_ppp_server_new_full(local, -1);
+}
diff --git a/gatchat/gatppp.h b/gatchat/gatppp.h
new file mode 100644 (file)
index 0000000..b5a2234
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __G_AT_PPP_H
+#define __G_AT_PPP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gat.h"
+#include "gathdlc.h"
+
+struct _GAtPPP;
+
+typedef struct _GAtPPP GAtPPP;
+
+typedef enum _GAtPPPDisconnectReason {
+       G_AT_PPP_REASON_UNKNOWN,
+       G_AT_PPP_REASON_AUTH_FAIL,      /* Failed to authenticate */
+       G_AT_PPP_REASON_IPCP_FAIL,      /* Failed to negotiate IPCP */
+       G_AT_PPP_REASON_NET_FAIL,       /* Failed to create tun */
+       G_AT_PPP_REASON_PEER_CLOSED,    /* Peer initiated a close */
+       G_AT_PPP_REASON_LINK_DEAD,      /* Link to the peer died */
+       G_AT_PPP_REASON_LOCAL_CLOSE,    /* Normal user close */
+} GAtPPPDisconnectReason;
+
+typedef void (*GAtPPPConnectFunc)(const char *iface, const char *local,
+                                       const char *peer,
+                                       const char *dns1, const char *dns2,
+                                       gpointer user_data);
+typedef void (*GAtPPPDisconnectFunc)(GAtPPPDisconnectReason reason,
+                                       gpointer user_data);
+
+GAtPPP *g_at_ppp_new(void);
+GAtPPP *g_at_ppp_server_new(const char *local);
+GAtPPP *g_at_ppp_server_new_full(const char *local, int fd);
+
+gboolean g_at_ppp_open(GAtPPP *ppp, GAtIO *io);
+gboolean g_at_ppp_listen(GAtPPP *ppp, GAtIO *io);
+void g_at_ppp_set_connect_function(GAtPPP *ppp, GAtPPPConnectFunc callback,
+                                       gpointer user_data);
+void g_at_ppp_set_disconnect_function(GAtPPP *ppp, GAtPPPDisconnectFunc func,
+                                       gpointer user_data);
+void g_at_ppp_set_suspend_function(GAtPPP *ppp, GAtSuspendFunc func,
+                                       gpointer user_data);
+void g_at_ppp_set_debug(GAtPPP *ppp, GAtDebugFunc func, gpointer user_data);
+void g_at_ppp_shutdown(GAtPPP *ppp);
+void g_at_ppp_suspend(GAtPPP *ppp);
+void g_at_ppp_resume(GAtPPP *ppp);
+void g_at_ppp_ref(GAtPPP *ppp);
+void g_at_ppp_unref(GAtPPP *ppp);
+
+gboolean g_at_ppp_set_credentials(GAtPPP *ppp, const char *username,
+                                               const char *passwd);
+const char *g_at_ppp_get_username(GAtPPP *ppp);
+const char *g_at_ppp_get_password(GAtPPP *ppp);
+
+void g_at_ppp_set_recording(GAtPPP *ppp, const char *filename);
+
+void g_at_ppp_set_server_info(GAtPPP *ppp, const char *remote_ip,
+                               const char *dns1, const char *dns2);
+
+void g_at_ppp_set_acfc_enabled(GAtPPP *ppp, gboolean enabled);
+void g_at_ppp_set_pfc_enabled(GAtPPP *ppp, gboolean enabled);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_AT_PPP_H */
diff --git a/gatchat/gatrawip.c b/gatchat/gatrawip.c
new file mode 100644 (file)
index 0000000..6114e9d
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+#include "gatrawip.h"
+
+struct _GAtRawIP {
+       gint ref_count;
+       GAtIO *io;
+       GAtIO *tun_io;
+       char *ifname;
+       struct ring_buffer *write_buffer;
+       struct ring_buffer *tun_write_buffer;
+       GAtDebugFunc debugf;
+       gpointer debug_data;
+};
+
+GAtRawIP *g_at_rawip_new(GIOChannel *channel)
+{
+       GAtRawIP *rawip;
+       GAtIO *io;
+
+       io = g_at_io_new(channel);
+       if (io == NULL)
+               return NULL;
+
+       rawip = g_at_rawip_new_from_io(io);
+
+       g_at_io_unref(io);
+
+       return rawip;
+}
+
+GAtRawIP *g_at_rawip_new_from_io(GAtIO *io)
+{
+       GAtRawIP *rawip;
+
+       rawip = g_try_new0(GAtRawIP, 1);
+       if (rawip == NULL)
+               return NULL;
+
+       rawip->ref_count = 1;
+
+       rawip->write_buffer = NULL;
+       rawip->tun_write_buffer = NULL;
+
+       rawip->io = g_at_io_ref(io);
+
+       return rawip;
+}
+
+GAtRawIP *g_at_rawip_ref(GAtRawIP *rawip)
+{
+       if (rawip == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&rawip->ref_count);
+
+       return rawip;
+}
+
+void g_at_rawip_unref(GAtRawIP *rawip)
+{
+       if (rawip == NULL)
+               return;
+
+       if (g_atomic_int_dec_and_test(&rawip->ref_count) == FALSE)
+               return;
+
+       g_at_rawip_shutdown(rawip);
+
+       g_at_io_unref(rawip->io);
+       rawip->io = NULL;
+
+       g_free(rawip->ifname);
+       rawip->ifname = NULL;
+
+       g_free(rawip);
+}
+
+static gboolean can_write_data(gpointer data)
+{
+       GAtRawIP *rawip = data;
+       unsigned int len;
+       unsigned char *buf;
+       gsize bytes_written;
+
+       if (rawip->write_buffer == NULL)
+               return FALSE;
+
+       len = ring_buffer_len_no_wrap(rawip->write_buffer);
+       buf = ring_buffer_read_ptr(rawip->write_buffer, 0);
+
+       bytes_written = g_at_io_write(rawip->io, (gchar *) buf, len);
+       ring_buffer_drain(rawip->write_buffer, bytes_written);
+
+       if (ring_buffer_len(rawip->write_buffer) > 0)
+               return TRUE;
+
+       rawip->write_buffer = NULL;
+
+       return FALSE;
+}
+
+static gboolean tun_write_data(gpointer data)
+{
+       GAtRawIP *rawip = data;
+       unsigned int len;
+       unsigned char *buf;
+       gsize bytes_written;
+
+       if (rawip->tun_write_buffer == NULL)
+               return FALSE;
+
+       len = ring_buffer_len_no_wrap(rawip->tun_write_buffer);
+       buf = ring_buffer_read_ptr(rawip->tun_write_buffer, 0);
+
+       bytes_written = g_at_io_write(rawip->tun_io, (gchar *) buf, len);
+       ring_buffer_drain(rawip->tun_write_buffer, bytes_written);
+
+       if (ring_buffer_len(rawip->tun_write_buffer) > 0)
+               return TRUE;
+
+       rawip->tun_write_buffer = NULL;
+
+       return FALSE;
+}
+
+static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
+{
+       GAtRawIP *rawip = user_data;
+
+       rawip->tun_write_buffer = rbuf;
+
+       g_at_io_set_write_handler(rawip->tun_io, tun_write_data, rawip);
+}
+
+static void tun_bytes(struct ring_buffer *rbuf, gpointer user_data)
+{
+       GAtRawIP *rawip = user_data;
+
+       rawip->write_buffer = rbuf;
+
+       g_at_io_set_write_handler(rawip->io, can_write_data, rawip);
+}
+
+static void create_tun(GAtRawIP *rawip)
+{
+       GIOChannel *channel;
+       struct ifreq ifr;
+       int fd, err;
+
+       fd = open("/dev/net/tun", O_RDWR);
+       if (fd < 0)
+               return;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+       strcpy(ifr.ifr_name, "gprs%d");
+
+       err = ioctl(fd, TUNSETIFF, (void *) &ifr);
+       if (err < 0) {
+               close(fd);
+               return;
+       }
+
+       rawip->ifname = g_strdup(ifr.ifr_name);
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL) {
+               close(fd);
+               return;
+       }
+
+       rawip->tun_io = g_at_io_new(channel);
+
+       g_io_channel_unref(channel);
+}
+
+void g_at_rawip_open(GAtRawIP *rawip)
+{
+       if (rawip == NULL)
+               return;
+
+       create_tun(rawip);
+
+       if (rawip->tun_io == NULL)
+               return;
+
+       g_at_io_set_read_handler(rawip->io, new_bytes, rawip);
+       g_at_io_set_read_handler(rawip->tun_io, tun_bytes, rawip);
+}
+
+void g_at_rawip_shutdown(GAtRawIP *rawip)
+{
+       if (rawip == NULL)
+               return;
+
+       if (rawip->tun_io == NULL)
+               return;
+
+       g_at_io_set_read_handler(rawip->io, NULL, NULL);
+       g_at_io_set_read_handler(rawip->tun_io, NULL, NULL);
+
+       rawip->write_buffer = NULL;
+       rawip->tun_write_buffer = NULL;
+
+       g_at_io_unref(rawip->tun_io);
+       rawip->tun_io = NULL;
+}
+
+const char *g_at_rawip_get_interface(GAtRawIP *rawip)
+{
+       if (rawip == NULL)
+               return NULL;
+
+       return rawip->ifname;
+}
+
+void g_at_rawip_set_debug(GAtRawIP *rawip, GAtDebugFunc func,
+                                               gpointer user_data)
+{
+       if (rawip == NULL)
+               return;
+
+       rawip->debugf = func;
+       rawip->debug_data = user_data;
+}
diff --git a/gatchat/gatrawip.h b/gatchat/gatrawip.h
new file mode 100644 (file)
index 0000000..a74c742
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __G_AT_RAWIP_H
+#define __G_AT_RAWIP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gat.h"
+#include "gatio.h"
+
+struct _GAtRawIP;
+
+typedef struct _GAtRawIP GAtRawIP;
+
+GAtRawIP *g_at_rawip_new(GIOChannel *channel);
+GAtRawIP *g_at_rawip_new_from_io(GAtIO *io);
+
+GAtRawIP *g_at_rawip_ref(GAtRawIP *rawip);
+void g_at_rawip_unref(GAtRawIP *rawip);
+
+void g_at_rawip_open(GAtRawIP *rawip);
+void g_at_rawip_shutdown(GAtRawIP *rawip);
+
+const char *g_at_rawip_get_interface(GAtRawIP *rawip);
+
+void g_at_rawip_set_debug(GAtRawIP *rawip, GAtDebugFunc func,
+                                               gpointer user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_AT_RAWIP_H */
diff --git a/gatchat/gatresult.c b/gatchat/gatresult.c
new file mode 100644 (file)
index 0000000..2659db2
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "gatresult.h"
+
+void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result)
+{
+       iter->result = result;
+       iter->pre.next = result->lines;
+       iter->pre.data = NULL;
+       iter->l = &iter->pre;
+       iter->line_pos = 0;
+}
+
+gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix)
+{
+       char *line;
+       int prefix_len = prefix ? strlen(prefix) : 0;
+       int linelen;
+
+       while ((iter->l = iter->l->next)) {
+               line = iter->l->data;
+               linelen = strlen(line);
+
+               if (linelen > G_AT_RESULT_LINE_LENGTH_MAX)
+                       continue;
+
+               if (prefix_len == 0) {
+                       iter->line_pos = 0;
+                       goto out;
+               }
+
+               if (g_str_has_prefix(line, prefix) == FALSE)
+                       continue;
+
+               iter->line_pos = prefix_len;
+
+               while (iter->line_pos < strlen(line) &&
+                       line[iter->line_pos] == ' ')
+                       iter->line_pos += 1;
+
+               goto out;
+       }
+
+       return FALSE;
+
+out:
+       /* Already checked the length to be no more than buflen */
+       strcpy(iter->buf, line);
+       return TRUE;
+}
+
+const char *g_at_result_iter_raw_line(GAtResultIter *iter)
+{
+       const char *line;
+
+       if (iter == NULL)
+               return NULL;
+
+       if (iter->l == NULL)
+               return NULL;
+
+       line = iter->l->data;
+
+       line += iter->line_pos;
+
+       return line;
+}
+
+static inline int skip_to_next_field(const char *line, int pos, int len)
+{
+       if (pos < len && line[pos] == ',')
+               pos += 1;
+
+       while (pos < len && line[pos] == ' ')
+               pos += 1;
+
+       return pos;
+}
+
+gboolean g_at_result_iter_next_unquoted_string(GAtResultIter *iter,
+                                               const char **str)
+{
+       unsigned int pos;
+       unsigned int end;
+       unsigned int len;
+       char *line;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+
+       /* Omitted string */
+       if (line[pos] == ',') {
+               end = pos;
+               iter->buf[pos] = '\0';
+               goto out;
+       }
+
+       if (line[pos] == '"' || line[pos] == ')')
+               return FALSE;
+
+       end = pos;
+
+       while (end < len && line[end] != ',' && line[end] != ')')
+               end += 1;
+
+       iter->buf[end] = '\0';
+
+out:
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (str)
+               *str = iter->buf + pos;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str)
+{
+       unsigned int pos;
+       unsigned int end;
+       unsigned int len;
+       char *line;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+
+       /* Omitted string */
+       if (line[pos] == ',') {
+               end = pos;
+               iter->buf[pos] = '\0';
+               goto out;
+       }
+
+       if (line[pos++] != '"')
+               return FALSE;
+
+       end = pos;
+
+       while (end < len && line[end] != '"')
+               end += 1;
+
+       if (line[end] != '"')
+               return FALSE;
+
+       iter->buf[end] = '\0';
+
+       /* Skip " */
+       end += 1;
+
+out:
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (str)
+               *str = iter->buf + pos;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter,
+               const guint8 **str, gint *length)
+{
+       unsigned int pos;
+       unsigned int end;
+       unsigned int len;
+       char *line;
+       char *bufpos;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+       bufpos = iter->buf + pos;
+
+       /* Omitted string */
+       if (line[pos] == ',') {
+               end = pos;
+               iter->buf[pos] = '\0';
+               goto out;
+       }
+
+       if (line[pos] == '"')
+               pos += 1;
+
+       end = pos;
+
+       while (end < len && g_ascii_isxdigit(line[end]))
+               end += 1;
+
+       if ((end - pos) & 1)
+               return FALSE;
+
+       *length = (end - pos) / 2;
+
+       for (; pos < end; pos += 2)
+               sscanf(line + pos, "%02hhx", bufpos++);
+
+       if (line[end] == '"')
+               end += 1;
+
+out:
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (str)
+               *str = (guint8 *) bufpos - *length;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number)
+{
+       int pos;
+       int end;
+       int len;
+       int value = 0;
+       char *line;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+       end = pos;
+
+       while (line[end] >= '0' && line[end] <= '9') {
+               value = value * 10 + (int)(line[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (number)
+               *number = value;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_next_number_default(GAtResultIter *iter, gint dflt,
+                                               gint *number)
+{
+       unsigned int pos;
+       int len;
+       char *line;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = skip_to_next_field(line, iter->line_pos, len);
+
+       if (pos != iter->line_pos) {
+               iter->line_pos = pos;
+
+               if (number)
+                       *number = dflt;
+
+               return TRUE;
+       }
+
+       return g_at_result_iter_next_number(iter, number);
+}
+
+gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max)
+{
+       int pos;
+       int end;
+       int len;
+       int low = 0;
+       int high = 0;
+       char *line;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+
+       while (pos < len && line[pos] == ' ')
+               pos += 1;
+
+       end = pos;
+
+       while (line[end] >= '0' && line[end] <= '9') {
+               low = low * 10 + (int)(line[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+       if (line[end] != '-') {
+               high = low;
+               goto out;
+       }
+
+       pos = end = end + 1;
+
+       while (line[end] >= '0' && line[end] <= '9') {
+               high = high * 10 + (int)(line[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+out:
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (min)
+               *min = low;
+
+       if (max)
+               *max = high;
+
+       return TRUE;
+}
+
+static gint skip_until(const char *line, int start, const char delim)
+{
+       int len = strlen(line);
+       int i = start;
+
+       while (i < len) {
+               if (line[i] == delim)
+                       return i;
+
+               if (line[i] == '\"') {
+                       i += 1;
+                       while (i < len && line[i] != '\"')
+                               i += 1;
+
+                       if (i < len)
+                               i += 1;
+
+                       continue;
+               }
+
+               if (line[i] != '(') {
+                       i += 1;
+                       continue;
+               }
+
+               i = skip_until(line, i+1, ')');
+
+               if (i < len)
+                       i += 1;
+       }
+
+       return i;
+}
+
+gboolean g_at_result_iter_skip_next(GAtResultIter *iter)
+{
+       unsigned int skipped_to;
+       char *line;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+
+       skipped_to = skip_until(line, iter->line_pos, ',');
+
+       if (skipped_to == iter->line_pos && line[skipped_to] != ',')
+               return FALSE;
+
+       iter->line_pos = skip_to_next_field(line, skipped_to, strlen(line));
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_open_list(GAtResultIter *iter)
+{
+       char *line;
+       unsigned int len;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       if (iter->line_pos >= len)
+               return FALSE;
+
+       if (line[iter->line_pos] != '(')
+               return FALSE;
+
+       iter->line_pos += 1;
+
+       while (iter->line_pos < strlen(line) &&
+               line[iter->line_pos] == ' ')
+               iter->line_pos += 1;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_close_list(GAtResultIter *iter)
+{
+       char *line;
+       unsigned int len;
+
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->l == NULL)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       if (iter->line_pos >= len)
+               return FALSE;
+
+       if (line[iter->line_pos] != ')')
+               return FALSE;
+
+       iter->line_pos += 1;
+
+       iter->line_pos = skip_to_next_field(line, iter->line_pos, len);
+
+       return TRUE;
+}
+
+const char *g_at_result_final_response(GAtResult *result)
+{
+       if (result == NULL)
+               return NULL;
+
+       return result->final_or_pdu;
+}
+
+const char *g_at_result_pdu(GAtResult *result)
+{
+       if (result == NULL)
+               return NULL;
+
+       return result->final_or_pdu;
+}
+
+gint g_at_result_num_response_lines(GAtResult *result)
+{
+       if (result == NULL)
+               return 0;
+
+       if (result->lines == NULL)
+               return 0;
+
+       return g_slist_length(result->lines);
+}
diff --git a/gatchat/gatresult.h b/gatchat/gatresult.h
new file mode 100644 (file)
index 0000000..589dd3d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATCHAT_RESULT_H
+#define __GATCHAT_RESULT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _GAtResult {
+       GSList *lines;
+       char *final_or_pdu;
+};
+
+typedef struct _GAtResult GAtResult;
+
+#define G_AT_RESULT_LINE_LENGTH_MAX 2048
+
+struct _GAtResultIter {
+       GAtResult *result;
+       GSList *l;
+       char buf[G_AT_RESULT_LINE_LENGTH_MAX + 1];
+       unsigned int line_pos;
+       GSList pre;
+};
+
+typedef struct _GAtResultIter GAtResultIter;
+
+void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result);
+
+gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix);
+gboolean g_at_result_iter_open_list(GAtResultIter *iter);
+gboolean g_at_result_iter_close_list(GAtResultIter *iter);
+
+gboolean g_at_result_iter_skip_next(GAtResultIter *iter);
+
+gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max);
+gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str);
+gboolean g_at_result_iter_next_unquoted_string(GAtResultIter *iter,
+                                               const char **str);
+gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number);
+gboolean g_at_result_iter_next_number_default(GAtResultIter *iter, gint dflt,
+                                               gint *number);
+gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter,
+               const guint8 **str, gint *length);
+
+const char *g_at_result_iter_raw_line(GAtResultIter *iter);
+
+const char *g_at_result_final_response(GAtResult *result);
+const char *g_at_result_pdu(GAtResult *result);
+
+gint g_at_result_num_response_lines(GAtResult *result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATCHAT_RESULT_H */
diff --git a/gatchat/gatserver.c b/gatchat/gatserver.c
new file mode 100644 (file)
index 0000000..7c87c7a
--- /dev/null
@@ -0,0 +1,1493 @@
+/*
+ *
+ *  AT server library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+#include "gatserver.h"
+#include "gatio.h"
+
+#define BUF_SIZE 4096
+/* <cr><lf> + the max length of information text + <cr><lf> */
+#define MAX_TEXT_SIZE 2052
+/* #define WRITE_SCHEDULER_DEBUG 1 */
+
+enum ParserState {
+       PARSER_STATE_IDLE,
+       PARSER_STATE_A,
+       PARSER_STATE_COMMAND,
+       PARSER_STATE_GARBAGE,
+};
+
+enum ParserResult {
+       PARSER_RESULT_COMMAND,
+       PARSER_RESULT_EMPTY_COMMAND,
+       PARSER_RESULT_REPEAT_LAST,
+       PARSER_RESULT_GARBAGE,
+       PARSER_RESULT_UNSURE,
+};
+
+/* V.250 Table 1/V.250 Result codes */
+static const char *server_result_to_string(GAtServerResult result)
+{
+       switch (result) {
+       case G_AT_SERVER_RESULT_OK:
+               return "OK";
+       case G_AT_SERVER_RESULT_CONNECT:
+               return "CONNECT";
+       case G_AT_SERVER_RESULT_RING:
+               return "RING";
+       case G_AT_SERVER_RESULT_NO_CARRIER:
+               return "NO CARRIER";
+       case G_AT_SERVER_RESULT_ERROR:
+               return "ERROR";
+       case G_AT_SERVER_RESULT_NO_DIALTONE:
+               return "NO DIALTONE";
+       case G_AT_SERVER_RESULT_BUSY:
+               return "BUSY";
+       case G_AT_SERVER_RESULT_NO_ANSWER:
+               return "NO ANSWER";
+       default:
+               return NULL;
+       }
+}
+
+/* Basic command setting for V.250 */
+struct v250_settings {
+       char s0;                        /* set by S0=<val> */
+       char s3;                        /* set by S3=<val> */
+       char s4;                        /* set by S4=<val> */
+       char s5;                        /* set by S5=<val> */
+       int s6;                         /* set by S6=<val> */
+       int s7;                         /* set by S7=<val> */
+       int s8;                         /* set by S8=<val> */
+       int s10;                        /* set by S10=<val> */
+       gboolean echo;                  /* set by E<val> */
+       gboolean quiet;                 /* set by Q<val> */
+       gboolean is_v1;                 /* set by V<val>, v0 or v1 */
+       int res_format;                 /* set by X<val> */
+       int c109;                       /* set by &C<val> */
+       int c108;                       /* set by &D<val> */
+       char l;                         /* set by L<val> */
+       char m;                         /* set by M<val> */
+       char dial_mode;                 /* set by P or T */
+};
+
+/* AT command set that server supported */
+struct at_command {
+       GAtServerNotifyFunc notify;
+       gpointer user_data;
+       GDestroyNotify destroy_notify;
+};
+
+struct _GAtServer {
+       gint ref_count;                         /* Ref count */
+       struct v250_settings v250;              /* V.250 command setting */
+       GAtIO *io;                              /* Server IO */
+       guint read_so_far;                      /* Number of bytes processed */
+       GAtDisconnectFunc user_disconnect;      /* User disconnect func */
+       gpointer user_disconnect_data;          /* User disconnect data */
+       GAtDebugFunc debugf;                    /* Debugging output function */
+       gpointer debug_data;                    /* Data to pass to debug func */
+       GHashTable *command_list;               /* List of AT commands */
+       GQueue *write_queue;                    /* Write buffer queue */
+       guint max_read_attempts;                /* Max reads per select */
+       enum ParserState parser_state;
+       gboolean destroyed;                     /* Re-entrancy guard */
+       char *last_line;                        /* Last read line */
+       unsigned int cur_pos;                   /* Where we are on the line */
+       GAtServerResult last_result;
+       gboolean final_sent;
+       gboolean final_async;
+       gboolean in_read_handler;
+       GAtServerFinishFunc finishf;            /* Callback when cmd finishes */
+       gpointer finish_data;                   /* Finish func data */
+};
+
+static void server_wakeup_writer(GAtServer *server);
+static void server_parse_line(GAtServer *server);
+
+static struct ring_buffer *allocate_next(GAtServer *server)
+{
+       struct ring_buffer *buf = ring_buffer_new(BUF_SIZE);
+
+       if (buf == NULL)
+               return NULL;
+
+       g_queue_push_tail(server->write_queue, buf);
+
+       return buf;
+}
+
+static void send_common(GAtServer *server, const char *buf, unsigned int len)
+{
+       gsize towrite = len;
+       gsize bytes_written = 0;
+       struct ring_buffer *write_buf;
+
+       write_buf = g_queue_peek_tail(server->write_queue);
+
+       while (bytes_written < towrite) {
+               gsize wbytes = MIN((gsize)ring_buffer_avail(write_buf),
+                                               towrite - bytes_written);
+
+               bytes_written += ring_buffer_write(write_buf,
+                                                       buf + bytes_written,
+                                                       wbytes);
+
+               /*
+                * Make sure we don't allocate a buffer if we've written
+                * everything out already
+                */
+               if (ring_buffer_avail(write_buf) == 0 &&
+                               bytes_written < towrite)
+                       write_buf = allocate_next(server);
+       }
+
+       server_wakeup_writer(server);
+}
+
+static void send_result_common(GAtServer *server, const char *result)
+
+{
+       struct v250_settings v250 = server->v250;
+       char buf[MAX_TEXT_SIZE + 1];
+       char t = v250.s3;
+       char r = v250.s4;
+       unsigned int len;
+
+       if (v250.quiet)
+               return;
+
+       if (result == NULL)
+               return;
+
+       if (strlen(result) > 2048)
+               return;
+
+       if (v250.is_v1)
+               len = sprintf(buf, "%c%c%s%c%c", t, r, result, t, r);
+       else
+               len = sprintf(buf, "%s%c", result, t);
+
+       send_common(server, buf, len);
+}
+
+static inline void send_final_common(GAtServer *server, const char *result)
+{
+       send_result_common(server, result);
+       server->final_async = FALSE;
+
+       if (server->finishf)
+               server->finishf(server, server->finish_data);
+}
+
+static inline void send_final_numeric(GAtServer *server, GAtServerResult result)
+{
+       char buf[1024];
+
+       if (server->v250.is_v1)
+               sprintf(buf, "%s", server_result_to_string(result));
+       else
+               sprintf(buf, "%u", (unsigned int)result);
+
+       send_final_common(server, buf);
+}
+
+void g_at_server_send_final(GAtServer *server, GAtServerResult result)
+{
+       if (server->final_sent != FALSE)
+               return;
+
+       server->final_sent = TRUE;
+       server->last_result = result;
+
+       if (result == G_AT_SERVER_RESULT_OK) {
+               if (server->final_async)
+                       server_parse_line(server);
+
+               return;
+       }
+
+       send_final_numeric(server, result);
+}
+
+void g_at_server_send_ext_final(GAtServer *server, const char *result)
+{
+       server->final_sent = TRUE;
+       server->last_result = G_AT_SERVER_RESULT_EXT_ERROR;
+       send_final_common(server, result);
+}
+
+void g_at_server_send_intermediate(GAtServer *server, const char *result)
+{
+       send_result_common(server, result);
+}
+
+void g_at_server_send_unsolicited(GAtServer *server, const char *result)
+{
+       send_result_common(server, result);
+}
+
+void g_at_server_send_info(GAtServer *server, const char *line, gboolean last)
+{
+       char buf[MAX_TEXT_SIZE + 1];
+       char t = server->v250.s3;
+       char r = server->v250.s4;
+       unsigned int len;
+
+       if (strlen(line) > 2048)
+               return;
+
+       if (last)
+               len = sprintf(buf, "%c%c%s%c%c", t, r, line, t, r);
+       else
+               len = sprintf(buf, "%c%c%s", t, r, line);
+
+       send_common(server, buf, len);
+}
+
+static gboolean get_result_value(GAtServer *server, GAtResult *result,
+                                               int min, int max, int *value)
+{
+       GAtResultIter iter;
+       int val;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, ""))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, &val))
+               return FALSE;
+
+       if (val < min || val > max)
+               return FALSE;
+
+       if (value)
+               *value = val;
+
+       return TRUE;
+}
+
+static void v250_settings_create(struct v250_settings *v250)
+{
+       v250->s0 = 0;
+       v250->s3 = '\r';
+       v250->s4 = '\n';
+       v250->s5 = '\b';
+       v250->s6 = 2;
+       v250->s7 = 50;
+       v250->s8 = 2;
+       v250->s10 = 2;
+       v250->echo = TRUE;
+       v250->quiet = FALSE;
+       v250->is_v1 = TRUE;
+       v250->res_format = 0;
+       v250->c109 = 1;
+       v250->c108 = 0;
+       v250->l = 0;
+       v250->m = 1;
+       v250->dial_mode = 'T';
+}
+
+static void s_template_cb(GAtServerRequestType type, GAtResult *result,
+                                       GAtServer *server, char *sreg,
+                                       const char *prefix, int min, int max)
+{
+       char buf[20];
+       int tmp;
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               if (!get_result_value(server, result, min, max, &tmp)) {
+                       g_at_server_send_final(server,
+                                               G_AT_SERVER_RESULT_ERROR);
+                       return;
+               }
+
+               *sreg = tmp;
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               tmp = *sreg;
+               sprintf(buf, "%03d", tmp);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               sprintf(buf, "%s: (%d-%d)", prefix, min, max);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void at_s0_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       s_template_cb(type, result, server, &server->v250.s0, "S0", 0, 7);
+}
+
+static void at_s3_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       s_template_cb(type, result, server, &server->v250.s3, "S3", 0, 127);
+}
+
+static void at_s4_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       s_template_cb(type, result, server, &server->v250.s4, "S4", 0, 127);
+}
+
+static void at_s5_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       s_template_cb(type, result, server, &server->v250.s5, "S5", 0, 127);
+}
+
+static void at_l_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       s_template_cb(type, result, server, &server->v250.l, "L", 0, 3);
+}
+
+static void at_m_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       s_template_cb(type, result, server, &server->v250.m, "M", 0, 2);
+}
+
+static void at_template_cb(GAtServerRequestType type, GAtResult *result,
+                                       GAtServer *server, int *value,
+                                       const char *prefix,
+                                       int min, int max, int deftval)
+{
+       char buf[20];
+       int tmp;
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               if (!get_result_value(server, result, min, max, &tmp)) {
+                       g_at_server_send_final(server,
+                                               G_AT_SERVER_RESULT_ERROR);
+                       return;
+               }
+
+               *value = tmp;
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               tmp = *value;
+               sprintf(buf, "%s: %d", prefix, tmp);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               sprintf(buf, "%s: (%d-%d)", prefix, min, max);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               *value = deftval;
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void at_e_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.echo, "E", 0, 1, 1);
+}
+
+static void at_q_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.quiet, "Q", 0, 1, 0);
+}
+
+static void at_v_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.is_v1, "V", 0, 1, 1);
+}
+
+static void at_x_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.res_format,
+                       "X", 0, 4, 4);
+}
+
+static void at_s6_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.s6, "S6", 0, 1, 1);
+}
+
+static void at_s7_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.s7, "S7", 1, 255, 50);
+}
+
+static void at_s8_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.s8, "S8", 1, 255, 2);
+}
+
+static void at_s10_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.s10, "S10", 1, 254, 2);
+}
+
+static void at_c109_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.c109, "&C", 0, 1, 1);
+}
+
+static void at_c108_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       at_template_cb(type, result, server, &server->v250.c108, "&D", 0, 2, 2);
+}
+
+/* According to ITU V.250 6.3.2 and 6.3.3: "Implementation of this command
+ * is mandatory; however, if DTMF or pulse dialling is not implemented,
+ * this command will have no effect"
+ */
+static void at_t_cb(GAtServer *server, GAtServerRequestType type,
+                                       GAtResult *result, gpointer user_data)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               server->v250.dial_mode = 'T';
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void at_p_cb(GAtServer *server, GAtServerRequestType type,
+                                       GAtResult *result, gpointer user_data)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               server->v250.dial_mode = 'P';
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void at_f_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               if (!get_result_value(server, result, 0, 0, NULL)) {
+                       g_at_server_send_final(server,
+                                               G_AT_SERVER_RESULT_ERROR);
+                       return;
+               }
+               /* intentional fallback here */
+
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               /* default behavior on AT&F same as ATZ */
+               v250_settings_create(&server->v250);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void at_z_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               v250_settings_create(&server->v250);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static inline gboolean is_extended_command_prefix(const char c)
+{
+       switch (c) {
+       case '+':
+       case '*':
+       case '!':
+       case '%':
+               return TRUE;
+       default:
+               return FALSE;
+       }
+}
+
+static void at_command_notify(GAtServer *server, char *command,
+                               char *prefix, GAtServerRequestType type)
+{
+       struct at_command *node;
+       GAtResult result;
+
+       node = g_hash_table_lookup(server->command_list, prefix);
+
+       if (node == NULL) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       result.lines = g_slist_prepend(NULL, command);
+       result.final_or_pdu = 0;
+
+       node->notify(server, type, &result, node->user_data);
+
+       g_slist_free(result.lines);
+}
+
+static unsigned int parse_extended_command(GAtServer *server, char *buf)
+{
+       const char *valid_extended_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                               "0123456789!%-./:_";
+       const char *separators = ";?=";
+       unsigned int prefix_len, i;
+       gboolean in_string = FALSE;
+       gboolean seen_equals = FALSE;
+       char prefix[18]; /* According to V250, 5.4.1 */
+       GAtServerRequestType type;
+       char tmp;
+       unsigned int cmd_start;
+
+       prefix_len = strcspn(buf, separators);
+
+       if (prefix_len > 17 || prefix_len < 2)
+               return 0;
+
+       /* Convert to upper case, we will always use upper case naming */
+       for (i = 0; i < prefix_len; i++)
+               prefix[i] = g_ascii_toupper(buf[i]);
+
+       prefix[prefix_len] = '\0';
+
+       if (strspn(prefix + 1, valid_extended_chars) != (prefix_len - 1))
+               return 0;
+
+       /*
+        * V.250 Section 5.4.1: "The first character following "+" shall be
+        * an alphabetic character in the range "A" through "Z".
+        */
+       if (prefix[1] <= 'A' || prefix[1] >= 'Z')
+               return 0;
+
+       if (buf[i] != '\0' && buf[i] != ';' && buf[i] != '?' && buf[i] != '=')
+               return 0;
+
+       type = G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY;
+       cmd_start = prefix_len;
+
+       /* Continue until we hit eol or ';' */
+       while (buf[i] && !(buf[i] == ';' && in_string == FALSE)) {
+               if (buf[i] == '"') {
+                       in_string = !in_string;
+                       goto next;
+               }
+
+               if (in_string == TRUE)
+                       goto next;
+
+               if (buf[i] == '?') {
+                       if (seen_equals && buf[i-1] != '=')
+                               return 0;
+
+                       if (buf[i + 1] != '\0' && buf[i + 1] != ';')
+                               return 0;
+
+                       type = G_AT_SERVER_REQUEST_TYPE_QUERY;
+                       cmd_start += 1;
+
+                       if (seen_equals)
+                               type = G_AT_SERVER_REQUEST_TYPE_SUPPORT;
+               } else if (buf[i] == '=') {
+                       if (seen_equals)
+                               return 0;
+
+                       seen_equals = TRUE;
+                       type = G_AT_SERVER_REQUEST_TYPE_SET;
+                       cmd_start += 1;
+               }
+
+next:
+               i++;
+       }
+
+       /* We can scratch in this buffer, so mark ';' as null */
+       tmp = buf[i];
+       buf[i] = '\0';
+       at_command_notify(server, buf + cmd_start, prefix, type);
+       buf[i] = tmp;
+
+       /* Also consume the terminating null */
+       return i + 1;
+}
+
+static int get_basic_prefix_size(const char *buf)
+{
+       if (g_ascii_isalpha(buf[0])) {
+               if (g_ascii_toupper(buf[0]) == 'S') {
+                       int size;
+
+                       /* V.250 5.3.2 'S' command follows with a parameter
+                        * number.
+                        */
+                       for (size = 1; g_ascii_isdigit(buf[size]); size++)
+                               ;
+
+                       /*
+                        * Do some basic sanity checking, don't accept 00, 01,
+                        * etc or empty S values
+                        */
+                       if (size == 1)
+                               return 0;
+
+                       if (size > 2 && buf[1] == '0')
+                               return 0;
+
+                       return size;
+               }
+
+               /* All other cases it is a simple 1 character prefix */
+               return 1;
+       }
+
+       if (buf[0] == '&') {
+               if (g_ascii_isalpha(buf[1]) == FALSE)
+                       return 0;
+
+               return 2;
+       }
+
+       return 0;
+}
+
+static unsigned int parse_basic_command(GAtServer *server, char *buf)
+{
+       gboolean seen_equals = FALSE;
+       char prefix[4], tmp;
+       unsigned int i, prefix_size;
+       GAtServerRequestType type;
+       unsigned int cmd_start;
+
+       prefix_size = get_basic_prefix_size(buf);
+       if (prefix_size == 0)
+               return 0;
+
+       i = prefix_size;
+       prefix[0] = g_ascii_toupper(buf[0]);
+       cmd_start = prefix_size;
+
+       if (prefix[0] == 'D') {
+               type = G_AT_SERVER_REQUEST_TYPE_SET;
+
+               /* All characters appearing on the same line, up to a
+                * semicolon character (IA5 3/11) or the end of the
+                * command line is the part of the call.
+                */
+               while (buf[i] != '\0' && buf[i] != ';')
+                       i += 1;
+
+               if (buf[i] == ';')
+                       i += 1;
+
+               goto done;
+       }
+
+       type = G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY;
+
+       /* Match '?', '=',  '=?' and '=xxx' */
+       if (buf[i] == '=') {
+               seen_equals = TRUE;
+               i += 1;
+               cmd_start += 1;
+       }
+
+       if (buf[i] == '?') {
+               i += 1;
+               cmd_start += 1;
+
+               if (seen_equals)
+                       type = G_AT_SERVER_REQUEST_TYPE_SUPPORT;
+               else
+                       type = G_AT_SERVER_REQUEST_TYPE_QUERY;
+       } else {
+               int before = i;
+
+               /* V.250 5.3.1 The subparameter (if any) are all digits */
+               while (g_ascii_isdigit(buf[i]))
+                       i++;
+
+               if (i - before > 0)
+                       type = G_AT_SERVER_REQUEST_TYPE_SET;
+       }
+
+done:
+       if (prefix_size <= 3) {
+               memcpy(prefix + 1, buf + 1, prefix_size - 1);
+               prefix[prefix_size] = '\0';
+
+               tmp = buf[i];
+               buf[i] = '\0';
+               at_command_notify(server, buf + cmd_start, prefix, type);
+               buf[i] = tmp;
+       } else /* Handle S-parameter with 100+ */
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+
+       /*
+        * Commands like ATA, ATZ cause the remainder linevto be ignored.
+        * In GSM/UMTS the ATD uses the separator ';' character as a voicecall
+        * modifier, so we ignore everything coming after that character
+        * as well.
+        */
+       if (prefix[0] == 'A' || prefix[0] == 'Z' || prefix[0] == 'D')
+               return strlen(buf);
+
+       /* Consume the seperator ';' */
+       if (buf[i] == ';')
+               i += 1;
+
+       return i;
+}
+
+static void server_parse_line(GAtServer *server)
+{
+       char *line = server->last_line;
+       unsigned int pos = server->cur_pos;
+       unsigned int len = strlen(line);
+
+       while (pos < len) {
+               unsigned int consumed;
+
+               server->final_sent = FALSE;
+               server->final_async = FALSE;
+
+               if (is_extended_command_prefix(line[pos]))
+                       consumed = parse_extended_command(server, line + pos);
+               else
+                       consumed = parse_basic_command(server, line + pos);
+
+               if (consumed == 0) {
+                       g_at_server_send_final(server,
+                                               G_AT_SERVER_RESULT_ERROR);
+                       return;
+               }
+
+               pos += consumed;
+               server->cur_pos = pos;
+
+               /*
+                * We wait the callback until it finished processing
+                * the command and called the send_final.
+                */
+               if (server->final_sent == FALSE) {
+                       server->final_async = TRUE;
+                       return;
+               }
+
+               if (server->last_result != G_AT_SERVER_RESULT_OK)
+                       return;
+       }
+
+       send_final_numeric(server, G_AT_SERVER_RESULT_OK);
+}
+
+static enum ParserResult server_feed(GAtServer *server,
+                                       const char *bytes, gsize *len)
+{
+       gsize i = 0;
+       enum ParserResult res = PARSER_RESULT_UNSURE;
+       char s3 = server->v250.s3;
+
+       while (i < *len) {
+               char byte = bytes[i];
+
+               switch (server->parser_state) {
+               case PARSER_STATE_IDLE:
+                       if (byte == s3) {
+                               i += 1;
+                               res = PARSER_RESULT_EMPTY_COMMAND;
+                               goto out;
+                       } else if (byte == '\n') {
+                               i += 1;
+                               res = PARSER_RESULT_GARBAGE;
+                               goto out;
+                       } else if (byte == 'A' || byte == 'a')
+                               server->parser_state = PARSER_STATE_A;
+                       else if (byte != ' ' && byte != '\t')
+                               server->parser_state = PARSER_STATE_GARBAGE;
+                       break;
+
+               case PARSER_STATE_A:
+                       if (byte == s3) {
+                               server->parser_state = PARSER_STATE_IDLE;
+                               i += 1;
+                               res = PARSER_RESULT_GARBAGE;
+                               goto out;
+                       } else if (byte == '/') {
+                               server->parser_state = PARSER_STATE_IDLE;
+                               i += 1;
+                               res = PARSER_RESULT_REPEAT_LAST;
+                               goto out;
+                       } else if (byte == 'T' || byte == 't')
+                               server->parser_state = PARSER_STATE_COMMAND;
+                       else
+                               server->parser_state = PARSER_STATE_GARBAGE;
+
+                       break;
+
+               case PARSER_STATE_COMMAND:
+                       if (byte == s3) {
+                               server->parser_state = PARSER_STATE_IDLE;
+                               i += 1;
+                               res = PARSER_RESULT_COMMAND;
+                               goto out;
+                       }
+                       break;
+
+               case PARSER_STATE_GARBAGE:
+                       /* Detect CR or HDLC frame marker flag */
+                       if (byte == s3 || byte == '~') {
+                               server->parser_state = PARSER_STATE_IDLE;
+                               i += 1;
+                               res = PARSER_RESULT_GARBAGE;
+                               goto out;
+                       }
+                       break;
+
+               default:
+                       break;
+               };
+
+               i += 1;
+       }
+
+out:
+       *len = i;
+       return res;
+}
+
+static char *extract_line(GAtServer *p, struct ring_buffer *rbuf)
+{
+       unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
+       unsigned int pos = 0;
+       unsigned char *buf = ring_buffer_read_ptr(rbuf, pos);
+       int strip_front = 0;
+       int line_length = 0;
+       gboolean in_string = FALSE;
+       char s3 = p->v250.s3;
+       char s5 = p->v250.s5;
+       char *line;
+       int i;
+
+       while (pos < p->read_so_far) {
+               if (*buf == '"')
+                       in_string = !in_string;
+
+               if (in_string == FALSE && (*buf == ' ' || *buf == '\t')) {
+                       if (line_length == 0)
+                               strip_front += 1;
+               } else
+                       line_length += 1;
+
+               buf += 1;
+               pos += 1;
+
+               if (pos == wrap)
+                       buf = ring_buffer_read_ptr(rbuf, pos);
+       }
+
+       /* We will strip AT and S3 */
+       line_length -= 3;
+
+       line = g_try_new(char, line_length + 1);
+       if (line == NULL) {
+               ring_buffer_drain(rbuf, p->read_so_far);
+               return NULL;
+       }
+
+       /* Strip leading whitespace + AT */
+       ring_buffer_drain(rbuf, strip_front + 2);
+
+       pos = 0;
+       i = 0;
+       wrap = ring_buffer_len_no_wrap(rbuf);
+       buf = ring_buffer_read_ptr(rbuf, pos);
+
+       while (pos < (p->read_so_far - strip_front - 2)) {
+               if (*buf == '"')
+                       in_string = !in_string;
+
+               if (*buf == s5) {
+                       if (i != 0)
+                               i -= 1;
+               } else if ((*buf == ' ' || *buf == '\t') && in_string == FALSE)
+                       ; /* Skip */
+               else if (*buf != s3)
+                       line[i++] = *buf;
+
+               buf += 1;
+               pos += 1;
+
+               if (pos == wrap)
+                       buf = ring_buffer_read_ptr(rbuf, pos);
+       }
+
+       /* Strip S3 */
+       ring_buffer_drain(rbuf, p->read_so_far - strip_front - 2);
+
+       line[i] = '\0';
+
+       return line;
+}
+
+static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
+{
+       GAtServer *p = user_data;
+       unsigned int len = ring_buffer_len(rbuf);
+       unsigned int wrap = ring_buffer_len_no_wrap(rbuf);
+       unsigned char *buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
+       enum ParserResult result;
+
+       /* We do not support command abortion, so ignore input */
+       if (p->final_async) {
+               ring_buffer_drain(rbuf, len);
+               return;
+       }
+
+       p->in_read_handler = TRUE;
+
+       while (p->io && (p->read_so_far < len)) {
+               gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
+               result = server_feed(p, (char *)buf, &rbytes);
+
+               if (p->v250.echo)
+                       send_common(p, (char *)buf, rbytes);
+
+               buf += rbytes;
+               p->read_so_far += rbytes;
+
+               if (p->read_so_far == wrap) {
+                       buf = ring_buffer_read_ptr(rbuf, p->read_so_far);
+                       wrap = len;
+               }
+
+               switch (result) {
+               case PARSER_RESULT_UNSURE:
+                       continue;
+
+               case PARSER_RESULT_EMPTY_COMMAND:
+                       /*
+                        * According to section 5.2.4 and 5.6 of V250,
+                        * Empty commands must be OK by the DCE
+                        */
+                       g_at_server_send_final(p, G_AT_SERVER_RESULT_OK);
+                       ring_buffer_drain(rbuf, p->read_so_far);
+                       break;
+
+               case PARSER_RESULT_COMMAND:
+               {
+                       g_free(p->last_line);
+
+                       p->last_line = extract_line(p, rbuf);
+                       p->cur_pos = 0;
+
+                       if (p->last_line)
+                               server_parse_line(p);
+                       else
+                               g_at_server_send_final(p,
+                                               G_AT_SERVER_RESULT_ERROR);
+                       break;
+               }
+
+               case PARSER_RESULT_REPEAT_LAST:
+                       p->cur_pos = 0;
+                       ring_buffer_drain(rbuf, p->read_so_far);
+
+                       if (p->last_line)
+                               server_parse_line(p);
+                       else
+                               g_at_server_send_final(p,
+                                               G_AT_SERVER_RESULT_OK);
+                       break;
+
+               case PARSER_RESULT_GARBAGE:
+                       ring_buffer_drain(rbuf, p->read_so_far);
+                       break;
+               }
+
+               len -= p->read_so_far;
+               wrap -= p->read_so_far;
+               p->read_so_far = 0;
+
+               /*
+                * Handle situations where we receive two command lines in
+                * one read, which should not be possible (and implies the
+                * earlier command should be canceled.
+                *
+                * e.g. AT+CMD1\rAT+CMD2
+                */
+               if (result != PARSER_RESULT_GARBAGE) {
+                       ring_buffer_drain(rbuf, len);
+                       break;
+               }
+       }
+
+       p->in_read_handler = FALSE;
+
+       if (p->destroyed)
+               g_free(p);
+}
+
+static gboolean can_write_data(gpointer data)
+{
+       GAtServer *server = data;
+       gsize bytes_written;
+       gsize towrite;
+       struct ring_buffer *write_buf;
+       unsigned char *buf;
+#ifdef WRITE_SCHEDULER_DEBUG
+       int limiter;
+#endif
+
+       if (!server->write_queue)
+               return FALSE;
+
+       /* Write data out from the head of the queue */
+       write_buf = g_queue_peek_head(server->write_queue);
+
+       buf = ring_buffer_read_ptr(write_buf, 0);
+
+       towrite = ring_buffer_len_no_wrap(write_buf);
+
+#ifdef WRITE_SCHEDULER_DEBUG
+       limiter = towrite;
+
+       if (limiter > 5)
+               limiter = 5;
+#endif
+
+       bytes_written = g_at_io_write(server->io,
+                       (char *)buf,
+#ifdef WRITE_SCHEDULER_DEBUG
+                       limiter
+#else
+                       towrite
+#endif
+                       );
+
+       if (bytes_written == 0)
+               return FALSE;
+
+       ring_buffer_drain(write_buf, bytes_written);
+
+       /* All data in current buffer is written, free it
+        * unless it's the last buffer in the queue.
+        */
+       if ((ring_buffer_len(write_buf) == 0) &&
+                       (g_queue_get_length(server->write_queue) > 1)) {
+               write_buf = g_queue_pop_head(server->write_queue);
+               ring_buffer_free(write_buf);
+               write_buf = g_queue_peek_head(server->write_queue);
+       }
+
+       if (ring_buffer_len(write_buf) > 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void write_queue_free(GQueue *write_queue)
+{
+       struct ring_buffer *write_buf;
+
+       while ((write_buf = g_queue_pop_head(write_queue)))
+               ring_buffer_free(write_buf);
+
+       g_queue_free(write_queue);
+}
+
+static void g_at_server_cleanup(GAtServer *server)
+{
+       /* Cleanup pending data to write */
+       write_queue_free(server->write_queue);
+
+       g_hash_table_destroy(server->command_list);
+       server->command_list = NULL;
+
+       g_free(server->last_line);
+
+       g_at_io_unref(server->io);
+       server->io = NULL;
+}
+
+static void io_disconnect(gpointer user_data)
+{
+       GAtServer *server = user_data;
+
+       g_at_server_cleanup(server);
+
+       if (server->user_disconnect)
+               server->user_disconnect(server->user_disconnect_data);
+}
+
+static void server_wakeup_writer(GAtServer *server)
+{
+       g_at_io_set_write_handler(server->io, can_write_data, server);
+}
+
+static void at_notify_node_destroy(gpointer data)
+{
+       struct at_command *node = data;
+
+       if (node->destroy_notify)
+               node->destroy_notify(node->user_data);
+
+       g_free(node);
+}
+
+static void basic_command_register(GAtServer *server)
+{
+       g_at_server_register(server, "S0", at_s0_cb, NULL, NULL);
+       g_at_server_register(server, "S3", at_s3_cb, NULL, NULL);
+       g_at_server_register(server, "S4", at_s4_cb, NULL, NULL);
+       g_at_server_register(server, "S5", at_s5_cb, NULL, NULL);
+       g_at_server_register(server, "E", at_e_cb, NULL, NULL);
+       g_at_server_register(server, "Q", at_q_cb, NULL, NULL);
+       g_at_server_register(server, "V", at_v_cb, NULL, NULL);
+       g_at_server_register(server, "X", at_x_cb, NULL, NULL);
+       g_at_server_register(server, "S6", at_s6_cb, NULL, NULL);
+       g_at_server_register(server, "S7", at_s7_cb, NULL, NULL);
+       g_at_server_register(server, "S8", at_s8_cb, NULL, NULL);
+       g_at_server_register(server, "S10", at_s10_cb, NULL, NULL);
+       g_at_server_register(server, "&C", at_c109_cb, NULL, NULL);
+       g_at_server_register(server, "&D", at_c108_cb, NULL, NULL);
+       g_at_server_register(server, "Z", at_z_cb, NULL, NULL);
+       g_at_server_register(server, "&F", at_f_cb, NULL, NULL);
+       g_at_server_register(server, "L", at_l_cb, NULL, NULL);
+       g_at_server_register(server, "M", at_m_cb, NULL, NULL);
+       g_at_server_register(server, "T", at_t_cb, NULL, NULL);
+       g_at_server_register(server, "P", at_p_cb, NULL, NULL);
+}
+
+GAtServer *g_at_server_new(GIOChannel *io)
+{
+       GAtServer *server;
+
+       if (io == NULL)
+               return NULL;
+
+       server = g_try_new0(GAtServer, 1);
+       if (server == NULL)
+               return NULL;
+
+       server->ref_count = 1;
+       v250_settings_create(&server->v250);
+       server->io = g_at_io_new(io);
+       if (!server->io)
+               goto error;
+
+       g_at_io_set_disconnect_function(server->io, io_disconnect, server);
+
+       server->command_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free,
+                                                       at_notify_node_destroy);
+
+       server->write_queue = g_queue_new();
+       if (!server->write_queue)
+               goto error;
+
+       if (allocate_next(server) == NULL)
+               goto error;
+
+       server->max_read_attempts = 3;
+
+       g_at_io_set_read_handler(server->io, new_bytes, server);
+
+       basic_command_register(server);
+
+       return server;
+
+error:
+       g_at_io_unref(server->io);
+
+       if (server->command_list)
+               g_hash_table_destroy(server->command_list);
+
+       if (server->write_queue)
+               write_queue_free(server->write_queue);
+
+       if (server)
+               g_free(server);
+
+       return NULL;
+}
+
+GIOChannel *g_at_server_get_channel(GAtServer *server)
+{
+       if (server == NULL || server->io == NULL)
+               return NULL;
+
+       return g_at_io_get_channel(server->io);
+}
+
+GAtIO *g_at_server_get_io(GAtServer *server)
+{
+       if (server == NULL)
+               return NULL;
+
+       return server->io;
+}
+
+GAtServer *g_at_server_ref(GAtServer *server)
+{
+       if (server == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&server->ref_count);
+
+       return server;
+}
+
+void g_at_server_suspend(GAtServer *server)
+{
+       if (server == NULL)
+               return;
+
+       g_at_io_set_write_handler(server->io, NULL, NULL);
+       g_at_io_set_read_handler(server->io, NULL, NULL);
+
+       g_at_io_set_debug(server->io, NULL, NULL);
+}
+
+void g_at_server_resume(GAtServer *server)
+{
+       if (server == NULL)
+               return;
+
+       if (g_at_io_get_channel(server->io) == NULL) {
+               io_disconnect(server);
+               return;
+       }
+
+       g_at_io_set_disconnect_function(server->io, io_disconnect, server);
+
+       g_at_io_set_debug(server->io, server->debugf, server->debug_data);
+       g_at_io_set_read_handler(server->io, new_bytes, server);
+
+       if (g_queue_get_length(server->write_queue) > 0)
+               server_wakeup_writer(server);
+}
+
+void g_at_server_unref(GAtServer *server)
+{
+       gboolean is_zero;
+
+       if (server == NULL)
+               return;
+
+       is_zero = g_atomic_int_dec_and_test(&server->ref_count);
+
+       if (is_zero == FALSE)
+               return;
+
+       if (server->io) {
+               g_at_server_suspend(server);
+               g_at_server_cleanup(server);
+       }
+
+       g_at_server_shutdown(server);
+
+       /* glib delays the destruction of the watcher until it exits, this
+        * means we can't free the data just yet, even though we've been
+        * destroyed already.  We have to wait until the read_watcher
+        * destroy function gets called
+        */
+       if (server->in_read_handler)
+               server->destroyed = TRUE;
+       else
+               g_free(server);
+}
+
+gboolean g_at_server_shutdown(GAtServer *server)
+{
+       if (server == NULL)
+               return FALSE;
+
+       /* Don't trigger user disconnect on shutdown */
+       server->user_disconnect = NULL;
+       server->user_disconnect_data = NULL;
+
+       return TRUE;
+}
+
+gboolean g_at_server_set_echo(GAtServer *server, gboolean echo)
+{
+       if (server == NULL)
+               return FALSE;
+
+       server->v250.echo = echo;
+
+       return TRUE;
+}
+
+gboolean g_at_server_set_disconnect_function(GAtServer *server,
+                                               GAtDisconnectFunc disconnect,
+                                               gpointer user_data)
+{
+       if (server == NULL)
+               return FALSE;
+
+       server->user_disconnect = disconnect;
+       server->user_disconnect_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_at_server_set_debug(GAtServer *server, GAtDebugFunc func,
+                                               gpointer user_data)
+{
+       if (server == NULL)
+               return FALSE;
+
+       server->debugf = func;
+       server->debug_data = user_data;
+
+       g_at_io_set_debug(server->io, server->debugf, server->debug_data);
+
+       return TRUE;
+}
+
+gboolean g_at_server_register(GAtServer *server, const char *prefix,
+                                       GAtServerNotifyFunc notify,
+                                       gpointer user_data,
+                                       GDestroyNotify destroy_notify)
+{
+       struct at_command *node;
+
+       if (server == NULL || server->command_list == NULL)
+               return FALSE;
+
+       if (notify == NULL)
+               return FALSE;
+
+       if (prefix == NULL || strlen(prefix) == 0)
+               return FALSE;
+
+       node = g_try_new0(struct at_command, 1);
+       if (node == NULL)
+               return FALSE;
+
+       node->notify = notify;
+       node->user_data = user_data;
+       node->destroy_notify = destroy_notify;
+
+       g_hash_table_replace(server->command_list, g_strdup(prefix), node);
+
+       return TRUE;
+}
+
+gboolean g_at_server_unregister(GAtServer *server, const char *prefix)
+{
+       struct at_command *node;
+
+       if (server == NULL || server->command_list == NULL)
+               return FALSE;
+
+       if (prefix == NULL || strlen(prefix) == 0)
+               return FALSE;
+
+       node = g_hash_table_lookup(server->command_list, prefix);
+       if (node == NULL)
+               return FALSE;
+
+       g_hash_table_remove(server->command_list, prefix);
+
+       return TRUE;
+}
+
+gboolean g_at_server_set_finish_callback(GAtServer *server,
+                                               GAtServerFinishFunc finishf,
+                                               gpointer user_data)
+{
+       if (server == NULL)
+               return FALSE;
+
+       server->finishf = finishf;
+       server->finish_data = user_data;
+
+       return TRUE;
+}
+
+gboolean g_at_server_command_pending(GAtServer *server)
+{
+       if (server == NULL)
+               return FALSE;
+
+       return server->final_async;
+}
diff --git a/gatchat/gatserver.h b/gatchat/gatserver.h
new file mode 100644 (file)
index 0000000..1a9ca5b
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ *
+ *  AT Server library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATSERVER_H
+#define __GATSERVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gatresult.h"
+#include "gatutil.h"
+#include "gatio.h"
+
+struct _GAtServer;
+
+typedef struct _GAtServer GAtServer;
+
+/* V.250 Table 1/V.250 Result codes */
+enum _GAtServerResult {
+       G_AT_SERVER_RESULT_OK = 0,
+       G_AT_SERVER_RESULT_CONNECT = 1,
+       G_AT_SERVER_RESULT_RING = 2,
+       G_AT_SERVER_RESULT_NO_CARRIER = 3,
+       G_AT_SERVER_RESULT_ERROR = 4,
+       G_AT_SERVER_RESULT_NO_DIALTONE = 6,
+       G_AT_SERVER_RESULT_BUSY = 7,
+       G_AT_SERVER_RESULT_NO_ANSWER = 8,
+       G_AT_SERVER_RESULT_EXT_ERROR = 256,
+};
+
+typedef enum _GAtServerResult GAtServerResult;
+
+/* Types of AT command:
+ * COMMAND_ONLY: command without any sub-parameters, e.g. ATA, AT+CLCC
+ * QUERY: command followed by '?', e.g. AT+CPIN?
+ * SUPPORT: command followed by '=?', e.g. AT+CSMS=?
+ * SET: command followed by '=', e.g. AT+CLIP=1
+ *     or, basic command followed with sub-parameters, e.g. ATD12345;
+ */
+enum _GAtServerRequestType {
+       G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY,
+       G_AT_SERVER_REQUEST_TYPE_QUERY,
+       G_AT_SERVER_REQUEST_TYPE_SUPPORT,
+       G_AT_SERVER_REQUEST_TYPE_SET,
+};
+
+typedef enum _GAtServerRequestType GAtServerRequestType;
+
+typedef void (*GAtServerNotifyFunc)(GAtServer *server,
+                                       GAtServerRequestType type,
+                                       GAtResult *result, gpointer user_data);
+
+typedef void (*GAtServerFinishFunc)(GAtServer *server, gpointer user_data);
+
+GAtServer *g_at_server_new(GIOChannel *io);
+GIOChannel *g_at_server_get_channel(GAtServer *server);
+GAtIO *g_at_server_get_io(GAtServer *server);
+
+GAtServer *g_at_server_ref(GAtServer *server);
+void g_at_server_suspend(GAtServer *server);
+void g_at_server_resume(GAtServer *server);
+void g_at_server_unref(GAtServer *server);
+
+gboolean g_at_server_shutdown(GAtServer *server);
+
+gboolean g_at_server_set_echo(GAtServer *server, gboolean echo);
+gboolean g_at_server_set_disconnect_function(GAtServer *server,
+                                       GAtDisconnectFunc disconnect,
+                                       gpointer user_data);
+gboolean g_at_server_set_debug(GAtServer *server,
+                                       GAtDebugFunc func,
+                                       gpointer user_data);
+
+gboolean g_at_server_register(GAtServer *server, const char *prefix,
+                                       GAtServerNotifyFunc notify,
+                                       gpointer user_data,
+                                       GDestroyNotify destroy_notify);
+gboolean g_at_server_unregister(GAtServer *server, const char *prefix);
+
+/* Send a final result code. E.g. G_AT_SERVER_RESULT_NO_DIALTONE */
+void g_at_server_send_final(GAtServer *server, GAtServerResult result);
+
+/* Send an extended final result code. E.g. +CME ERROR: SIM failure. */
+void g_at_server_send_ext_final(GAtServer *server, const char *result);
+
+/* Send an intermediate result code to report the progress. E.g. CONNECT */
+void g_at_server_send_intermediate(GAtServer *server, const char *result);
+
+/* Send an unsolicited result code. E.g. RING */
+void g_at_server_send_unsolicited(GAtServer *server, const char *result);
+
+/*
+ * Send a single response line for the command.  The line should be no longer
+ * than 2048 characters.  If the response contains multiple lines, use
+ * FALSE for the 'last' parameter for lines 1 .. n -1, and 'TRUE' for the last
+ * line.  This is required for formatting of 27.007 compliant multi-line
+ * responses.
+ */
+void g_at_server_send_info(GAtServer *server, const char *line, gboolean last);
+
+gboolean g_at_server_set_finish_callback(GAtServer *server,
+                                               GAtServerFinishFunc finishf,
+                                               gpointer user_data);
+gboolean g_at_server_command_pending(GAtServer *server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATSERVER_H */
diff --git a/gatchat/gatsyntax.c b/gatchat/gatsyntax.c
new file mode 100644 (file)
index 0000000..f88c068
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "gatsyntax.h"
+
+enum GSMV1_STATE {
+       GSMV1_STATE_IDLE = 0,
+       GSMV1_STATE_INITIAL_CR,
+       GSMV1_STATE_INITIAL_LF,
+       GSMV1_STATE_RESPONSE,
+       GSMV1_STATE_RESPONSE_STRING,
+       GSMV1_STATE_TERMINATOR_CR,
+       GSMV1_STATE_GUESS_MULTILINE_RESPONSE,
+       GSMV1_STATE_MULTILINE_RESPONSE,
+       GSMV1_STATE_MULTILINE_TERMINATOR_CR,
+       GSMV1_STATE_PDU_CHECK_EXTRA_CR,
+       GSMV1_STATE_PDU_CHECK_EXTRA_LF,
+       GSMV1_STATE_PDU,
+       GSMV1_STATE_PDU_CR,
+       GSMV1_STATE_PROMPT,
+       GSMV1_STATE_ECHO,
+       GSMV1_STATE_PPP_DATA,
+       GSMV1_STATE_SHORT_PROMPT,
+       GSMV1_STATE_SHORT_PROMPT_CR,
+};
+
+enum GSM_PERMISSIVE_STATE {
+       GSM_PERMISSIVE_STATE_IDLE = 0,
+       GSM_PERMISSIVE_STATE_RESPONSE,
+       GSM_PERMISSIVE_STATE_RESPONSE_STRING,
+       GSM_PERMISSIVE_STATE_GUESS_PDU,
+       GSM_PERMISSIVE_STATE_PDU,
+       GSM_PERMISSIVE_STATE_PROMPT,
+       GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT,
+       GSM_PERMISSIVE_STATE_SHORT_PROMPT,
+};
+
+static void gsmv1_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint)
+{
+       switch (hint) {
+       case G_AT_SYNTAX_EXPECT_PDU:
+               syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_CR;
+               break;
+       case G_AT_SYNTAX_EXPECT_MULTILINE:
+               syntax->state = GSMV1_STATE_GUESS_MULTILINE_RESPONSE;
+               break;
+       case G_AT_SYNTAX_EXPECT_SHORT_PROMPT:
+               syntax->state = GSMV1_STATE_SHORT_PROMPT;
+               break;
+       default:
+               break;
+       };
+}
+
+static GAtSyntaxResult gsmv1_feed(GAtSyntax *syntax,
+                                       const char *bytes, gsize *len)
+{
+       gsize i = 0;
+       GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE;
+
+       while (i < *len) {
+               char byte = bytes[i];
+
+               switch (syntax->state) {
+               case GSMV1_STATE_IDLE:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_INITIAL_CR;
+                       else if (byte == '~')
+                               syntax->state = GSMV1_STATE_PPP_DATA;
+                       else
+                               syntax->state = GSMV1_STATE_ECHO;
+                       break;
+
+               case GSMV1_STATE_INITIAL_CR:
+                       if (byte == '\n')
+                               syntax->state = GSMV1_STATE_INITIAL_LF;
+                       else if (byte == '\r') {
+                               syntax->state = GSMV1_STATE_IDLE;
+                               return G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+                       } else
+                               syntax->state = GSMV1_STATE_ECHO;
+                       break;
+
+               case GSMV1_STATE_INITIAL_LF:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_TERMINATOR_CR;
+                       else if (byte == '>')
+                               syntax->state = GSMV1_STATE_PROMPT;
+                       else if (byte == '"')
+                               syntax->state = GSMV1_STATE_RESPONSE_STRING;
+                       else
+                               syntax->state = GSMV1_STATE_RESPONSE;
+                       break;
+
+               case GSMV1_STATE_RESPONSE:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_TERMINATOR_CR;
+                       else if (byte == '"')
+                               syntax->state = GSMV1_STATE_RESPONSE_STRING;
+                       break;
+
+               case GSMV1_STATE_RESPONSE_STRING:
+                       if (byte == '"')
+                               syntax->state = GSMV1_STATE_RESPONSE;
+                       break;
+
+               case GSMV1_STATE_TERMINATOR_CR:
+                       syntax->state = GSMV1_STATE_IDLE;
+
+                       if (byte == '\n') {
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_LINE;
+                       } else
+                               res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+
+                       goto out;
+
+               case GSMV1_STATE_GUESS_MULTILINE_RESPONSE:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_INITIAL_CR;
+                       else
+                               syntax->state = GSMV1_STATE_MULTILINE_RESPONSE;
+                       break;
+
+               case GSMV1_STATE_MULTILINE_RESPONSE:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_MULTILINE_TERMINATOR_CR;
+                       break;
+
+               case GSMV1_STATE_MULTILINE_TERMINATOR_CR:
+                       syntax->state = GSMV1_STATE_IDLE;
+
+                       if (byte == '\n') {
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_MULTILINE;
+                       } else
+                               res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+
+                       goto out;
+
+               /* Some 27.007 compliant modems still get this wrong.  They
+                * insert an extra CRLF between the command and he PDU,
+                * in effect making them two separate lines.  We try to
+                * handle this case gracefully
+                */
+               case GSMV1_STATE_PDU_CHECK_EXTRA_CR:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_LF;
+                       else
+                               syntax->state = GSMV1_STATE_PDU;
+                       break;
+
+               case GSMV1_STATE_PDU_CHECK_EXTRA_LF:
+                       res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+                       syntax->state = GSMV1_STATE_PDU;
+
+                       if (byte == '\n')
+                               i += 1;
+
+                       goto out;
+
+               case GSMV1_STATE_PDU:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_PDU_CR;
+                       break;
+
+               case GSMV1_STATE_PDU_CR:
+                       syntax->state = GSMV1_STATE_IDLE;
+
+                       if (byte == '\n') {
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_PDU;
+                       } else
+                               res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+
+                       goto out;
+
+               case GSMV1_STATE_PROMPT:
+                       if (byte == ' ') {
+                               syntax->state = GSMV1_STATE_IDLE;
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_PROMPT;
+                               goto out;
+                       }
+
+                       syntax->state = GSMV1_STATE_RESPONSE;
+                       return G_AT_SYNTAX_RESULT_UNSURE;
+
+               case GSMV1_STATE_ECHO:
+                       /* This handles the case of echo of the PDU terminated
+                        * by CtrlZ character
+                        */
+                       if (byte == 26 || byte == '\r') {
+                               syntax->state = GSMV1_STATE_IDLE;
+                               res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+                               i += 1;
+                               goto out;
+                       }
+
+                       break;
+
+               case GSMV1_STATE_PPP_DATA:
+                       if (byte == '~') {
+                               syntax->state = GSMV1_STATE_IDLE;
+                               res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+                               i += 1;
+                               goto out;
+                       }
+
+                       break;
+
+               case GSMV1_STATE_SHORT_PROMPT:
+                       if (byte == '\r')
+                               syntax->state = GSMV1_STATE_SHORT_PROMPT_CR;
+                       else
+                               syntax->state = GSMV1_STATE_ECHO;
+
+                       break;
+
+               case GSMV1_STATE_SHORT_PROMPT_CR:
+                       if (byte == '\n') {
+                               syntax->state = GSMV1_STATE_IDLE;
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_PROMPT;
+                               goto out;
+                       }
+
+                       syntax->state = GSMV1_STATE_RESPONSE;
+                       return G_AT_SYNTAX_RESULT_UNSURE;
+
+               default:
+                       break;
+               };
+
+               i += 1;
+       }
+
+out:
+       *len = i;
+       return res;
+}
+
+static void gsm_permissive_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint)
+{
+       if (hint == G_AT_SYNTAX_EXPECT_PDU)
+               syntax->state = GSM_PERMISSIVE_STATE_GUESS_PDU;
+       else if (hint == G_AT_SYNTAX_EXPECT_SHORT_PROMPT)
+               syntax->state = GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT;
+}
+
+static GAtSyntaxResult gsm_permissive_feed(GAtSyntax *syntax,
+                                               const char *bytes, gsize *len)
+{
+       gsize i = 0;
+       GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE;
+
+       while (i < *len) {
+               char byte = bytes[i];
+
+               switch (syntax->state) {
+               case GSM_PERMISSIVE_STATE_IDLE:
+                       if (byte == '\r' || byte == '\n')
+                               /* ignore */;
+                       else if (byte == '>')
+                               syntax->state = GSM_PERMISSIVE_STATE_PROMPT;
+                       else
+                               syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
+                       break;
+
+               case GSM_PERMISSIVE_STATE_RESPONSE:
+                       if (byte == '\r') {
+                               syntax->state = GSM_PERMISSIVE_STATE_IDLE;
+
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_LINE;
+                               goto out;
+                       } else if (byte == '"')
+                               syntax->state =
+                                       GSM_PERMISSIVE_STATE_RESPONSE_STRING;
+                       break;
+
+               case GSM_PERMISSIVE_STATE_RESPONSE_STRING:
+                       if (byte == '"')
+                               syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
+                       break;
+
+               case GSM_PERMISSIVE_STATE_GUESS_PDU:
+                       if (byte != '\r' && byte != '\n')
+                               syntax->state = GSM_PERMISSIVE_STATE_PDU;
+                       break;
+
+               case GSM_PERMISSIVE_STATE_PDU:
+                       if (byte == '\r') {
+                               syntax->state = GSM_PERMISSIVE_STATE_IDLE;
+
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_PDU;
+                               goto out;
+                       }
+                       break;
+
+               case GSM_PERMISSIVE_STATE_PROMPT:
+                       if (byte == ' ') {
+                               syntax->state = GSM_PERMISSIVE_STATE_IDLE;
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_PROMPT;
+                               goto out;
+                       }
+
+                       syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
+                       return G_AT_SYNTAX_RESULT_UNSURE;
+
+               case GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT:
+                       if (byte == '\n')
+                               /* ignore */;
+                       else if (byte == '\r')
+                               syntax->state =
+                                       GSM_PERMISSIVE_STATE_SHORT_PROMPT;
+                       else
+                               syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
+                       break;
+
+               case GSM_PERMISSIVE_STATE_SHORT_PROMPT:
+                       if (byte == '\n') {
+                               syntax->state = GSM_PERMISSIVE_STATE_IDLE;
+                               i += 1;
+                               res = G_AT_SYNTAX_RESULT_PROMPT;
+                               goto out;
+                       }
+
+                       syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
+                       return G_AT_SYNTAX_RESULT_UNSURE;
+
+               default:
+                       break;
+               };
+
+               i += 1;
+       }
+
+out:
+       *len = i;
+       return res;
+}
+
+GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
+                                       GAtSyntaxSetHintFunc hint,
+                                       int initial_state)
+{
+       GAtSyntax *syntax;
+
+       syntax = g_new0(GAtSyntax, 1);
+
+       syntax->feed = feed;
+       syntax->set_hint = hint;
+       syntax->state = initial_state;
+       syntax->ref_count = 1;
+
+       return syntax;
+}
+
+
+GAtSyntax *g_at_syntax_new_gsmv1(void)
+{
+       return g_at_syntax_new_full(gsmv1_feed, gsmv1_hint, GSMV1_STATE_IDLE);
+}
+
+GAtSyntax *g_at_syntax_new_gsm_permissive(void)
+{
+       return g_at_syntax_new_full(gsm_permissive_feed, gsm_permissive_hint,
+                                       GSM_PERMISSIVE_STATE_IDLE);
+}
+
+GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax)
+{
+       if (syntax == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&syntax->ref_count);
+
+       return syntax;
+}
+
+void g_at_syntax_unref(GAtSyntax *syntax)
+{
+       gboolean is_zero;
+
+       if (syntax == NULL)
+               return;
+
+       is_zero = g_atomic_int_dec_and_test(&syntax->ref_count);
+
+       if (is_zero)
+               g_free(syntax);
+}
diff --git a/gatchat/gatsyntax.h b/gatchat/gatsyntax.h
new file mode 100644 (file)
index 0000000..1b991ad
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATSYNTAX_H
+#define __GATSYNTAX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum _GAtSyntaxExpectHint {
+       G_AT_SYNTAX_EXPECT_PDU,
+       G_AT_SYNTAX_EXPECT_MULTILINE,
+       G_AT_SYNTAX_EXPECT_PROMPT,
+       G_AT_SYNTAX_EXPECT_SHORT_PROMPT
+};
+
+typedef enum _GAtSyntaxExpectHint GAtSyntaxExpectHint;
+
+enum _GAtSyntaxResult {
+       G_AT_SYNTAX_RESULT_UNRECOGNIZED,
+       G_AT_SYNTAX_RESULT_UNSURE,
+       G_AT_SYNTAX_RESULT_LINE,
+       G_AT_SYNTAX_RESULT_MULTILINE,
+       G_AT_SYNTAX_RESULT_PDU,
+       G_AT_SYNTAX_RESULT_PROMPT,
+};
+
+typedef enum _GAtSyntaxResult GAtSyntaxResult;
+
+typedef struct _GAtSyntax GAtSyntax;
+
+typedef void (*GAtSyntaxSetHintFunc)(GAtSyntax *syntax,
+                                       GAtSyntaxExpectHint hint);
+typedef GAtSyntaxResult (*GAtSyntaxFeedFunc)(GAtSyntax *syntax,
+                                               const char *bytes, gsize *len);
+
+struct _GAtSyntax {
+       gint ref_count;
+       int state;
+       GAtSyntaxSetHintFunc set_hint;
+       GAtSyntaxFeedFunc feed;
+};
+
+
+GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
+                                       GAtSyntaxSetHintFunc hint,
+                                       int initial_state);
+
+/* This syntax implements very strict checking of 27.007 standard, which means
+ * it might not work with a majority of modems.  However, it does handle echo
+ * properly and can be used to detect a modem's deviations from the relevant
+ * standards.
+ */
+GAtSyntax *g_at_syntax_new_gsmv1(void);
+
+/* This syntax implements an extremely lax parser that can handle a variety
+ * of modems.  Unfortunately it does not deal with echo at all, so echo must
+ * be explicitly turned off before using the parser
+ */
+GAtSyntax *g_at_syntax_new_gsm_permissive(void);
+
+GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax);
+void g_at_syntax_unref(GAtSyntax *syntax);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATSYNTAX_H */
diff --git a/gatchat/gattty.c b/gatchat/gattty.c
new file mode 100644 (file)
index 0000000..3e447e2
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+
+#include <glib.h>
+
+#include "gattty.h"
+
+static gboolean set_baud(const char *baud, struct termios *ti)
+{
+       speed_t speed;
+
+       if (g_str_equal(baud, "300"))
+               speed = B300;
+       else if (g_str_equal(baud, "1200"))
+               speed = B1200;
+       else if (g_str_equal(baud, "2400"))
+               speed = B2400;
+       else if (g_str_equal(baud, "4800"))
+               speed = B4800;
+       else if (g_str_equal(baud, "9600"))
+               speed = B9600;
+       else if (g_str_equal(baud, "19200"))
+               speed = B19200;
+       else if (g_str_equal(baud, "38400"))
+               speed = B38400;
+       else if (g_str_equal(baud, "57600"))
+               speed = B57600;
+       else if (g_str_equal(baud, "115200"))
+               speed = B115200;
+       else if (g_str_equal(baud, "230400"))
+               speed = B230400;
+       else if (g_str_equal(baud, "460800"))
+               speed = B460800;
+       else if (g_str_equal(baud, "500000"))
+               speed = B500000;
+       else if (g_str_equal(baud, "576000"))
+               speed = B576000;
+       else if (g_str_equal(baud, "921600"))
+               speed = B921600;
+       else if (g_str_equal(baud, "1000000"))
+               speed = B1000000;
+       else if (g_str_equal(baud, "1152000"))
+               speed = B1152000;
+       else if (g_str_equal(baud, "1500000"))
+               speed = B1500000;
+       else if (g_str_equal(baud, "2000000"))
+               speed = B2000000;
+#ifdef B2500000
+       else if (g_str_equal(baud, "2500000"))
+               speed = B2500000;
+#endif
+#ifdef B3000000
+       else if (g_str_equal(baud, "3000000"))
+               speed = B3000000;
+#endif
+#ifdef B3500000
+       else if (g_str_equal(baud, "3500000"))
+               speed = B3500000;
+#endif
+#ifdef B4000000
+       else if (g_str_equal(baud, "4000000"))
+               speed = B4000000;
+#endif
+       else
+               return FALSE;
+
+       cfsetospeed(ti, speed);
+       cfsetispeed(ti, speed);
+
+       return TRUE;
+}
+
+static gboolean set_read(const char *bits, struct termios *ti)
+{
+       if (g_str_equal(bits, "off"))
+               ti->c_cflag &= ~(CREAD);
+       else if (g_str_equal(bits, "on"))
+               ti->c_cflag |= CREAD;
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean set_stop_bits(const char *bits, struct termios *ti)
+{
+       if (g_str_equal(bits, "1"))
+               ti->c_cflag &= ~(CSTOPB);
+       else if (g_str_equal(bits, "2"))
+               ti->c_cflag |= CSTOPB;
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean set_data_bits(const char *bits, struct termios *ti)
+{
+       if (g_str_equal(bits, "7")) {
+               ti->c_cflag &= ~(CSIZE);
+               ti->c_cflag |= CS7;
+       } else if (g_str_equal(bits, "8")) {
+               ti->c_cflag &= ~(CSIZE);
+               ti->c_cflag |= CS8;
+       } else
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean set_parity(const char *parity, struct termios *ti)
+{
+       if (g_str_equal(parity, "none"))
+               ti->c_cflag &= ~(PARENB);
+       else if (g_str_equal(parity, "even")) {
+               ti->c_cflag |= PARENB;
+               ti->c_cflag &= ~(PARODD);
+       } else if (g_str_equal(parity, "odd")) {
+               ti->c_cflag |= PARENB;
+               ti->c_cflag |= PARODD;
+       } else
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean set_xonxoff(const char *xonxoff, struct termios *ti)
+{
+       if (g_str_equal(xonxoff, "on")) {
+               ti->c_iflag |= (IXON | IXOFF | IXANY);
+               ti->c_cc[VSTART] = 17;
+               ti->c_cc[VSTOP] = 19;
+       } else if (g_str_equal(xonxoff, "off"))
+               ti->c_iflag &= ~(IXON | IXOFF | IXANY);
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean set_rtscts(const char *rtscts, struct termios *ti)
+{
+       if (g_str_equal(rtscts, "on"))
+               ti->c_cflag |= CRTSCTS;
+       else if (g_str_equal(rtscts, "off"))
+               ti->c_cflag &= ~(CRTSCTS);
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean set_local(const char *local, struct termios *ti)
+{
+       if (g_str_equal(local, "on"))
+               ti->c_cflag |= CLOCAL;
+       else if (g_str_equal(local, "off"))
+               ti->c_cflag &= ~(CLOCAL);
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+static int open_device(const char *tty, GHashTable *options)
+{
+       struct termios ti;
+       int fd;
+
+       /* Switch TTY to raw mode */
+       memset(&ti, 0, sizeof(ti));
+       cfmakeraw(&ti);
+
+       if (options) {
+               GHashTableIter iter;
+               const char *key;
+               const char *value;
+
+               g_hash_table_iter_init (&iter, options);
+               while (g_hash_table_iter_next(&iter, (void *) &key,
+                                                       (void *) &value)) {
+                       gboolean ok = FALSE;
+
+                       if (g_str_equal(key, "Baud"))
+                               ok = set_baud(value, &ti);
+                       else if (g_str_equal(key, "StopBits"))
+                               ok = set_stop_bits(value, &ti);
+                       else if (g_str_equal(key, "DataBits"))
+                               ok = set_data_bits(value, &ti);
+                       else if (g_str_equal(key, "Parity"))
+                               ok = set_parity(value, &ti);
+                       else if (g_str_equal(key, "XonXoff"))
+                               ok = set_xonxoff(value, &ti);
+                       else if (g_str_equal(key, "RtsCts"))
+                               ok = set_rtscts(value, &ti);
+                       else if (g_str_equal(key, "Local"))
+                               ok = set_local(value, &ti);
+                       else if (g_str_equal(key, "Read"))
+                               ok = set_read(value, &ti);
+
+                       if (ok == FALSE)
+                               return -1;
+               }
+       }
+
+       fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
+       if (fd < 0)
+               return -1;
+
+       tcflush(fd, TCIOFLUSH);
+       tcsetattr(fd, TCSANOW, &ti);
+
+       return fd;
+}
+
+GIOChannel *g_at_tty_open(const char *tty, GHashTable *options)
+{
+       GIOChannel *channel;
+       int fd;
+
+       fd = open_device(tty, options);
+       if (fd < 0)
+               return NULL;
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL) {
+               close(fd);
+               return NULL;
+       }
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       return channel;
+}
+
+GIOChannel *g_at_tty_open_qcdm(const char *tty)
+{
+       GIOChannel *channel;
+       struct termios ti;
+       int fd;
+
+        fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
+        if (fd < 0)
+                return NULL;
+
+       /* Switch TTY to raw mode */
+       memset(&ti, 0, sizeof(ti));
+       cfmakeraw(&ti);
+
+       /* No parity, 1 stop bit */
+       ti.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+       ti.c_cflag |= (B115200 | CS8);
+
+       if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+               close(fd);
+               return NULL;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL) {
+               close(fd);
+               return NULL;
+       }
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       return channel;
+}
diff --git a/gatchat/gattty.h b/gatchat/gattty.h
new file mode 100644 (file)
index 0000000..2343b92
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATTTY_H
+#define __GATTTY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * Opens a serial port given by tty.  If options is NULL, then the serial port
+ * is opened in raw mode.  Otherwise the options are parsed and set accordingly
+ *
+ * The following keys / values are recognized (all strings)
+ *
+ * "Baud" - "300", "600", etc
+ * "Stopbits" - "1", "2"
+ * "Databits" - "7", "8"
+ * "Parity" - "none", "odd", "even"
+ * "XonXoff" - "on", "off"
+ * "RtsCts" - "on", "off"
+ * "Local" - "on", "off"
+ * "Read" - "on, "off"
+ */
+GIOChannel *g_at_tty_open(const char *tty, GHashTable *options);
+
+GIOChannel *g_at_tty_open_qcdm(const char *tty);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATTTY_H */
diff --git a/gatchat/gatutil.c b/gatchat/gatutil.c
new file mode 100644 (file)
index 0000000..a2528e1
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gatutil.h"
+
+void g_at_util_debug_chat(gboolean in, const char *str, gsize len,
+                               GAtDebugFunc debugf, gpointer user_data)
+{
+       char type = in ? '<' : '>';
+       gsize escaped = 2; /* Enough for '<', ' ' */
+       char *escaped_str;
+       const char *esc = "<ESC>";
+       gsize esc_size = strlen(esc);
+       const char *ctrlz = "<CtrlZ>";
+       gsize ctrlz_size = strlen(ctrlz);
+       gsize i;
+
+       if (debugf == NULL || !len)
+               return;
+
+       for (i = 0; i < len; i++) {
+               char c = str[i];
+
+               if (g_ascii_isprint(c))
+                       escaped += 1;
+               else if (c == '\r' || c == '\t' || c == '\n')
+                       escaped += 2;
+               else if (c == 26)
+                       escaped += ctrlz_size;
+               else if (c == 25)
+                       escaped += esc_size;
+               else
+                       escaped += 4;
+       }
+
+       escaped_str = g_try_malloc(escaped + 1);
+       if (escaped_str == NULL)
+               return;
+
+       escaped_str[0] = type;
+       escaped_str[1] = ' ';
+       escaped_str[2] = '\0';
+       escaped_str[escaped] = '\0';
+
+       for (escaped = 2, i = 0; i < len; i++) {
+               unsigned char c = str[i];
+
+               switch (c) {
+               case '\r':
+                       escaped_str[escaped++] = '\\';
+                       escaped_str[escaped++] = 'r';
+                       break;
+               case '\t':
+                       escaped_str[escaped++] = '\\';
+                       escaped_str[escaped++] = 't';
+                       break;
+               case '\n':
+                       escaped_str[escaped++] = '\\';
+                       escaped_str[escaped++] = 'n';
+                       break;
+               case 26:
+                       strncpy(&escaped_str[escaped], ctrlz, ctrlz_size);
+                       escaped += ctrlz_size;
+                       break;
+               case 25:
+                       strncpy(&escaped_str[escaped], esc, esc_size);
+                       escaped += esc_size;
+                       break;
+               default:
+                       if (g_ascii_isprint(c))
+                               escaped_str[escaped++] = c;
+                       else {
+                               escaped_str[escaped++] = '\\';
+                               escaped_str[escaped++] = '0' + ((c >> 6) & 07);
+                               escaped_str[escaped++] = '0' + ((c >> 3) & 07);
+                               escaped_str[escaped++] = '0' + (c & 07);
+                       }
+               }
+       }
+
+       debugf(escaped_str, user_data);
+       g_free(escaped_str);
+}
+
+void g_at_util_debug_dump(gboolean in, const unsigned char *buf, gsize len,
+                               GAtDebugFunc debugf, gpointer user_data)
+{
+       char type = in ? '<' : '>';
+       GString *str;
+       gsize i;
+
+       if (debugf == NULL || !len)
+               return;
+
+       str = g_string_sized_new(1 + (len * 2));
+       if (str == NULL)
+               return;
+
+       g_string_append_c(str, type);
+
+       for (i = 0; i < len; i++)
+               g_string_append_printf(str, " %02x", buf[i]);
+
+       debugf(str->str, user_data);
+       g_string_free(str, TRUE);
+}
+
+void g_at_util_debug_hexdump(gboolean in, const unsigned char *buf, gsize len,
+                               GAtDebugFunc debugf, gpointer user_data)
+{
+       static const char hexdigits[] = "0123456789abcdef";
+       char str[68];
+       gsize i;
+
+       if (debugf == NULL || !len)
+               return;
+
+       str[0] = in ? '<' : '>';
+
+       for (i = 0; i < len; i++) {
+               str[((i % 16) * 3) + 1] = ' ';
+               str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
+               str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
+               str[(i % 16) + 51] = g_ascii_isprint(buf[i]) ? buf[i] : '.';
+
+               if ((i + 1) % 16 == 0) {
+                       str[49] = ' ';
+                       str[50] = ' ';
+                       str[67] = '\0';
+                       debugf(str, user_data);
+                       str[0] = ' ';
+               }
+       }
+
+       if (i % 16 > 0) {
+               gsize j;
+               for (j = (i % 16); j < 16; j++) {
+                       str[(j * 3) + 1] = ' ';
+                       str[(j * 3) + 2] = ' ';
+                       str[(j * 3) + 3] = ' ';
+                       str[j + 51] = ' ';
+               }
+               str[49] = ' ';
+               str[50] = ' ';
+               str[67] = '\0';
+               debugf(str, user_data);
+       }
+}
+
+gboolean g_at_util_setup_io(GIOChannel *io, GIOFlags flags)
+{
+       GIOFlags io_flags;
+
+       if (g_io_channel_set_encoding(io, NULL, NULL) != G_IO_STATUS_NORMAL)
+               return FALSE;
+
+       g_io_channel_set_buffered(io, FALSE);
+
+       if (flags & G_IO_FLAG_SET_MASK) {
+               io_flags = g_io_channel_get_flags(io);
+
+               io_flags |= (flags & G_IO_FLAG_SET_MASK);
+
+               if (g_io_channel_set_flags(io, io_flags, NULL) !=
+                                                       G_IO_STATUS_NORMAL)
+                       return FALSE;
+       }
+
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       return TRUE;
+}
diff --git a/gatchat/gatutil.h b/gatchat/gatutil.h
new file mode 100644 (file)
index 0000000..d4f803a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GATUTIL_H
+#define __GATUTIL_H
+
+#include "gat.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void g_at_util_debug_chat(gboolean in, const char *str, gsize len,
+                               GAtDebugFunc debugf, gpointer user_data);
+
+void g_at_util_debug_dump(gboolean in, const unsigned char *buf, gsize len,
+                               GAtDebugFunc debugf, gpointer user_data);
+
+void g_at_util_debug_hexdump(gboolean in, const unsigned char *buf, gsize len,
+                               GAtDebugFunc debugf, gpointer user_data);
+
+gboolean g_at_util_setup_io(GIOChannel *io, GIOFlags flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATUTIL_H */
diff --git a/gatchat/gsm0710.c b/gatchat/gsm0710.c
new file mode 100644 (file)
index 0000000..2c257d7
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009  Trolltech ASA.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "gsm0710.h"
+
+static const unsigned char crc_table[256] = {
+       0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
+       0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
+       0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69,
+       0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
+       0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D,
+       0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
+       0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51,
+       0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
+       0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05,
+       0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
+       0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19,
+       0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
+       0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D,
+       0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
+       0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21,
+       0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
+       0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95,
+       0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
+       0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89,
+       0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
+       0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD,
+       0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
+       0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1,
+       0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
+       0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5,
+       0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
+       0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9,
+       0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
+       0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD,
+       0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
+       0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1,
+       0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
+};
+
+static inline guint8 gsm0710_crc(const guint8 *data, int len)
+{
+       guint8 crc = 0xFF;
+       int i;
+
+       for (i = 0; i < len; i++)
+               crc = crc_table[crc ^ data[i]];
+
+       return crc;
+}
+
+static inline guint8 gsm0710_fcs(const guint8 *data, int len)
+{
+       return 0xff - gsm0710_crc(data, len);
+}
+
+static inline gboolean gsm0710_check_fcs(const guint8 *data, int len,
+                                               guint8 cfcs)
+{
+       guint8 fcs = gsm0710_crc(data, len);
+
+       fcs = crc_table[fcs ^ cfcs];
+
+       if (fcs == 0xcf)
+               return TRUE;
+
+       return FALSE;
+}
+
+int gsm0710_advanced_extract_frame(guint8 *buf, int len,
+                                       guint8 *out_dlc, guint8 *out_control,
+                                       guint8 **out_frame, int *out_len)
+{
+       int posn = 0;
+       int posn2;
+       int framelen;
+       guint8 dlc;
+       guint8 control;
+
+       while (posn < len) {
+               if (buf[posn] != 0x7E) {
+                       posn += 1;
+                       continue;
+               }
+
+               /* Skip additional 0x7E bytes between frames */
+               while ((posn + 1) < len && buf[posn + 1] == 0x7E)
+                       posn += 1;
+
+               /* Search for the end of the packet (the next 0x7E byte) */
+               framelen = posn + 1;
+               while (framelen < len && buf[framelen] != 0x7E)
+                       framelen += 1;
+
+               if (framelen >= len)
+                       break;
+
+               if (framelen < 4) {
+                       posn = framelen;
+                       continue;
+               }
+
+               /* Undo control byte quoting in the packet */
+               posn2 = 0;
+               ++posn;
+               while (posn < framelen) {
+                       if (buf[posn] == 0x7D) {
+                               ++posn;
+
+                               if (posn >= framelen)
+                                       break;
+
+                               buf[posn2++] = buf[posn++] ^ 0x20;
+                       } else {
+                               buf[posn2++] = buf[posn++];
+                       }
+               }
+
+               /* Validate the checksum on the packet header */
+               if (!gsm0710_check_fcs(buf, 2, buf[posn2 - 1]))
+                       continue;
+
+               /* Decode and dispatch the packet */
+               dlc = (buf[0] >> 2) & 0x3F;
+               control = buf[1] & 0xEF; /* Strip "PF" bit */
+
+               if (out_frame)
+                       *out_frame = buf + 2;
+
+               if (out_len)
+                       *out_len = posn2 - 3;
+
+               if (out_dlc)
+                       *out_dlc = dlc;
+
+               if (out_control)
+                       *out_control = control;
+
+               break;
+       }
+
+       return posn;
+}
+
+int gsm0710_advanced_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
+                                       const guint8 *data, int len)
+{
+       int temp, crc;
+       int size;
+
+       frame[0] = 0x7E;
+       frame[1] = ((dlc << 2) | 0x03);
+       frame[2] = type;
+
+       crc = gsm0710_fcs(frame + 1, 2);
+
+       /* The Address field might need to be escaped if this is a response
+        * frame
+        */
+
+       /* Need to quote the type field now that crc has been computed */
+       if (type == 0x7E || type == 0x7D) {
+               frame[2] = 0x7D;
+               frame[3] = (type ^ 0x20);
+               size = 4;
+       } else {
+               size = 3;
+       }
+
+       while (len > 0) {
+               temp = *data++ & 0xFF;
+               --len;
+
+               if (temp != 0x7E && temp != 0x7D) {
+                       frame[size++] = temp;
+               } else {
+                       frame[size++] = 0x7D;
+                       frame[size++] = (temp ^ 0x20);
+               }
+       }
+
+       if (crc != 0x7E && crc != 0x7D) {
+               frame[size++] = crc;
+       } else {
+               frame[size++] = 0x7D;
+               frame[size++] = (crc ^ 0x20);
+       }
+
+       frame[size++] = 0x7E;
+
+       return size;
+}
+
+int gsm0710_basic_extract_frame(guint8 *buf, int len,
+                                       guint8 *out_dlc, guint8 *out_control,
+                                       guint8 **out_frame, int *out_len)
+{
+       int posn = 0;
+       int framelen;
+       int header_size;
+       guint8 fcs;
+       guint8 dlc;
+       guint8 type;
+
+       while (posn < len) {
+               if (buf[posn] != 0xF9) {
+                       posn += 1;
+                       continue;
+               }
+
+               /* Skip additional 0xF9 bytes between frames */
+               while ((posn + 1) < len && buf[posn + 1] == 0xF9)
+                       posn += 1;
+
+               /* We need at least 4 bytes for the flag + header */
+               if ((posn + 4) > len)
+                       break;
+
+               /* The low bit of the second byte should be 1,
+                  which indicates a short channel number.  According to
+                  27.010 Section 5.2.3, if this is not true, then
+                  the frame is invalid and should be discarded
+               */
+               if ((buf[posn + 1] & 0x01) == 0) {
+                       ++posn;
+                       continue;
+               }
+
+               /* Get the packet length and validate it */
+               framelen = buf[posn + 3] >> 1;
+
+               if ((buf[posn + 3] & 0x01) != 0) {
+                       /* Single-byte length indication */
+                       header_size = 3;
+               } else {
+                       /* Double-byte length indication */
+                       if ((posn + 5) > len)
+                               break;
+
+                       framelen |= buf[posn + 4] << 7;
+                       header_size = 4;
+               }
+
+               /* Total size of the packet is the flag + 3 or 4 byte header
+                * Address Control Length followed by Information and FCS.
+                * However, we must check the presence of the end flag
+                * according to 27.010 Section 5.2.3
+                */
+               if ((posn + header_size + 3 + framelen) > len)
+                       break;
+
+               fcs = buf[posn + 1 + header_size + framelen];
+
+               /*
+                * The end flag is not guaranteed to be only ours
+                * according to 27.010 Section 5.2.6.1:
+                * "The closing flag may also be the opening flag of the
+                * following frame", thus we do not consume it in the following
+                * stages
+                */
+
+               /*
+                * If FCS is invalid, discard the packet in accordance to
+                * Section 5.2.3 of 27.010
+                */
+               if (!gsm0710_check_fcs(buf + posn + 1, header_size, fcs)) {
+                       posn += header_size + framelen + 2;
+                       continue;
+               }
+
+               if (buf[posn + header_size + framelen + 2] != 0xF9) {
+                       posn += header_size + framelen + 2;
+                       continue;
+               }
+
+               /* Get the channel number and packet type from the header */
+               dlc = buf[posn + 1] >> 2;
+               type = buf[posn + 2] & 0xEF;    /* Strip "PF" bit */
+
+               if (out_frame)
+                       *out_frame = buf + posn + 1 + header_size;
+
+               if (out_len)
+                       *out_len = framelen;
+
+               if (out_dlc)
+                       *out_dlc = dlc;
+
+               if (out_control)
+                       *out_control = type;
+
+               posn += header_size + framelen + 2;
+
+               break;
+       }
+
+       return posn;
+}
+
+int gsm0710_basic_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
+                               const guint8 *data, int len)
+{
+       int size;
+       int header_size;
+
+       frame[0] = 0xF9;
+       frame[1] = ((dlc << 2) | 0x03);
+       frame[2] = type;
+
+       if (len <= 127) {
+               frame[3] = ((len << 1) | 0x01);
+               header_size = 4;
+       } else {
+               frame[3] = (len << 1);
+               frame[4] = (len >> 7);
+               header_size = 5;
+       }
+
+       size = header_size;
+
+       if (len > 0) {
+               memcpy(frame + header_size, data, len);
+               size += len;
+       }
+
+       /* Note: GSM 07.10 says that the CRC is only computed over the header */
+       frame[size++] = gsm0710_fcs(frame + 1, header_size - 1);
+       frame[size++] = 0xF9;
+
+       return size;
+}
diff --git a/gatchat/gsm0710.h b/gatchat/gsm0710.h
new file mode 100644 (file)
index 0000000..a5402e0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009  Trolltech ASA.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GSM0710_H
+#define __GSM0710_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Frame types and subtypes */
+#define GSM0710_OPEN_CHANNEL           0x3F
+#define GSM0710_CLOSE_CHANNEL          0x53
+#define GSM0710_DATA                   0xEF
+#define GSM0710_DATA_ALT               0x03
+#define GSM0710_STATUS_SET             0xE3
+#define GSM0710_STATUS_ACK             0xE1
+
+int gsm0710_basic_extract_frame(guint8 *data, int len,
+                                       guint8 *out_dlc, guint8 *out_type,
+                                       guint8 **frame, int *out_len);
+
+int gsm0710_basic_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
+                               const guint8 *data, int len);
+
+int gsm0710_advanced_extract_frame(guint8 *data, int len,
+                                       guint8 *out_dlc, guint8 *out_type,
+                                       guint8 **frame, int *out_len);
+
+int gsm0710_advanced_fill_frame(guint8 *frame, guint8 dlc, guint8 type,
+                                       const guint8 *data, int len);
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/gatchat/gsmdial.c b/gatchat/gsmdial.c
new file mode 100644 (file)
index 0000000..60e4f24
--- /dev/null
@@ -0,0 +1,796 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+#include <gatppp.h>
+
+#define IFCONFIG_PATH "/sbin/ifconfig"
+
+static const char *none_prefix[] = { NULL };
+static const char *cfun_prefix[] = { "+CFUN:", NULL };
+static const char *creg_prefix[] = { "+CREG:", NULL };
+static const char *cgreg_prefix[] = { "+CGREG:", NULL };
+
+static gchar *option_ip = NULL;
+static gint option_port = 0;
+static gchar *option_modem = NULL;
+static gchar *option_control = NULL;
+static gint option_cid = 0;
+static gchar *option_apn = NULL;
+static gint option_offmode = 0;
+static gboolean option_legacy = FALSE;
+static gchar *option_username = NULL;
+static gchar *option_password = NULL;
+static gchar *option_pppdump = NULL;
+static gboolean option_bluetooth = FALSE;
+static gboolean option_acfc = FALSE;
+static gboolean option_pfc = FALSE;
+
+static GAtPPP *ppp;
+static GAtChat *control;
+static GAtChat *modem;
+static GMainLoop *event_loop;
+
+enum state {
+       STATE_NONE = 0,
+       STATE_REGISTERING,
+       STATE_ATTACHING,
+       STATE_ACTIVATING
+};
+
+static int state = 0;
+static int oldmode = 0;
+
+static void gsmdial_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (const char *) data, str);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+       g_main_loop_quit(event_loop);
+       return FALSE;
+}
+
+static void power_down(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       g_main_loop_quit(event_loop);
+}
+
+static void kill_ppp(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       g_print("kill_ppp: %d\n", ok);
+
+       if (ok == FALSE)
+               return;
+
+       g_at_ppp_unref(ppp);
+       ppp = NULL;
+}
+
+static void ppp_suspend_ath0(gpointer user_data)
+{
+       g_at_chat_resume(modem);
+       g_at_chat_send(modem, "ATH0", none_prefix, kill_ppp, NULL, NULL);
+}
+
+static void resume_ppp(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       g_print("resume_ppp: %d\n", ok);
+
+       if (ok == FALSE)
+               return;
+
+       g_at_chat_suspend(modem);
+       g_at_ppp_resume(ppp);
+}
+
+static void ppp_suspend_ato0(gpointer user_data)
+{
+       g_at_chat_resume(modem);
+       g_at_chat_send(modem, "ATO0", none_prefix, resume_ppp, NULL, NULL);
+}
+
+static gboolean signal_cb(GIOChannel *channel, GIOCondition cond, gpointer data)
+{
+       static int terminated = 0;
+       int signal_fd = GPOINTER_TO_INT(data);
+       struct signalfd_siginfo si;
+       ssize_t res;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR))
+               return FALSE;
+
+       res = read(signal_fd, &si, sizeof(si));
+       if (res != sizeof(si))
+               return FALSE;
+
+       switch (si.ssi_signo) {
+       case SIGINT:
+       case SIGTERM:
+               if (terminated == 0) {
+                       g_timeout_add_seconds(10, quit_eventloop, NULL);
+
+                       if (ppp == NULL) {
+                               char buf[64];
+                               sprintf(buf, "AT+CFUN=%u", option_offmode);
+                               g_at_chat_send(control, buf, none_prefix,
+                                               power_down, NULL, NULL);
+                       } else
+                               g_at_ppp_shutdown(ppp);
+               }
+
+               terminated++;
+               break;
+       case SIGUSR1:
+               if (ppp == NULL)
+                       break;
+
+               g_at_ppp_set_suspend_function(ppp, ppp_suspend_ato0, NULL);
+               g_at_ppp_suspend(ppp);
+               break;
+       case SIGUSR2:
+               if (ppp == NULL)
+                       break;
+
+               g_at_ppp_set_suspend_function(ppp, ppp_suspend_ath0, NULL);
+               g_at_ppp_suspend(ppp);
+               break;
+       default:
+               break;
+       }
+
+       return TRUE;
+}
+
+static gboolean at_util_parse_reg_unsolicited(GAtResult *result,
+                                               const char *prefix, int *status,
+                                               int *lac, int *ci, int *tech)
+{
+       GAtResultIter iter;
+       int s;
+       int l = -1, c = -1, t = -1;
+       const char *str;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, prefix) == FALSE)
+               return FALSE;
+
+       if (g_at_result_iter_next_number(&iter, &s) == FALSE)
+               return FALSE;
+
+       if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+               l = strtol(str, NULL, 16);
+       else
+               goto out;
+
+       if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+               c = strtol(str, NULL, 16);
+       else
+               goto out;
+
+       g_at_result_iter_next_number(&iter, &t);
+
+out:
+       if (status)
+               *status = s;
+
+       if (lac)
+               *lac = l;
+
+       if (ci)
+               *ci = c;
+
+       if (tech)
+               *tech = t;
+
+       return TRUE;
+}
+
+static gboolean at_util_parse_reg(GAtResult *result, const char *prefix,
+                                       int *mode, int *status,
+                                       int *lac, int *ci, int *tech)
+{
+       GAtResultIter iter;
+       int m, s;
+       int l = -1, c = -1, t = -1;
+       const char *str;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, prefix)) {
+               g_at_result_iter_next_number(&iter, &m);
+
+               /* Sometimes we get an unsolicited CREG/CGREG here, skip it */
+               if (g_at_result_iter_next_number(&iter, &s) == FALSE)
+                       continue;
+
+               if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+                       l = strtol(str, NULL, 16);
+               else
+                       goto out;
+
+               if (g_at_result_iter_next_string(&iter, &str) == TRUE)
+                       c = strtol(str, NULL, 16);
+               else
+                       goto out;
+
+               g_at_result_iter_next_number(&iter, &t);
+
+out:
+               if (mode)
+                       *mode = m;
+
+               if (status)
+                       *status = s;
+
+               if (lac)
+                       *lac = l;
+
+               if (ci)
+                       *ci = c;
+
+               if (tech)
+                       *tech = t;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean execute(const char *cmd)
+{
+       int status;
+
+       status = system(cmd);
+       if (status < 0) {
+               g_print("Failed to execute command: %s\n", strerror(errno));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void ppp_connect(const char *iface, const char *local, const char *peer,
+                       const char *dns1, const char *dns2,
+                       gpointer user_data)
+{
+       char buf[512];
+
+       /* print out the negotiated address and dns server */
+       g_print("Network Device: %s\n", iface);
+       g_print("IP Address: %s\n", local);
+       g_print("Peer IP Address: %s\n", peer);
+       g_print("Primary DNS Server: %s\n", dns1);
+       g_print("Secondary DNS Server: %s\n", dns2);
+
+       if (getuid() != 0) {
+               g_print("Need root privilege to config PPP interface\n");
+               return;
+       }
+
+       snprintf(buf, sizeof(buf), "%s %s up", IFCONFIG_PATH, iface);
+       execute(buf);
+
+       snprintf(buf, sizeof(buf), "%s %s %s pointopoint %s", IFCONFIG_PATH,
+                               iface, local, peer);
+       execute(buf);
+}
+
+static void no_carrier_notify(GAtResult *result, gpointer user_data)
+{
+       char buf[64];
+
+       if (option_bluetooth) {
+               g_main_loop_quit(event_loop);
+               return;
+       }
+
+       sprintf(buf, "AT+CFUN=%u", option_offmode);
+       g_at_chat_send(control, buf, none_prefix, power_down, NULL, NULL);
+}
+
+static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data)
+{
+       g_print("PPP Link down: %d\n", reason);
+
+       g_at_ppp_unref(ppp);
+       ppp = NULL;
+
+       if (option_modem == NULL)
+               g_at_chat_set_debug(modem, gsmdial_debug, "");
+       else
+               g_at_chat_set_debug(modem, gsmdial_debug, "Modem");
+
+       g_at_chat_register(modem, "NO CARRIER", no_carrier_notify,
+                                       FALSE, NULL, NULL);
+       g_at_chat_resume(modem);
+}
+
+static void connect_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       GAtIO *io;
+
+       if (!ok) {
+               g_print("Unable to define context\n");
+               exit(1);
+       }
+
+       /* get the data IO channel */
+       io = g_at_chat_get_io(modem);
+
+       /*
+        * shutdown gatchat or else it tries to take all the input
+        * from the modem and does not let PPP get it.
+        */
+       g_at_chat_suspend(modem);
+
+       /* open ppp */
+       ppp = g_at_ppp_new();
+       if (ppp == NULL) {
+               g_print("Unable to create PPP object\n");
+               exit(1);
+       }
+       g_at_ppp_set_debug(ppp, gsmdial_debug, "PPP");
+
+       g_at_ppp_set_credentials(ppp, option_username, option_password);
+
+       g_at_ppp_set_acfc_enabled(ppp, option_acfc);
+       g_at_ppp_set_pfc_enabled(ppp, option_pfc);
+
+       /* set connect and disconnect callbacks */
+       g_at_ppp_set_connect_function(ppp, ppp_connect, NULL);
+       g_at_ppp_set_disconnect_function(ppp, ppp_disconnect, NULL);
+
+       /* open the ppp connection */
+       g_at_ppp_open(ppp, io);
+
+       if (option_pppdump)
+               g_at_ppp_set_recording(ppp, option_pppdump);
+}
+
+static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       char buf[64];
+
+       if (!ok) {
+               g_print("Unable to define context\n");
+               exit(1);
+       }
+
+       if (option_legacy == TRUE)
+               sprintf(buf, "ATD*99***%u#", option_cid);
+       else
+               sprintf(buf, "AT+CGDATA=\"PPP\",%u", option_cid);
+
+       g_at_chat_send(modem, buf, none_prefix, connect_cb, NULL, NULL);
+}
+
+static void setup_context(int status)
+{
+       char buf[1024];
+       int len;
+
+       state = STATE_ACTIVATING;
+
+       g_print("Registered to GPRS network, roaming=%s\n",
+                                       status == 5 ? "true" : "false");
+
+       len = sprintf(buf, "AT+CGDCONT=%u,\"IP\"", option_cid);
+       snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", option_apn);
+       g_at_chat_send(control, buf, none_prefix, at_cgdcont_cb, NULL, NULL);
+}
+
+static void cgreg_notify(GAtResult *result, gpointer user_data)
+{
+       int status, lac, ci, tech;
+
+       if (state != STATE_ATTACHING)
+               return;
+
+       if (at_util_parse_reg_unsolicited(result, "+CGREG:", &status,
+                                               &lac, &ci, &tech) == FALSE)
+               return;
+
+       if (status != 1 && status != 5)
+               return;
+
+       setup_context(status);
+}
+
+static void cgreg_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       int status, lac, ci, tech;
+
+       if (!ok)
+               return;
+
+       if (at_util_parse_reg(result, "+CGREG:", NULL, &status,
+                                               &lac, &ci, &tech) == FALSE)
+               return;
+
+       if (status != 1 && status != 5) {
+               g_at_chat_register(control, "+CGREG:",
+                                       cgreg_notify, FALSE, NULL, NULL);
+               return;
+       }
+
+       setup_context(status);
+}
+
+static void attached_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       if (!ok)
+               return;
+
+       g_at_chat_send(control, "AT+CGREG?", cgreg_prefix,
+                                               cgreg_cb, NULL, NULL);
+}
+
+static void activate_gprs(int status)
+{
+       state = STATE_ATTACHING;
+       g_print("Registered to network, roaming=%s\n",
+                                       status == 5 ? "true" : "false");
+
+       g_print("Activating GPRS network...\n");
+       g_at_chat_send(control, "AT+CGATT=1", none_prefix,
+                                               attached_cb, NULL, NULL);
+}
+
+static void creg_notify(GAtResult *result, gpointer user_data)
+{
+       int status, lac, ci, tech;
+
+       if (state != STATE_REGISTERING)
+               return;
+
+       if (at_util_parse_reg_unsolicited(result, "+CREG:", &status,
+                                               &lac, &ci, &tech) == FALSE)
+               return;
+
+       if (status != 1 && status != 5)
+               return;
+
+       activate_gprs(status);
+}
+
+static void creg_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       int status, lac, ci, tech;
+
+       if (!ok)
+               return;
+
+       if (at_util_parse_reg(result, "+CREG:", NULL, &status,
+                                               &lac, &ci, &tech) == FALSE)
+               return;
+
+       if (status != 1 && status != 5) {
+               g_at_chat_register(control, "+CREG:",
+                                               creg_notify, FALSE, NULL, NULL);
+               return;
+       }
+
+       activate_gprs(status);
+}
+
+static void register_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       if (!ok) {
+               g_print("Couldn't register to network, exiting...\n");
+               exit(1);
+       }
+
+       state = STATE_REGISTERING;
+       g_print("Waiting for network registration...\n");
+
+       g_at_chat_send(control, "AT+CREG?", creg_prefix,
+                                               creg_cb, NULL, NULL);
+}
+
+static void start_dial(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       if (!ok) {
+               g_print("Checking PIN status failed\n");
+               exit(1);
+       }
+
+       g_at_chat_send(control, "AT+CREG=2", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(control, "AT+CGREG=2", none_prefix, NULL, NULL, NULL);
+
+       g_at_chat_send(control, "AT+COPS=0", none_prefix,
+                                               register_cb, NULL, NULL);
+}
+
+static void check_pin(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       if (!ok) {
+               g_print("Turning on the modem failed\n");
+               exit(1);
+       }
+
+       g_at_chat_send(control, "AT+CPIN?", NULL, start_dial, NULL, NULL);
+}
+
+static void check_mode(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       GAtResultIter iter;
+
+       if (!ok) {
+               g_print("Checking modem mode failed\n");
+               exit(1);
+       }
+
+       g_at_result_iter_init(&iter, result);
+       g_at_result_iter_next(&iter, "+CFUN:");
+       g_at_result_iter_next_number(&iter, &oldmode);
+
+       g_print("Current modem mode is %d\n", oldmode);
+
+       if (oldmode == 1) {
+               check_pin(ok, result, user_data);
+               return;
+       }
+
+       g_at_chat_send(control, "AT+CFUN=1", NULL, check_pin, NULL, NULL);
+}
+
+static int open_serial(void)
+{
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+
+       channel = g_at_tty_open(option_control, NULL);
+       if (channel == NULL)
+               return -EIO;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       control = g_at_chat_new(channel, syntax);
+       g_io_channel_unref(channel);
+       g_at_syntax_unref(syntax);
+
+       if (control == NULL)
+               return -EIO;
+
+       if (option_modem == NULL) {
+               g_at_chat_ref(control);
+               modem = control;
+               g_at_chat_set_debug(control, gsmdial_debug, "");
+       } else {
+               g_at_chat_set_debug(control, gsmdial_debug, "Control");
+
+               channel = g_at_tty_open(option_modem, NULL);
+               if (channel == NULL)
+                       return -EIO;
+
+               syntax = g_at_syntax_new_gsm_permissive();
+               modem = g_at_chat_new(channel, syntax);
+               g_io_channel_unref(channel);
+               g_at_syntax_unref(syntax);
+
+               if (modem == NULL)
+                       return -EIO;
+
+               g_at_chat_set_debug(modem, gsmdial_debug, "Modem");
+       }
+
+       return 0;
+}
+
+static int open_ip(void)
+{
+       int sk, err;
+       struct sockaddr_in addr;
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0)
+               return -EINVAL;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(option_ip);
+       addr.sin_port = htons(option_port);
+
+       err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0) {
+               close(sk);
+               return err;
+       }
+
+       channel = g_io_channel_unix_new(sk);
+       if (channel == NULL) {
+               close(sk);
+               return -ENOMEM;
+       }
+
+       syntax = g_at_syntax_new_gsmv1();
+       control = g_at_chat_new(channel, syntax);
+       g_io_channel_unref(channel);
+       g_at_syntax_unref(syntax);
+
+       if (control == NULL)
+               return -ENOMEM;
+
+       g_at_chat_ref(control);
+       modem = control;
+       g_at_chat_set_debug(control, gsmdial_debug, "");
+
+       return 0;
+}
+
+static GOptionEntry options[] = {
+       { "ip", 'i', 0, G_OPTION_ARG_STRING, &option_ip,
+                               "Specify IP" },
+       { "port", 'p', 0, G_OPTION_ARG_INT, &option_port,
+                               "Specify IP Port" },
+       { "control", 'n', 0, G_OPTION_ARG_FILENAME, &option_control,
+                               "Specify Modem Control port" },
+       { "modem", 'm', 0, G_OPTION_ARG_FILENAME, &option_modem,
+                               "Specify Modem port (ppp), if not provided"
+                               " the control port will be used" },
+       { "cid", 'c', 0, G_OPTION_ARG_INT, &option_cid,
+                               "Specify CID to use" },
+       { "apn", 'a', 0, G_OPTION_ARG_STRING, &option_apn,
+                               "Specify APN" },
+       { "offmode", 'o', 0, G_OPTION_ARG_INT, &option_offmode,
+                               "Specify CFUN offmode" },
+       { "legacy", 'l', 0, G_OPTION_ARG_NONE, &option_legacy,
+                               "Use ATD*99***<cid>#" },
+       { "bluetooth", 'b', 0, G_OPTION_ARG_NONE, &option_bluetooth,
+                               "Use only ATD*99" },
+       { "username", 'u', 0, G_OPTION_ARG_STRING, &option_username,
+                               "Specify PPP username" },
+       { "password", 'w', 0, G_OPTION_ARG_STRING, &option_password,
+                               "Specify PPP password" },
+       { "pppdump", 'D', 0, G_OPTION_ARG_STRING, &option_pppdump,
+                               "Specify pppdump filename" },
+       { "pfc", 0, 0, G_OPTION_ARG_NONE, &option_pfc,
+                               "Use Protocol Field Compression" },
+       { "acfc", 0, 0, G_OPTION_ARG_NONE, &option_acfc,
+                               "Use Address & Control Field Compression" },
+       { NULL },
+};
+
+int main(int argc, char **argv)
+{
+       GOptionContext *context;
+       GError *err = NULL;
+       sigset_t mask;
+       int signal_fd;
+       GIOChannel *signal_io;
+       int signal_source;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+               if (err != NULL) {
+                       g_printerr("%s\n", err->message);
+                       g_error_free(err);
+                       return 1;
+               }
+
+               g_printerr("An unknown error occurred\n");
+               return 1;
+       }
+
+       g_option_context_free(context);
+
+       if (option_control) {
+               int ret;
+
+               g_print("Control: %s\n", option_control);
+               if (option_modem)
+                       g_print("Modem: %s\n", option_modem);
+
+               ret = open_serial();
+               g_free(option_control);
+               g_free(option_modem);
+
+               if (ret < 0)
+                       goto out;
+       } else {
+               int ret;
+
+               g_print("IP: %s\n", option_ip);
+               g_print("Port: %d\n", option_port);
+               ret = open_ip();
+               g_free(option_ip);
+
+               if (ret < 0)
+                       goto out;
+       }
+
+       g_print("APN: %s\n", option_apn);
+       g_print("CID: %d\n", option_cid);
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGTERM);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGUSR1);
+       sigaddset(&mask, SIGUSR2);
+       sigaddset(&mask, SIGPIPE);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               perror("Can't set signal mask");
+               return 1;
+       }
+
+       signal_fd = signalfd(-1, &mask, 0);
+       if (signal_fd < 0) {
+               perror("Can't create signal filedescriptor");
+               return 1;
+       }
+
+       signal_io = g_io_channel_unix_new(signal_fd);
+       g_io_channel_set_close_on_unref(signal_io, TRUE);
+       signal_source = g_io_add_watch(signal_io,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       signal_cb, GINT_TO_POINTER(signal_fd));
+       g_io_channel_unref(signal_io);
+
+       event_loop = g_main_loop_new(NULL, FALSE);
+
+       if (option_bluetooth) {
+               g_at_chat_send(control, "ATD*99#", none_prefix, connect_cb,
+                               NULL, NULL);
+       } else {
+               g_at_chat_send(control, "ATE0Q0V1", NULL, NULL, NULL, NULL);
+               g_at_chat_send(control, "AT+CFUN?", cfun_prefix,
+                                                       check_mode, NULL, NULL);
+       }
+
+       g_main_loop_run(event_loop);
+       g_source_remove(signal_source);
+       g_main_loop_unref(event_loop);
+
+out:
+       if (ppp == NULL) {
+               g_at_chat_unref(control);
+               g_at_chat_unref(modem);
+       } else
+               g_at_ppp_unref(ppp);
+
+       g_free(option_apn);
+
+       return 0;
+}
diff --git a/gatchat/ppp.h b/gatchat/ppp.h
new file mode 100644 (file)
index 0000000..718575b
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "ppp_cp.h"
+
+#define LCP_PROTOCOL   0xc021
+#define CHAP_PROTOCOL  0xc223
+#define IPCP_PROTO     0x8021
+#define IPV6CP_PROTO   0x8057
+#define PPP_IP_PROTO   0x0021
+#define PPP_IPV6_PROTO 0x0057
+#define MD5            5
+
+#define DBG(p, fmt, arg...) do {                               \
+       char *str = g_strdup_printf("%s:%s() " fmt, __FILE__,   \
+                                       __FUNCTION__ , ## arg); \
+       ppp_debug(p, str);                                      \
+       g_free(str);                                            \
+} while (0)
+
+struct ppp_chap;
+struct ppp_net;
+
+struct ppp_header {
+       guint8 address;
+       guint8 control;
+       guint16 proto;
+       guint8 info[0];
+} __attribute__((packed));
+
+struct packed_short {
+       guint16 s;
+} __attribute__((packed));
+
+struct packed_long {
+       guint32 l;
+} __attribute__((packed));
+
+static inline guint32 __get_unaligned_long(const void *p)
+{
+       const struct packed_long *ptr = p;
+       return ptr->l;
+}
+
+static inline guint16 __get_unaligned_short(const void *p)
+{
+       const struct packed_short *ptr = p;
+       return ptr->s;
+}
+
+static inline void __put_unaligned_short(void *p, guint16 val)
+{
+       struct packed_short *ptr = p;
+       ptr->s = val;
+}
+
+#define get_host_long(p) \
+       (ntohl(__get_unaligned_long(p)))
+
+#define get_host_short(p) \
+       (ntohs(__get_unaligned_short(p)))
+
+#define put_network_short(p, val) \
+       (__put_unaligned_short(p, htons(val)))
+
+#define ppp_proto(packet) \
+       (get_host_short(packet + 2))
+
+/* LCP related functions */
+struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean dormant);
+void lcp_free(struct pppcp_data *lcp);
+void lcp_protocol_reject(struct pppcp_data *lcp, guint8 *packet, gsize len);
+void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled);
+void lcp_set_pfc_enabled(struct pppcp_data *pppcp, gboolean enabled);
+
+/* IPCP related functions */
+struct pppcp_data *ipcp_new(GAtPPP *ppp, gboolean is_server, guint32 ip);
+void ipcp_free(struct pppcp_data *data);
+void ipcp_set_server_info(struct pppcp_data *ipcp, guint32 peer_addr,
+                               guint32 dns1, guint32 dns2);
+
+/* IPv6 CP related functions */
+struct pppcp_data *ipv6cp_new(GAtPPP *ppp, gboolean is_server,
+                                       const char *local, const char *peer,
+                                       GError **error);
+void ipv6cp_free(struct pppcp_data *data);
+
+/* CHAP related functions */
+struct ppp_chap *ppp_chap_new(GAtPPP *ppp, guint8 method);
+void ppp_chap_free(struct ppp_chap *chap);
+void ppp_chap_process_packet(struct ppp_chap *chap, const guint8 *new_packet,
+                               gsize len);
+
+/* TUN / Network related functions */
+struct ppp_net *ppp_net_new(GAtPPP *ppp, int fd);
+const char *ppp_net_get_interface(struct ppp_net *net);
+void ppp_net_process_packet(struct ppp_net *net, const guint8 *packet,
+                               gsize len);
+void ppp_net_free(struct ppp_net *net);
+gboolean ppp_net_set_mtu(struct ppp_net *net, guint16 mtu);
+void ppp_net_suspend_interface(struct ppp_net *net);
+void ppp_net_resume_interface(struct ppp_net *net);
+
+/* PPP functions related to main GAtPPP object */
+void ppp_debug(GAtPPP *ppp, const char *str);
+void ppp_transmit(GAtPPP *ppp, guint8 *packet, guint infolen);
+void ppp_set_auth(GAtPPP *ppp, const guint8 *auth_data);
+void ppp_auth_notify(GAtPPP *ppp, gboolean success);
+void ppp_ipcp_up_notify(GAtPPP *ppp, const char *local, const char *peer,
+                                       const char *dns1, const char *dns2);
+void ppp_ipcp_down_notify(GAtPPP *ppp);
+void ppp_ipcp_finished_notify(GAtPPP *ppp);
+void ppp_lcp_up_notify(GAtPPP *ppp);
+void ppp_lcp_down_notify(GAtPPP *ppp);
+void ppp_lcp_finished_notify(GAtPPP *ppp);
+void ppp_set_recv_accm(GAtPPP *ppp, guint32 accm);
+void ppp_set_xmit_accm(GAtPPP *ppp, guint32 accm);
+void ppp_set_mtu(GAtPPP *ppp, const guint8 *data);
+void ppp_set_xmit_acfc(GAtPPP *ppp, gboolean acfc);
+void ppp_set_xmit_pfc(GAtPPP *ppp, gboolean pfc);
+struct ppp_header *ppp_packet_new(gsize infolen, guint16 protocol);
diff --git a/gatchat/ppp_auth.c b/gatchat/ppp_auth.c
new file mode 100644 (file)
index 0000000..1ddf762
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include "gatppp.h"
+#include "ppp.h"
+
+struct chap_header {
+       guint8 code;
+       guint8 identifier;
+       guint16 length;
+       guint8 data[0];
+} __attribute__((packed));
+
+struct ppp_chap {
+       guint8 method;
+       GAtPPP *ppp;
+};
+
+enum chap_code {
+       CHALLENGE = 1,
+       RESPONSE,
+       SUCCESS,
+       FAILURE
+};
+
+static void chap_process_challenge(struct ppp_chap *chap, const guint8 *packet)
+{
+       const struct chap_header *header = (const struct chap_header *) packet;
+       struct chap_header *response;
+       GChecksum *checksum;
+       const char *secret = g_at_ppp_get_password(chap->ppp);
+       const char *username = g_at_ppp_get_username(chap->ppp);
+       guint16 response_length;
+       struct ppp_header *ppp_packet;
+       gsize digest_len;
+
+       /* create a checksum over id, secret, and challenge */
+       checksum = g_checksum_new(chap->method);
+       if (checksum == NULL)
+               return;
+
+       g_checksum_update(checksum, &header->identifier, 1);
+
+       if (secret)
+               g_checksum_update(checksum, (guchar *) secret, strlen(secret));
+
+       g_checksum_update(checksum, &header->data[1], header->data[0]);
+
+       /* transmit a response packet */
+       /*
+        * allocate space for the header, the checksum, and the ppp header,
+        * and the value size byte
+        */
+       digest_len = g_checksum_type_get_length(chap->method);
+       response_length = digest_len + sizeof(*header) + 1;
+
+       if (username != NULL)
+               response_length += strlen(username);
+
+       ppp_packet = ppp_packet_new(response_length, CHAP_PROTOCOL);
+       if (ppp_packet == NULL)
+               goto challenge_out;
+
+       response = (struct chap_header *) &ppp_packet->info;
+       if (response) {
+               response->code = RESPONSE;
+               response->identifier = header->identifier;
+               response->length = htons(response_length);
+               g_checksum_get_digest(checksum, response->data + 1,
+                                                       &digest_len);
+               response->data[0] = digest_len;
+               /* leave the name empty? */
+       }
+
+       if (username != NULL)
+               memcpy(response->data + digest_len + 1, username,
+                               strlen(username));
+
+       /* transmit the packet */
+       ppp_transmit(chap->ppp, (guint8 *) ppp_packet, response_length);
+       g_free(ppp_packet);
+
+challenge_out:
+       g_checksum_free(checksum);
+}
+
+/*
+ * parse the packet
+ */
+void ppp_chap_process_packet(struct ppp_chap *chap, const guint8 *new_packet,
+                               gsize len)
+{
+       guint8 code;
+
+       if (len < sizeof(struct chap_header))
+               return;
+
+       code = new_packet[0];
+
+       switch (code) {
+       case CHALLENGE:
+               chap_process_challenge(chap, new_packet);
+               break;
+       case RESPONSE:
+               break;
+       case SUCCESS:
+               ppp_auth_notify(chap->ppp, TRUE);
+               break;
+       case FAILURE:
+               ppp_auth_notify(chap->ppp, FALSE);
+               break;
+       default:
+               break;
+       }
+}
+
+void ppp_chap_free(struct ppp_chap *chap)
+{
+       g_free(chap);
+}
+
+struct ppp_chap *ppp_chap_new(GAtPPP *ppp, guint8 method)
+{
+       struct ppp_chap *chap;
+
+       if (method != MD5)
+               return NULL;
+
+       chap = g_try_new0(struct ppp_chap, 1);
+       if (chap == NULL)
+               return NULL;
+
+       chap->ppp = ppp;
+       chap->method = G_CHECKSUM_MD5;
+
+       return chap;
+}
diff --git a/gatchat/ppp_cp.c b/gatchat/ppp_cp.c
new file mode 100644 (file)
index 0000000..f3f2cc4
--- /dev/null
@@ -0,0 +1,1031 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <glib.h>
+#include <arpa/inet.h>
+
+#include "gatppp.h"
+#include "ppp.h"
+
+static const char *pppcp_state_strings[] = {
+       "INITIAL", "STARTING", "CLOSED", "STOPPED", "CLOSING", "STOPPING",
+       "REQSENT", "ACKRCVD", "ACKSENT", "OPENED"
+};
+
+static const char *pppcp_event_strings[] = {
+       "Up", "Down", "Open", "Close", "TO+", "TO-", "RCR+", "RCR-",
+       "RCA", "RCN", "RTR", "RTA", "RUC", "RXJ+", "RXJ-", "RXR"
+};
+
+#define pppcp_trace(p) do { \
+       char *str = g_strdup_printf("%s: %s: current state %d:%s", \
+                               p->driver->name, __FUNCTION__, \
+                               p->state, pppcp_state_strings[p->state]); \
+       ppp_debug(p->ppp, str); \
+       g_free(str); \
+} while (0);
+
+#define pppcp_trace_event(p, type, actions, state) do { \
+       char *str = g_strdup_printf("event: %d (%s), " \
+                               "action: %x, new_state: %d (%s)", \
+                               type, pppcp_event_strings[type], \
+                               actions, state, pppcp_state_strings[state]); \
+       ppp_debug(p->ppp, str); \
+       g_free(str); \
+} while (0);
+
+#define pppcp_to_ppp_packet(p) \
+       (((guint8 *) p) - sizeof(struct ppp_header))
+
+#define INITIAL_RESTART_TIMEOUT        3       /* restart interval in seconds */
+#define MAX_TERMINATE          2
+#define MAX_CONFIGURE          10
+#define MAX_FAILURE            5
+#define CP_HEADER_SZ           4
+
+enum pppcp_state {
+       INITIAL         = 0,
+       STARTING        = 1,
+       CLOSED          = 2,
+       STOPPED         = 3,
+       CLOSING         = 4,
+       STOPPING        = 5,
+       REQSENT         = 6,
+       ACKRCVD         = 7,
+       ACKSENT         = 8,
+       OPENED          = 9,
+};
+
+enum actions {
+       INV = 0x10,
+       IRC = 0x20,
+       ZRC = 0x40,
+       TLU = 0x100,
+       TLD = 0x200,
+       TLS = 0x400,
+       TLF = 0x800,
+       SCR = 0x1000,
+       SCA = 0x2000,
+       SCN = 0x4000,
+       STR = 0x8000,
+       STA = 0x10000,
+       SCJ = 0x20000,
+       SER = 0x40000,
+};
+
+/*
+ * Transition table straight from RFC 1661 Section 4.1
+ * Y coordinate is the events, while X coordinate is the state
+ *
+ * Magic of bitwise operations allows the table to describe all state
+ * transitions defined in the specification
+ */
+static int cp_transitions[16][10] = {
+/* Up */
+{ 2, IRC|SCR|6, INV, INV, INV, INV, INV, INV, INV, INV },
+/* Down */
+{ INV, INV, 0, TLS|1, 0, 1, 1, 1, 1, TLD|1 },
+/* Open */
+{ TLS|1, 1, IRC|SCR|6, 3, 5, 5, 6, 7, 8, 9 },
+/* Close */
+{ 0, TLF|0, 2, 2, 4, 4, IRC|STR|4, IRC|STR|4, IRC|STR|4, TLD|IRC|STR|4 },
+/* TO+ */
+{ INV, INV, INV, INV, STR|4, STR|5, SCR|6, SCR|6, SCR|8, INV },
+/* TO- */
+{ INV, INV, INV, INV, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3, INV },
+/* RCR+ */
+{ INV, INV, STA|2, IRC|SCR|SCA|8, 4, 5, SCA|8, SCA|TLU|9, SCA|8, TLD|SCR|SCA|8 },
+/* RCR- */
+{ INV, INV, STA|2, IRC|SCR|SCN|6, 4, 5, SCN|6, SCN|7, SCN|6, TLD|SCR|SCN|6 },
+/* RCA */
+{ INV, INV, STA|2, STA|3, 4, 5, IRC|7, SCR|6, IRC|TLU|9, TLD|SCR|6 },
+/* RCN */
+{ INV, INV, STA|2, STA|3, 4, 5, IRC|SCR|6, SCR|6, IRC|SCR|8, TLD|SCR|6 },
+/* RTR */
+{ INV, INV, STA|2, STA|3, STA|4, STA|5, STA|6, STA|6, STA|6, TLD|ZRC|STA|5 },
+/* RTA */
+{ INV, INV, 2, 3, TLF|2, TLF|3, 6, 6, 8, TLD|SCR|6 },
+/* RUC */
+{ INV, INV, SCJ|2, SCJ|3, SCJ|4, SCJ|5, SCJ|6, SCJ|7, SCJ|8, SCJ|9 },
+/* RXJ+ */
+{ INV, INV, 2, 3, 4, 5, 6, 6, 8, 9 },
+/* RXJ- */
+{ INV, INV, TLF|2, TLF|3, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3, TLD|IRC|STR|5 },
+/* RXR */
+{ INV, INV, 2, 3, 4, 5, 6, 7, 8, SER|9 },
+};
+
+enum pppcp_event_type {
+       UP              = 0,
+       DOWN            = 1,
+       OPEN            = 2,
+       CLOSE           = 3,
+       TO_PLUS         = 4,
+       TO_MINUS        = 5,
+       RCR_PLUS        = 6,
+       RCR_MINUS       = 7,
+       RCA             = 8,
+       RCN             = 9,
+       RTR             = 10,
+       RTA             = 11,
+       RUC             = 12,
+       RXJ_PLUS        = 13,
+       RXJ_MINUS       = 14,
+       RXR             = 15,
+};
+
+struct pppcp_timer_data {
+       struct pppcp_data *data;
+       guint restart_counter;
+       guint restart_interval;
+       guint max_counter;
+       guint restart_timer;
+};
+
+struct pppcp_data {
+       unsigned char state;
+       struct pppcp_timer_data config_timer_data;
+       struct pppcp_timer_data terminate_timer_data;
+       guint max_failure;
+       guint failure_counter;
+       GAtPPP *ppp;
+       guint8 config_identifier;
+       guint8 terminate_identifier;
+       guint8 reject_identifier;
+       const guint8 *local_options;
+       guint16 local_options_len;
+       guint8 *peer_options;
+       guint16 peer_options_len;
+       gboolean send_reject;
+       const struct pppcp_proto *driver;
+       gpointer priv;
+};
+
+static void pppcp_generate_event(struct pppcp_data *data,
+                               enum pppcp_event_type event_type,
+                               const guint8 *packet, guint len);
+
+static void pppcp_packet_free(struct pppcp_packet *packet)
+{
+       g_free(pppcp_to_ppp_packet(packet));
+}
+
+static struct pppcp_packet *pppcp_packet_new(struct pppcp_data *data,
+                                               guint type, guint bufferlen)
+{
+       struct pppcp_packet *packet;
+       struct ppp_header *ppp_packet;
+       guint16 packet_length = bufferlen + sizeof(*packet);
+
+       ppp_packet = ppp_packet_new(packet_length, data->driver->proto);
+       if (ppp_packet == NULL)
+               return NULL;
+
+       /* advance past protocol to add CP header information */
+       packet = (struct pppcp_packet *) (ppp_packet->info);
+
+       packet->length = htons(packet_length);
+       packet->code = type;
+       return packet;
+}
+
+void ppp_option_iter_init(struct ppp_option_iter *iter,
+                                       const struct pppcp_packet *packet)
+{
+       iter->max = ntohs(packet->length) - CP_HEADER_SZ;
+       iter->pdata = packet->data;
+       iter->pos = 0;
+       iter->type = 0;
+       iter->len = 0;
+       iter->option_data = NULL;
+}
+
+gboolean ppp_option_iter_next(struct ppp_option_iter *iter)
+{
+       const guint8 *cur = iter->pdata + iter->pos;
+       const guint8 *end = iter->pdata + iter->max;
+
+       if (cur + 1 > end)
+               return FALSE;
+
+       if (cur + cur[1] > end)
+               return FALSE;
+
+       iter->type = cur[0];
+       iter->len = cur[1] - 2;
+       iter->option_data = cur + 2;
+
+       iter->pos += cur[1];
+
+       return TRUE;
+}
+
+guint8 ppp_option_iter_get_type(struct ppp_option_iter *iter)
+{
+       return iter->type;
+}
+
+guint8 ppp_option_iter_get_length(struct ppp_option_iter *iter)
+{
+       return iter->len;
+}
+
+const guint8 *ppp_option_iter_get_data(struct ppp_option_iter *iter)
+{
+       return iter->option_data;
+}
+
+guint8 pppcp_get_code(const guint8 *data)
+{
+       struct ppp_header *ppp_packet = (struct ppp_header *) data;
+       struct pppcp_packet *packet = (struct pppcp_packet *) ppp_packet->info;
+
+       return packet->code;
+}
+
+static gboolean pppcp_timeout(gpointer user_data)
+{
+       struct pppcp_timer_data *timer_data = user_data;
+
+       pppcp_trace(timer_data->data);
+
+       timer_data->restart_timer = 0;
+
+       if (timer_data->restart_counter > 0)
+               pppcp_generate_event(timer_data->data, TO_PLUS, NULL, 0);
+       else
+               pppcp_generate_event(timer_data->data, TO_MINUS, NULL, 0);
+
+       return FALSE;
+}
+
+static void pppcp_stop_timer(struct pppcp_timer_data *timer_data)
+{
+       if (timer_data->restart_timer > 0) {
+               g_source_remove(timer_data->restart_timer);
+               timer_data->restart_timer = 0;
+       }
+}
+
+static void pppcp_start_timer(struct pppcp_timer_data *timer_data)
+{
+       pppcp_stop_timer(timer_data);
+
+       timer_data->restart_timer =
+               g_timeout_add_seconds(timer_data->restart_interval,
+                               pppcp_timeout, timer_data);
+}
+
+static gboolean is_first_request(struct pppcp_timer_data *timer_data)
+{
+       return (timer_data->restart_counter == timer_data->max_counter);
+}
+
+/* actions */
+/* log an illegal event, but otherwise do nothing */
+static void pppcp_illegal_event(GAtPPP *ppp, guint8 state, guint8 type)
+{
+       DBG(ppp, "Illegal event %d while in state %d", type, state);
+}
+
+static void pppcp_this_layer_up(struct pppcp_data *data)
+{
+       if (data->driver->this_layer_up)
+               data->driver->this_layer_up(data);
+}
+
+static void pppcp_this_layer_down(struct pppcp_data *data)
+{
+       if (data->driver->this_layer_down)
+               data->driver->this_layer_down(data);
+}
+
+static void pppcp_this_layer_started(struct pppcp_data *data)
+{
+       if (data->driver->this_layer_started)
+               data->driver->this_layer_started(data);
+}
+
+static void pppcp_this_layer_finished(struct pppcp_data *data)
+{
+       pppcp_trace(data);
+       if (data->driver->this_layer_finished)
+               data->driver->this_layer_finished(data);
+}
+
+/*
+ * set the restart counter to either max-terminate
+ * or max-configure.  The counter is decremented for
+ * each transmission, including the first.
+ */
+static void pppcp_initialize_restart_count(struct pppcp_timer_data *timer_data)
+{
+       struct pppcp_data *data = timer_data->data;
+
+       pppcp_trace(data);
+
+       timer_data->restart_counter = timer_data->max_counter;
+}
+
+/*
+ * set restart counter to zero
+ */
+static void pppcp_zero_restart_count(struct pppcp_timer_data *timer_data)
+{
+       timer_data->restart_counter = 0;
+}
+
+/*
+ * TBD - generate new identifier for packet
+ */
+static guint8 new_identity(struct pppcp_data *data, guint prev_identifier)
+{
+       return prev_identifier + 1;
+}
+
+/*
+ * transmit a Configure-Request packet
+ * start the restart timer
+ * decrement the restart counter
+ */
+static void pppcp_send_configure_request(struct pppcp_data *pppcp)
+{
+       struct pppcp_packet *packet;
+       struct pppcp_timer_data *timer_data = &pppcp->config_timer_data;
+
+       pppcp_trace(pppcp);
+
+       packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_REQUEST,
+                                       pppcp->local_options_len);
+       memcpy(packet->data, pppcp->local_options, pppcp->local_options_len);
+
+       /*
+        * if this is the first request, we need a new identifier.
+        * if this is a retransmission, leave the identifier alone.
+        */
+       if (is_first_request(timer_data))
+               pppcp->config_identifier =
+                       new_identity(pppcp, pppcp->config_identifier);
+       packet->identifier = pppcp->config_identifier;
+
+       ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(packet->length));
+
+       pppcp_packet_free(packet);
+
+       /* start timer for retransmission */
+       timer_data->restart_counter--;
+       pppcp_start_timer(timer_data);
+}
+
+/*
+ * transmit a Configure-Ack packet
+ */
+static void pppcp_send_configure_ack(struct pppcp_data *pppcp,
+                                       const guint8 *request)
+{
+       struct pppcp_packet *packet;
+       struct pppcp_packet *cr_req = (struct pppcp_packet *) request;
+       guint16 len;
+
+       pppcp_trace(pppcp);
+
+       pppcp->failure_counter = 0;
+
+       /* subtract for header. */
+       len = ntohs(cr_req->length) - CP_HEADER_SZ;
+
+       packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_ACK, len);
+
+       memcpy(packet->data, cr_req->data, len);
+       packet->identifier = cr_req->identifier;
+       ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(packet->length));
+       pppcp_packet_free(packet);
+}
+
+/*
+ * transmit a Configure-Nak or Configure-Reject packet
+ */
+static void pppcp_send_configure_nak(struct pppcp_data *pppcp,
+                                       const guint8 *request)
+{
+       struct pppcp_packet *packet;
+       struct pppcp_packet *cr_req = (struct pppcp_packet *) request;
+
+       pppcp_trace(pppcp);
+
+       /*
+        * if we have exceeded our Max-Failure counter, we simply reject all
+        * the options.
+        */
+       if (pppcp->failure_counter >= pppcp->max_failure) {
+               guint16 len = ntohs(cr_req->length) - CP_HEADER_SZ;
+
+               packet = pppcp_packet_new(pppcp,
+                                       PPPCP_CODE_TYPE_CONFIGURE_REJECT, len);
+               memcpy(packet->data, cr_req->data, len);
+       } else {
+               enum pppcp_code code = PPPCP_CODE_TYPE_CONFIGURE_NAK;
+
+               if (pppcp->send_reject == TRUE)
+                       code = PPPCP_CODE_TYPE_CONFIGURE_REJECT;
+               else
+                       pppcp->failure_counter++;
+
+               packet = pppcp_packet_new(pppcp, code, pppcp->peer_options_len);
+               memcpy(packet->data, pppcp->peer_options,
+                                               pppcp->peer_options_len);
+       }
+
+       packet->identifier = cr_req->identifier;
+       ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(packet->length));
+
+       pppcp_packet_free(packet);
+
+       g_free(pppcp->peer_options);
+       pppcp->peer_options = NULL;
+       pppcp->peer_options_len = 0;
+}
+
+/*
+ * transmit a Terminate-Request packet.
+ * start the restart timer.
+ * decrement the restart counter
+ */
+static void pppcp_send_terminate_request(struct pppcp_data *data)
+{
+       struct pppcp_packet *packet;
+       struct pppcp_timer_data *timer_data = &data->terminate_timer_data;
+
+       pppcp_trace(data);
+
+       /*
+        * the data field can be used by the sender (us).
+        * leave this empty for now.
+        */
+       packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_TERMINATE_REQUEST, 0);
+
+       /*
+        * Is this a retransmission?  If so, do not change
+        * the identifier.  If not, we need a fresh identity.
+        */
+       if (is_first_request(timer_data))
+               data->terminate_identifier =
+                       new_identity(data, data->terminate_identifier);
+       packet->identifier = data->terminate_identifier;
+       ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(packet->length));
+
+       pppcp_packet_free(packet);
+       timer_data->restart_counter--;
+       pppcp_start_timer(timer_data);
+}
+
+/*
+ * transmit a Terminate-Ack packet
+ */
+static void pppcp_send_terminate_ack(struct pppcp_data *data,
+                                       const guint8 *request)
+{
+       struct pppcp_packet *packet;
+       struct pppcp_packet *pppcp_header = (struct pppcp_packet *) request;
+       struct pppcp_timer_data *timer_data = &data->terminate_timer_data;
+
+       pppcp_trace(data);
+
+       packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_TERMINATE_ACK, 0);
+
+       /* match identifier of the request */
+       packet->identifier = pppcp_header->identifier;
+
+       ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(pppcp_header->length));
+
+       pppcp_packet_free(packet);
+       pppcp_start_timer(timer_data);
+}
+
+/*
+ * transmit a Code-Reject packet
+ *
+ * XXX this seg faults.
+ */
+static void pppcp_send_code_reject(struct pppcp_data *data,
+                                       const guint8 *rejected_packet)
+{
+       struct pppcp_packet *packet;
+       const struct pppcp_packet *old_packet =
+                               (const struct pppcp_packet *) rejected_packet;
+
+       pppcp_trace(data);
+
+       packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_CODE_REJECT,
+                                               ntohs(old_packet->length));
+
+       /*
+        * Identifier must be changed for each Code-Reject sent
+        */
+       packet->identifier = new_identity(data, data->reject_identifier);
+
+       /*
+        * rejected packet should be copied in, but it should be
+        * truncated if it needs to be to comply with mtu requirement
+        */
+       memcpy(packet->data, rejected_packet,
+                       ntohs(packet->length) - CP_HEADER_SZ);
+
+       ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(packet->length));
+
+       pppcp_packet_free(packet);
+}
+
+/*
+ * transmit an Echo-Reply packet
+ */
+static void pppcp_send_echo_reply(struct pppcp_data *data,
+                                               const guint8 *request)
+{
+       struct pppcp_packet *packet;
+       struct pppcp_packet *header = (struct pppcp_packet *) request;
+
+       /*
+        * 0 bytes for data, 4 bytes for magic number
+        */
+       packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_ECHO_REPLY, 4);
+
+       /*
+        * match identifier of request
+        */
+       packet->identifier = header->identifier;
+
+       /* magic number will always be zero */
+       ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(packet->length));
+
+       pppcp_packet_free(packet);
+}
+
+static void pppcp_transition_state(enum pppcp_state new_state,
+                                       struct pppcp_data *data)
+{
+       /*
+        * if switching from a state where
+        * TO events occur, to one where they
+        * may not, shut off the timer
+        */
+       switch (new_state) {
+       case INITIAL:
+       case STARTING:
+       case CLOSED:
+       case STOPPED:
+       case OPENED:
+               pppcp_stop_timer(&data->config_timer_data);
+               pppcp_stop_timer(&data->terminate_timer_data);
+               break;
+       case CLOSING:
+       case STOPPING:
+       case REQSENT:
+       case ACKRCVD:
+       case ACKSENT:
+               break;
+       }
+       data->state = new_state;
+}
+
+/*
+ * send the event handler a new event to process
+ */
+static void pppcp_generate_event(struct pppcp_data *data,
+                               enum pppcp_event_type event_type,
+                               const guint8 *packet, guint len)
+{
+       int actions;
+       unsigned char new_state;
+
+       if (event_type > RXR)
+               goto error;
+
+       pppcp_trace(data);
+
+       actions = cp_transitions[event_type][data->state];
+       new_state = actions & 0xf;
+
+       pppcp_trace_event(data, event_type, actions, new_state);
+
+       if (actions & INV)
+               goto error;
+
+       if (actions & IRC) {
+               struct pppcp_timer_data *timer_data;
+
+               if (new_state == CLOSING || new_state == STOPPING)
+                       timer_data = &data->terminate_timer_data;
+               else
+                       timer_data = &data->config_timer_data;
+
+               pppcp_initialize_restart_count(timer_data);
+       } else if (actions & ZRC)
+               pppcp_zero_restart_count(&data->terminate_timer_data);
+
+       if (actions & SCR)
+               pppcp_send_configure_request(data);
+
+       if (actions & SCA)
+               pppcp_send_configure_ack(data, packet);
+       else if (actions & SCN)
+               pppcp_send_configure_nak(data, packet);
+
+       if (actions & STR)
+               pppcp_send_terminate_request(data);
+       else if (actions & STA)
+               pppcp_send_terminate_ack(data, packet);
+
+       if (actions & SCJ)
+               pppcp_send_code_reject(data, packet);
+
+       if (actions & SER)
+               pppcp_send_echo_reply(data, packet);
+
+       pppcp_transition_state(new_state, data);
+
+       if (actions & TLS)
+               pppcp_this_layer_started(data);
+       else if (actions & TLU)
+               pppcp_this_layer_up(data);
+       else if (actions & TLD)
+               pppcp_this_layer_down(data);
+       else if (actions & TLF)
+               pppcp_this_layer_finished(data);
+
+       return;
+
+error:
+       pppcp_illegal_event(data->ppp, data->state, event_type);
+}
+
+void pppcp_signal_open(struct pppcp_data *data)
+{
+       pppcp_generate_event(data, OPEN, NULL, 0);
+}
+
+void pppcp_signal_close(struct pppcp_data *data)
+{
+       pppcp_generate_event(data, CLOSE, NULL, 0);
+}
+
+void pppcp_signal_up(struct pppcp_data *data)
+{
+       pppcp_generate_event(data, UP, NULL, 0);
+}
+
+void pppcp_signal_down(struct pppcp_data *data)
+{
+       pppcp_generate_event(data, DOWN, NULL, 0);
+}
+
+static guint8 pppcp_process_configure_request(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet)
+{
+       pppcp_trace(pppcp);
+
+       if (pppcp->failure_counter >= pppcp->max_failure)
+               return RCR_MINUS;
+
+       if (pppcp->driver->rcr) {
+               enum rcr_result res;
+
+               res = pppcp->driver->rcr(pppcp, packet,
+                                               &pppcp->peer_options,
+                                               &pppcp->peer_options_len);
+
+               if (res == RCR_REJECT) {
+                       pppcp->send_reject = TRUE;
+                       return RCR_MINUS;
+               } else if (res == RCR_NAK) {
+                       pppcp->send_reject = FALSE;
+                       return RCR_MINUS;
+               }
+       }
+
+       return RCR_PLUS;
+}
+
+static guint8 pppcp_process_configure_ack(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet)
+{
+       gint len;
+
+       pppcp_trace(pppcp);
+
+       len = ntohs(packet->length) - CP_HEADER_SZ;
+
+       /* if identifiers don't match, we should silently discard */
+       if (packet->identifier != pppcp->config_identifier) {
+               return 0;
+       }
+
+       /*
+        * First we must sanity check that all config options acked are
+        * equal to the config options sent and are in the same order.
+        * If this is not the case, then silently drop the packet
+        */
+       if (pppcp->local_options_len != len)
+               return 0;
+
+       if (memcmp(pppcp->local_options, packet->data, len))
+               return 0;
+
+       /* Otherwise, apply local options */
+       if (pppcp->driver->rca)
+               pppcp->driver->rca(pppcp, packet);
+
+       return RCA;
+}
+
+static guint8 pppcp_process_configure_nak(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet)
+{
+       pppcp_trace(pppcp);
+
+       /* if identifiers don't match, we should silently discard */
+       if (packet->identifier != pppcp->config_identifier)
+               return 0;
+
+       if (pppcp->driver->rcn_nak)
+               pppcp->driver->rcn_nak(pppcp, packet);
+
+       return RCN;
+}
+
+static guint8 pppcp_process_configure_reject(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet)
+{
+       pppcp_trace(pppcp);
+
+       /*
+        * make sure identifier matches that of last sent configure
+        * request
+        */
+       if (packet->identifier != pppcp->config_identifier)
+               return 0;
+
+       /*
+        * check to see which options were rejected
+        * Rejected options must be a subset of requested
+        * options and in the same order.
+        *
+        * when a new configure-request is sent, we may
+        * not request any of these options be negotiated
+        */
+       if (pppcp->driver->rcn_rej)
+               pppcp->driver->rcn_rej(pppcp, packet);
+
+       return RCN;
+}
+
+static guint8 pppcp_process_terminate_request(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet)
+{
+       pppcp_trace(data);
+
+       return RTR;
+}
+
+static guint8 pppcp_process_terminate_ack(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet)
+{
+       /*
+        * if we wind up using the data field for anything, then
+        * we'd want to check the identifier.
+        * even if the identifiers don't match, we still handle
+        * a terminate ack, as it is allowed to be unelicited
+        */
+       pppcp_trace(data);
+
+       return RTA;
+}
+
+static guint8 pppcp_process_code_reject(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet)
+{
+       /*
+        * determine if the code reject is catastrophic or not.
+        * return RXJ_PLUS if this reject is acceptable, RXJ_MINUS if
+        * it is catastrophic.
+        *
+        * for now we always return RXJ_MINUS.  Any code
+        * reject will be catastrophic, since we only support the
+        * bare minimum number of codes necessary to function.
+        */
+       return RXJ_MINUS;
+}
+
+static guint8 pppcp_process_protocol_reject(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet)
+{
+       /*
+        * determine if the protocol reject is catastrophic or not.
+        * return RXJ_PLUS if this reject is acceptable, RXJ_MINUS if
+        * it is catastrophic.
+        *
+        * for now we always return RXJ_MINUS.  Any protocol
+        * reject will be catastrophic, since we only support the
+        * bare minimum number of protocols necessary to function.
+        */
+       return RXJ_MINUS;
+}
+
+/*
+ * For Echo-Request, Echo-Reply, and Discard-Request, we will not
+ * bother checking the magic number of the packet, because we will
+ * never send an echo or discard request.  We can't reliably detect
+ * loop back anyway, since we don't negotiate a magic number.
+ */
+static guint8 pppcp_process_echo_request(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet)
+{
+       return RXR;
+}
+
+static guint8 pppcp_process_echo_reply(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet)
+{
+       return 0;
+}
+
+static guint8 pppcp_process_discard_request(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet)
+{
+       return 0;
+}
+
+static guint8 (*packet_ops[11])(struct pppcp_data *data,
+                                       const struct pppcp_packet *packet) = {
+       pppcp_process_configure_request,
+       pppcp_process_configure_ack,
+       pppcp_process_configure_nak,
+       pppcp_process_configure_reject,
+       pppcp_process_terminate_request,
+       pppcp_process_terminate_ack,
+       pppcp_process_code_reject,
+       pppcp_process_protocol_reject,
+       pppcp_process_echo_request,
+       pppcp_process_echo_reply,
+       pppcp_process_discard_request,
+};
+
+void pppcp_send_protocol_reject(struct pppcp_data *data,
+                               const guint8 *rejected_packet, gsize len)
+{
+       struct pppcp_packet *packet;
+
+       pppcp_trace(data);
+
+       /*
+        * Protocol-Reject can only be sent when we are in
+        * the OPENED state.  If in any other state, silently discard.
+        */
+       if (data->state != OPENED)
+               return;
+
+       /*
+        * info should contain the old packet info, plus the 16bit
+        * protocol number we are rejecting.
+        */
+       packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_PROTOCOL_REJECT,
+                                       len - 2);
+
+       /*
+        * Identifier must be changed for each Protocol-Reject sent
+        */
+       packet->identifier = new_identity(data, data->reject_identifier);
+
+       /*
+        * rejected packet should be copied in, but it should be
+        * truncated if it needs to be to comply with mtu requirement
+        */
+       memcpy(packet->data, rejected_packet + 2, len - 2);
+
+       ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
+                       ntohs(packet->length));
+
+       pppcp_packet_free(packet);
+}
+
+/*
+ * parse the packet and determine which event this packet caused
+ */
+void pppcp_process_packet(gpointer priv, const guint8 *new_packet, gsize len)
+{
+       struct pppcp_data *data = priv;
+       const struct pppcp_packet *packet =
+                               (const struct pppcp_packet *) new_packet;
+       guint8 event_type;
+       guint data_len = 0;
+
+       if (len < sizeof(struct pppcp_packet))
+               return;
+
+       /* check flags to see if we support this code */
+       if (!(data->driver->supported_codes & (1 << packet->code)))
+               event_type = RUC;
+       else
+               event_type = packet_ops[packet->code-1](data, packet);
+
+       if (event_type) {
+               data_len = ntohs(packet->length);
+               pppcp_generate_event(data, event_type, new_packet, data_len);
+       }
+}
+
+void pppcp_free(struct pppcp_data *pppcp)
+{
+       pppcp_stop_timer(&pppcp->config_timer_data);
+       pppcp_stop_timer(&pppcp->terminate_timer_data);
+       g_free(pppcp->peer_options);
+       g_free(pppcp);
+}
+
+void pppcp_set_data(struct pppcp_data *pppcp, gpointer data)
+{
+       pppcp->priv = data;
+}
+
+gpointer pppcp_get_data(struct pppcp_data *pppcp)
+{
+       return pppcp->priv;
+}
+
+GAtPPP *pppcp_get_ppp(struct pppcp_data *pppcp)
+{
+       return pppcp->ppp;
+}
+
+void pppcp_set_local_options(struct pppcp_data *pppcp,
+                                       const guint8 *options, guint16 len)
+{
+       pppcp->local_options = options;
+       pppcp->local_options_len = len;
+}
+
+struct pppcp_data *pppcp_new(GAtPPP *ppp, const struct pppcp_proto *proto,
+                               gboolean dormant, guint max_failure)
+{
+       struct pppcp_data *data;
+
+       data = g_try_malloc0(sizeof(struct pppcp_data));
+       if (data == NULL)
+               return NULL;
+
+       if (dormant)
+               data->state = STOPPED;
+       else
+               data->state = INITIAL;
+
+       data->config_timer_data.restart_interval = INITIAL_RESTART_TIMEOUT;
+       data->terminate_timer_data.restart_interval = INITIAL_RESTART_TIMEOUT;
+       data->config_timer_data.max_counter = MAX_CONFIGURE;
+       data->terminate_timer_data.max_counter = MAX_TERMINATE;
+       data->config_timer_data.data = data;
+       data->terminate_timer_data.data = data;
+
+       if (max_failure)
+               data->max_failure = max_failure;
+       else
+               data->max_failure = MAX_FAILURE;
+
+       data->ppp = ppp;
+       data->driver = proto;
+
+       return data;
+}
diff --git a/gatchat/ppp_cp.h b/gatchat/ppp_cp.h
new file mode 100644 (file)
index 0000000..2a70740
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct pppcp_data;
+struct ppp_option_iter;
+
+/* option format */
+struct ppp_option {
+       guint8 type;
+       guint8 length;
+       guint8 data[0];
+};
+
+enum rcr_result {
+       RCR_ACCEPT,
+       RCR_REJECT,
+       RCR_NAK,
+};
+
+enum pppcp_code {
+       PPPCP_CODE_TYPE_CONFIGURE_REQUEST = 1,
+       PPPCP_CODE_TYPE_CONFIGURE_ACK,
+       PPPCP_CODE_TYPE_CONFIGURE_NAK,
+       PPPCP_CODE_TYPE_CONFIGURE_REJECT,
+       PPPCP_CODE_TYPE_TERMINATE_REQUEST,
+       PPPCP_CODE_TYPE_TERMINATE_ACK,
+       PPPCP_CODE_TYPE_CODE_REJECT,
+       PPPCP_CODE_TYPE_PROTOCOL_REJECT,
+       PPPCP_CODE_TYPE_ECHO_REQUEST,
+       PPPCP_CODE_TYPE_ECHO_REPLY,
+       PPPCP_CODE_TYPE_DISCARD_REQUEST
+};
+
+struct pppcp_packet {
+       guint8 code;
+       guint8 identifier;
+       guint16 length;
+       guint8 data[0];
+} __attribute__((packed));
+
+struct ppp_option_iter {
+       guint16 max;
+       guint16 pos;
+       const guint8 *pdata;
+       guint8 type;
+       guint8 len;
+       const guint8 *option_data;
+};
+
+struct pppcp_proto {
+       guint16 proto;
+       const char *name;
+       guint16 supported_codes;
+       void (*this_layer_up)(struct pppcp_data *data);
+       void (*this_layer_down)(struct pppcp_data *data);
+       void (*this_layer_started)(struct pppcp_data *data);
+       void (*this_layer_finished)(struct pppcp_data *data);
+       /* Remote side acked these options, we can now use them */
+       void (*rca)(struct pppcp_data *pppcp, const struct pppcp_packet *pkt);
+       /*
+        * Remote side sent us an Conf-Req-Nak or Conf-Req-Rej.  The protocol
+        * driver should examine the packet and update its options accordingly,
+        * then use set_local_options to set a new set of options to try
+        * before returning
+        */
+       void (*rcn_nak)(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *pkt);
+       void (*rcn_rej)(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *pkt);
+       /*
+        * Remote side has sent us a request with its options, return whether
+        * we should ack / nak / rej these options.  In the case of nak / rej,
+        * the list of options to be sent to the peer is given in the
+        * new_options & new_len out arguments
+        */
+       enum rcr_result (*rcr)(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *pkt,
+                                       guint8 **new_options, guint16 *new_len);
+};
+
+void ppp_option_iter_init(struct ppp_option_iter *iter,
+                                       const struct pppcp_packet *packet);
+gboolean ppp_option_iter_next(struct ppp_option_iter *iter);
+guint8 ppp_option_iter_get_type(struct ppp_option_iter *iter);
+guint8 ppp_option_iter_get_length(struct ppp_option_iter *iter);
+const guint8 *ppp_option_iter_get_data(struct ppp_option_iter *iter);
+
+struct pppcp_data *pppcp_new(GAtPPP *ppp, const struct pppcp_proto *proto,
+                               gboolean dormant, guint max_failure);
+void pppcp_free(struct pppcp_data *data);
+
+void pppcp_set_data(struct pppcp_data *pppcp, gpointer data);
+gpointer pppcp_get_data(struct pppcp_data *pppcp);
+GAtPPP *pppcp_get_ppp(struct pppcp_data *pppcp);
+
+guint8 pppcp_get_code(const guint8 *data);
+
+void pppcp_set_local_options(struct pppcp_data *data,
+                               const guint8 *options,
+                               guint16 len);
+
+void pppcp_process_packet(gpointer priv, const guint8 *new_packet, gsize len);
+void pppcp_send_protocol_reject(struct pppcp_data *data,
+                               const guint8 *rejected_packet, gsize len);
+void pppcp_signal_open(struct pppcp_data *data);
+void pppcp_signal_close(struct pppcp_data *data);
+void pppcp_signal_up(struct pppcp_data *data);
+void pppcp_signal_down(struct pppcp_data *data);
diff --git a/gatchat/ppp_ipcp.c b/gatchat/ppp_ipcp.c
new file mode 100644 (file)
index 0000000..439ad31
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <arpa/inet.h>
+#include <glib.h>
+
+#include "gatutil.h"
+#include "gatppp.h"
+#include "ppp.h"
+
+#define IPCP_SUPPORTED_CODES   ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CODE_REJECT))
+
+enum ipcp_option_types {
+       IP_ADDRESSES            = 1,
+       IP_COMPRESSION_PROTO    = 2,
+       IP_ADDRESS              = 3,
+       MOBILE_IPV4             = 4,
+       PRIMARY_DNS_SERVER      = 129,
+       PRIMARY_NBNS_SERVER     = 130,
+       SECONDARY_DNS_SERVER    = 131,
+       SECONDARY_NBNS_SERVER   = 132,
+};
+
+/* We request IP_ADDRESS, PRIMARY/SECONDARY DNS & NBNS */
+#define MAX_CONFIG_OPTION_SIZE 5*6
+
+#define REQ_OPTION_IPADDR      0x01
+#define REQ_OPTION_DNS1                0x02
+#define REQ_OPTION_DNS2                0x04
+#define REQ_OPTION_NBNS1       0x08
+#define REQ_OPTION_NBNS2       0x10
+
+#define MAX_IPCP_FAILURE       100
+
+struct ipcp_data {
+       guint8 options[MAX_CONFIG_OPTION_SIZE];
+       guint16 options_len;
+       guint8 req_options;
+       guint32 local_addr;
+       guint32 peer_addr;
+       guint32 dns1;
+       guint32 dns2;
+       guint32 nbns1;
+       guint32 nbns2;
+       gboolean is_server;
+};
+
+#define FILL_IP(options, req, type, var)               \
+       if (req) {                                      \
+               options[len] = type;                    \
+               options[len + 1] = 6;                   \
+               memcpy(options + len + 2, var, 4);      \
+                                                       \
+               len += 6;                               \
+       }                                               \
+
+static void ipcp_generate_config_options(struct ipcp_data *ipcp)
+{
+       guint16 len = 0;
+
+       FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_IPADDR,
+                                       IP_ADDRESS, &ipcp->local_addr);
+       FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_DNS1,
+                                       PRIMARY_DNS_SERVER, &ipcp->dns1);
+       FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_DNS2,
+                                       SECONDARY_DNS_SERVER, &ipcp->dns2);
+       FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_NBNS1,
+                                       PRIMARY_NBNS_SERVER, &ipcp->nbns1);
+       FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_NBNS2,
+                                       SECONDARY_NBNS_SERVER, &ipcp->nbns2);
+
+       ipcp->options_len = len;
+}
+
+static void ipcp_reset_client_config_options(struct ipcp_data *ipcp)
+{
+       ipcp->req_options = REQ_OPTION_IPADDR | REQ_OPTION_DNS1 |
+                               REQ_OPTION_DNS2 | REQ_OPTION_NBNS1 |
+                               REQ_OPTION_NBNS2;
+
+       ipcp->local_addr = 0;
+       ipcp->peer_addr = 0;
+       ipcp->dns1 = 0;
+       ipcp->dns2 = 0;
+       ipcp->nbns1 = 0;
+       ipcp->nbns2 = 0;
+
+       ipcp_generate_config_options(ipcp);
+}
+
+static void ipcp_reset_server_config_options(struct ipcp_data *ipcp)
+{
+       if (ipcp->local_addr != 0)
+               ipcp->req_options = REQ_OPTION_IPADDR;
+       else
+               ipcp->req_options = 0;
+
+       ipcp_generate_config_options(ipcp);
+}
+
+void ipcp_set_server_info(struct pppcp_data *pppcp, guint32 peer_addr,
+                               guint32 dns1, guint32 dns2)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(pppcp);
+
+       ipcp->peer_addr = peer_addr;
+       ipcp->dns1 = dns1;
+       ipcp->dns2 = dns2;
+}
+
+static void ipcp_up(struct pppcp_data *pppcp)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(pppcp);
+       char local[INET_ADDRSTRLEN];
+       char peer[INET_ADDRSTRLEN];
+       char dns1[INET_ADDRSTRLEN];
+       char dns2[INET_ADDRSTRLEN];
+       struct in_addr addr;
+
+       memset(local, 0, sizeof(local));
+       addr.s_addr = ipcp->local_addr;
+       inet_ntop(AF_INET, &addr, local, INET_ADDRSTRLEN);
+
+       memset(peer, 0, sizeof(peer));
+       addr.s_addr = ipcp->peer_addr;
+       inet_ntop(AF_INET, &addr, peer, INET_ADDRSTRLEN);
+
+       memset(dns1, 0, sizeof(dns1));
+       addr.s_addr = ipcp->dns1;
+       inet_ntop(AF_INET, &addr, dns1, INET_ADDRSTRLEN);
+
+       memset(dns2, 0, sizeof(dns2));
+       addr.s_addr = ipcp->dns2;
+       inet_ntop(AF_INET, &addr, dns2, INET_ADDRSTRLEN);
+
+       ppp_ipcp_up_notify(pppcp_get_ppp(pppcp), local[0] ? local : NULL,
+                                       peer[0] ? peer : NULL,
+                                       dns1[0] ? dns1 : NULL,
+                                       dns2[0] ? dns2 : NULL);
+}
+
+static void ipcp_down(struct pppcp_data *pppcp)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(pppcp);
+
+       if (ipcp->is_server)
+               ipcp_reset_server_config_options(ipcp);
+       else
+               ipcp_reset_client_config_options(ipcp);
+
+       pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len);
+       ppp_ipcp_down_notify(pppcp_get_ppp(pppcp));
+}
+
+static void ipcp_finished(struct pppcp_data *pppcp)
+{
+       ppp_ipcp_finished_notify(pppcp_get_ppp(pppcp));
+}
+
+static void ipcp_rca(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       if (ipcp->is_server)
+               return;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IP_ADDRESS:
+                       memcpy(&ipcp->local_addr, data, 4);
+                       break;
+               case PRIMARY_DNS_SERVER:
+                       memcpy(&ipcp->dns1, data, 4);
+                       break;
+               case PRIMARY_NBNS_SERVER:
+                       memcpy(&ipcp->nbns1, data, 4);
+                       break;
+               case SECONDARY_DNS_SERVER:
+                       memcpy(&ipcp->dns2, data, 4);
+                       break;
+               case SECONDARY_NBNS_SERVER:
+                       memcpy(&ipcp->nbns2, data, 4);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void ipcp_rcn_nak(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       if (ipcp->is_server)
+               return;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IP_ADDRESS:
+                       ipcp->req_options |= REQ_OPTION_IPADDR;
+                       memcpy(&ipcp->local_addr, data, 4);
+                       break;
+               case PRIMARY_DNS_SERVER:
+                       ipcp->req_options |= REQ_OPTION_DNS1;
+                       memcpy(&ipcp->dns1, data, 4);
+                       break;
+               case PRIMARY_NBNS_SERVER:
+                       ipcp->req_options |= REQ_OPTION_NBNS1;
+                       memcpy(&ipcp->nbns1, data, 4);
+                       break;
+               case SECONDARY_DNS_SERVER:
+                       ipcp->req_options |= REQ_OPTION_DNS2;
+                       memcpy(&ipcp->dns2, data, 4);
+                       break;
+               case SECONDARY_NBNS_SERVER:
+                       ipcp->req_options |= REQ_OPTION_NBNS2;
+                       memcpy(&ipcp->nbns2, data, 4);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ipcp_generate_config_options(ipcp);
+       pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len);
+}
+
+static void ipcp_rcn_rej(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IP_ADDRESS:
+                       ipcp->req_options &= ~REQ_OPTION_IPADDR;
+                       break;
+               case PRIMARY_DNS_SERVER:
+                       ipcp->req_options &= ~REQ_OPTION_DNS1;
+                       break;
+               case PRIMARY_NBNS_SERVER:
+                       ipcp->req_options &= ~REQ_OPTION_NBNS1;
+                       break;
+               case SECONDARY_DNS_SERVER:
+                       ipcp->req_options &= ~REQ_OPTION_DNS2;
+                       break;
+               case SECONDARY_NBNS_SERVER:
+                       ipcp->req_options &= ~REQ_OPTION_NBNS2;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ipcp_generate_config_options(ipcp);
+       pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len);
+}
+
+static enum rcr_result ipcp_server_rcr(struct ipcp_data *ipcp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ppp_option_iter iter;
+       guint8 nak_options[MAX_CONFIG_OPTION_SIZE];
+       guint16 len = 0;
+       guint8 *rej_options = NULL;
+       guint16 rej_len = 0;
+       guint32 addr;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+               guint8 type = ppp_option_iter_get_type(&iter);
+
+               switch (type) {
+               case IP_ADDRESS:
+                       memcpy(&addr, data, 4);
+
+                       FILL_IP(nak_options,
+                                       addr != ipcp->peer_addr || addr == 0,
+                                       type, &ipcp->peer_addr);
+                       break;
+               case PRIMARY_DNS_SERVER:
+                       memcpy(&addr, data, 4);
+
+                       FILL_IP(nak_options, addr != ipcp->dns1 || addr == 0,
+                                       type, &ipcp->dns1);
+                       break;
+               case SECONDARY_DNS_SERVER:
+                       memcpy(&addr, data, 4);
+
+                       FILL_IP(nak_options, addr != ipcp->dns2 || addr == 0,
+                                       type, &ipcp->dns2);
+                       break;
+               default:
+                       /* Reject */
+                       if (rej_options == NULL) {
+                               guint16 max_len = ntohs(packet->length) - 4;
+                               rej_options = g_new0(guint8, max_len);
+                       }
+
+                       if (rej_options != NULL) {
+                               guint8 opt_len =
+                                       ppp_option_iter_get_length(&iter);
+
+                               rej_options[rej_len] = type;
+                               rej_options[rej_len + 1] = opt_len + 2;
+                               memcpy(rej_options + rej_len + 2,
+                                                               data, opt_len);
+                               rej_len += opt_len + 2;
+                       }
+                       break;
+               }
+       }
+
+       if (rej_len > 0) {
+               *new_len = rej_len;
+               *new_options = rej_options;
+
+               return RCR_REJECT;
+       }
+
+       if (len > 0) {
+               *new_len = len;
+               *new_options = g_memdup(nak_options, len);
+
+               return RCR_NAK;
+       }
+
+       return RCR_ACCEPT;
+}
+
+static enum rcr_result ipcp_client_rcr(struct ipcp_data *ipcp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       guint8 *options = NULL;
+       struct ppp_option_iter iter;
+       guint8 len = 0;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+               guint8 type = ppp_option_iter_get_type(&iter);
+
+               switch (type) {
+               case IP_ADDRESS:
+                       memcpy(&ipcp->peer_addr, data, 4);
+
+                       if (ipcp->peer_addr != 0)
+                               break;
+
+                       /*
+                        * Fall through, reject IP_ADDRESS if peer sends
+                        * us 0 (expecting us to provide its IP address)
+                        */
+               default:
+                       if (options == NULL) {
+                               guint16 max_len = ntohs(packet->length) - 4;
+                               options = g_new0(guint8, max_len);
+                       }
+
+                       if (options != NULL) {
+                               guint8 opt_len =
+                                       ppp_option_iter_get_length(&iter);
+
+                               options[len] = type;
+                               options[len + 1] = opt_len + 2;
+                               memcpy(options + len + 2, data, opt_len);
+                               len += opt_len + 2;
+                       }
+
+                       break;
+               }
+       }
+
+       if (len > 0) {
+               *new_len = len;
+               *new_options = options;
+
+               return RCR_REJECT;
+       }
+
+       return RCR_ACCEPT;
+}
+
+static enum rcr_result ipcp_rcr(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(pppcp);
+
+       if (ipcp->is_server)
+               return ipcp_server_rcr(ipcp, packet, new_options, new_len);
+       else
+               return ipcp_client_rcr(ipcp, packet, new_options, new_len);
+}
+
+struct pppcp_proto ipcp_proto = {
+       .proto                  = IPCP_PROTO,
+       .name                   = "ipcp",
+       .supported_codes        = IPCP_SUPPORTED_CODES,
+       .this_layer_up          = ipcp_up,
+       .this_layer_down        = ipcp_down,
+       .this_layer_finished    = ipcp_finished,
+       .rca                    = ipcp_rca,
+       .rcn_nak                = ipcp_rcn_nak,
+       .rcn_rej                = ipcp_rcn_rej,
+       .rcr                    = ipcp_rcr,
+};
+
+struct pppcp_data *ipcp_new(GAtPPP *ppp, gboolean is_server, guint32 ip)
+{
+       struct ipcp_data *ipcp;
+       struct pppcp_data *pppcp;
+
+       ipcp = g_try_new0(struct ipcp_data, 1);
+       if (ipcp == NULL)
+               return NULL;
+
+       /*
+        * Some 3G modems use repeated IPCP NAKs as the way of stalling
+        * util sending us the client IP address. So we increase the
+        * default number of NAKs we accept before start treating them
+        * as rejects.
+        */
+       pppcp = pppcp_new(ppp, &ipcp_proto, FALSE, MAX_IPCP_FAILURE);
+       if (pppcp == NULL) {
+               g_free(ipcp);
+               return NULL;
+       }
+
+       pppcp_set_data(pppcp, ipcp);
+       ipcp->is_server = is_server;
+
+       if (is_server) {
+               ipcp->local_addr = ip;
+               ipcp_reset_server_config_options(ipcp);
+       } else
+               ipcp_reset_client_config_options(ipcp);
+
+       pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len);
+
+       return pppcp;
+}
+
+void ipcp_free(struct pppcp_data *data)
+{
+       struct ipcp_data *ipcp = pppcp_get_data(data);
+
+       g_free(ipcp);
+       pppcp_free(data);
+}
diff --git a/gatchat/ppp_ipv6cp.c b/gatchat/ppp_ipv6cp.c
new file mode 100644 (file)
index 0000000..ecfd570
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gatppp.h"
+#include "ppp.h"
+
+#define IPV6CP_SUPPORTED_CODES ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CODE_REJECT))
+
+#define OPTION_COPY(_options, _len, _req, _type, _var, _opt_len)       \
+       if (_req) {                                                     \
+               _options[_len] = _type;                                 \
+               _options[_len + 1] = _opt_len + 2;                      \
+               memcpy(_options + _len + 2, _var, _opt_len);            \
+               _len += _opt_len + 2;                                   \
+       }
+
+/* We request only IPv6 Interface Id */
+#define IPV6CP_MAX_CONFIG_OPTION_SIZE  10
+#define IPV6CP_MAX_FAILURE             3
+#define IPV6CP_ERROR ipv6cp_error_quark()
+
+enum ipv6cp_option_types {
+       IPV6CP_INTERFACE_ID = 1,
+};
+
+struct ipv6cp_data {
+       guint8 options[IPV6CP_MAX_CONFIG_OPTION_SIZE];
+       guint16 options_len;
+       guint8 req_options;
+       guint64 local_addr;
+       guint64 peer_addr;
+       gboolean is_server;
+};
+
+static GQuark ipv6cp_error_quark(void)
+{
+       return g_quark_from_static_string("ipv6cp");
+}
+
+static void ipv6cp_generate_config_options(struct ipv6cp_data *ipv6cp)
+{
+       guint16 len = 0;
+
+       OPTION_COPY(ipv6cp->options, len,
+                       ipv6cp->req_options & IPV6CP_INTERFACE_ID,
+                       IPV6CP_INTERFACE_ID, &ipv6cp->local_addr,
+                       sizeof(ipv6cp->local_addr));
+
+       ipv6cp->options_len = len;
+}
+
+static void ipv6cp_reset_config_options(struct ipv6cp_data *ipv6cp)
+{
+       ipv6cp->req_options = IPV6CP_INTERFACE_ID;
+
+       ipv6cp_generate_config_options(ipv6cp);
+}
+
+static void ipv6cp_up(struct pppcp_data *pppcp)
+{
+
+}
+
+static void ipv6cp_down(struct pppcp_data *pppcp)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+
+       ipv6cp_reset_config_options(ipv6cp);
+
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+}
+
+static void ipv6cp_finished(struct pppcp_data *pppcp)
+{
+
+}
+
+static enum rcr_result ipv6cp_server_rcr(struct ipv6cp_data *ipv6cp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ppp_option_iter iter;
+       guint8 nak_options[IPV6CP_MAX_CONFIG_OPTION_SIZE];
+       guint16 len = 0;
+       guint8 *rej_options = NULL;
+       guint16 rej_len = 0;
+       guint64 addr;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               guint8 type = ppp_option_iter_get_type(&iter);
+               const void *data = ppp_option_iter_get_data(&iter);
+
+               switch (type) {
+               case IPV6CP_INTERFACE_ID:
+                       memcpy(&addr, data, sizeof(addr));
+
+                       OPTION_COPY(nak_options, len,
+                                       addr != ipv6cp->peer_addr || addr == 0,
+                                       type, &ipv6cp->peer_addr,
+                                       ppp_option_iter_get_length(&iter));
+                       break;
+               default:
+                       if (rej_options == NULL) {
+                               guint16 max_len = ntohs(packet->length) - 4;
+                               rej_options = g_new0(guint8, max_len);
+                       }
+
+                       OPTION_COPY(rej_options, rej_len, rej_options != NULL,
+                                       type, data,
+                                       ppp_option_iter_get_length(&iter));
+                       break;
+               }
+       }
+
+       if (rej_len > 0) {
+               *new_len = rej_len;
+               *new_options = rej_options;
+
+               return RCR_REJECT;
+       }
+
+       if (len > 0) {
+               *new_len = len;
+               *new_options = g_memdup(nak_options, len);
+
+               return RCR_NAK;
+       }
+
+       return RCR_ACCEPT;
+}
+
+static enum rcr_result ipv6cp_client_rcr(struct ipv6cp_data *ipv6cp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ppp_option_iter iter;
+       guint8 *options = NULL;
+       guint8 len = 0;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               guint8 type = ppp_option_iter_get_type(&iter);
+               const void *data = ppp_option_iter_get_data(&iter);
+
+               switch (type) {
+               case IPV6CP_INTERFACE_ID:
+                       memcpy(&ipv6cp->peer_addr, data,
+                                       sizeof(ipv6cp->peer_addr));
+
+                       if (ipv6cp->peer_addr != 0)
+                               break;
+                       /*
+                        * Fall through, reject zero Interface ID
+                        */
+               default:
+                       if (options == NULL) {
+                               guint16 max_len = ntohs(packet->length) - 4;
+                               options = g_new0(guint8, max_len);
+                       }
+
+                       OPTION_COPY(options, len, options != NULL,
+                                       type, data,
+                                       ppp_option_iter_get_length(&iter));
+                       break;
+               }
+       }
+
+       if (len > 0) {
+               *new_len = len;
+               *new_options = options;
+
+               return RCR_REJECT;
+       }
+
+       return RCR_ACCEPT;
+}
+
+static enum rcr_result ipv6cp_rcr(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+
+       if (ipv6cp->is_server)
+               return ipv6cp_server_rcr(ipv6cp, packet, new_options, new_len);
+       else
+               return ipv6cp_client_rcr(ipv6cp, packet, new_options, new_len);
+}
+
+static void ipv6cp_rca(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       if (ipv6cp->is_server)
+               return;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IPV6CP_INTERFACE_ID:
+                       memcpy(&ipv6cp->local_addr, data,
+                                       sizeof(ipv6cp->local_addr));
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void ipv6cp_rcn_nak(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       if (ipv6cp->is_server)
+               return;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IPV6CP_INTERFACE_ID:
+                       ipv6cp->req_options |= IPV6CP_INTERFACE_ID;
+                       memcpy(&ipv6cp->local_addr, data,
+                               sizeof(ipv6cp->local_addr));
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ipv6cp_generate_config_options(ipv6cp);
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+}
+
+static void ipv6cp_rcn_rej(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               switch (ppp_option_iter_get_type(&iter)) {
+               case IPV6CP_INTERFACE_ID:
+                       ipv6cp->req_options &= ~IPV6CP_INTERFACE_ID;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ipv6cp_generate_config_options(ipv6cp);
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+}
+
+struct pppcp_proto ipv6cp_proto = {
+       .proto                  = IPV6CP_PROTO,
+       .name                   = "ipv6cp",
+       .supported_codes        = IPV6CP_SUPPORTED_CODES,
+       .this_layer_up          = ipv6cp_up,
+       .this_layer_down        = ipv6cp_down,
+       .this_layer_finished    = ipv6cp_finished,
+       .rca                    = ipv6cp_rca,
+       .rcn_nak                = ipv6cp_rcn_nak,
+       .rcn_rej                = ipv6cp_rcn_rej,
+       .rcr                    = ipv6cp_rcr,
+};
+
+struct pppcp_data *ipv6cp_new(GAtPPP *ppp, gboolean is_server,
+                                       const char *local, const char *peer,
+                                       GError **error)
+{
+       struct ipv6cp_data *ipv6cp;
+       struct pppcp_data *pppcp;
+       struct in6_addr local_addr;
+       struct in6_addr peer_addr;
+
+       if (local == NULL)
+               memset(&local_addr, 0, sizeof(local_addr));
+       else if (inet_pton(AF_INET6, local, &local_addr) != 1) {
+               g_set_error(error, IPV6CP_ERROR, errno,
+                               "Unable to set local Interface ID: %s",
+                               strerror(errno));
+               return NULL;
+       }
+
+       if (peer == NULL)
+               memset(&peer_addr, 0, sizeof(peer_addr));
+       else if (inet_pton(AF_INET6, peer, &peer_addr) != 1) {
+               g_set_error(error, IPV6CP_ERROR, errno,
+                               "Unable to set peer Interface ID: %s",
+                               g_strerror(errno));
+               return NULL;
+       }
+
+       ipv6cp = g_try_new0(struct ipv6cp_data, 1);
+       if (ipv6cp == NULL)
+               return NULL;
+
+       pppcp = pppcp_new(ppp, &ipv6cp_proto, FALSE, IPV6CP_MAX_FAILURE);
+       if (pppcp == NULL) {
+               g_free(ipv6cp);
+               return NULL;
+       }
+
+       memcpy(&ipv6cp->local_addr, &local_addr.s6_addr[8],
+                                               sizeof(ipv6cp->local_addr));
+       memcpy(&ipv6cp->peer_addr, &peer_addr.s6_addr[8],
+                                               sizeof(ipv6cp->peer_addr));
+       ipv6cp->is_server = is_server;
+
+       pppcp_set_data(pppcp, ipv6cp);
+
+       ipv6cp_reset_config_options(ipv6cp);
+
+       pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len);
+
+       return pppcp;
+}
+
+void ipv6cp_free(struct pppcp_data *data)
+{
+       struct ipv6cp_data *ipv6cp = pppcp_get_data(data);
+
+       g_free(ipv6cp);
+       pppcp_free(data);
+}
diff --git a/gatchat/ppp_lcp.c b/gatchat/ppp_lcp.c
new file mode 100644 (file)
index 0000000..4f420f1
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <glib.h>
+#include <arpa/inet.h>
+
+#include "gatppp.h"
+#include "ppp.h"
+
+#define LCP_SUPPORTED_CODES    ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \
+                               (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \
+                               (1 << PPPCP_CODE_TYPE_CODE_REJECT) | \
+                               (1 << PPPCP_CODE_TYPE_PROTOCOL_REJECT) | \
+                               (1 << PPPCP_CODE_TYPE_ECHO_REQUEST) | \
+                               (1 << PPPCP_CODE_TYPE_ECHO_REPLY) | \
+                               (1 << PPPCP_CODE_TYPE_DISCARD_REQUEST))
+
+enum lcp_options {
+       RESERVED                = 0,
+       MRU                     = 1,
+       ACCM                    = 2,
+       AUTH_PROTO              = 3,
+       QUAL_PROTO              = 4,
+       MAGIC_NUMBER            = 5,
+       DEPRECATED_QUAL_PROTO   = 6,
+       PFC                     = 7,
+       ACFC                    = 8,
+};
+
+/* Maximum size of all options, we only ever request ACCM, MRU, ACFC and PFC */
+#define MAX_CONFIG_OPTION_SIZE 14
+
+#define REQ_OPTION_ACCM        0x1
+#define REQ_OPTION_MRU 0x2
+#define REQ_OPTION_ACFC        0x4
+#define REQ_OPTION_PFC 0x8
+
+struct lcp_data {
+       guint8 options[MAX_CONFIG_OPTION_SIZE];
+       guint16 options_len;
+       guint8 req_options;
+       guint32 accm;                   /* ACCM value */
+       guint16 mru;
+};
+
+static void lcp_generate_config_options(struct lcp_data *lcp)
+{
+       guint16 len = 0;
+
+       if (lcp->req_options & REQ_OPTION_ACCM) {
+               guint32 accm;
+
+               accm = htonl(lcp->accm);
+
+               lcp->options[len] = ACCM;
+               lcp->options[len + 1] = 6;
+               memcpy(lcp->options + len + 2, &accm, sizeof(accm));
+
+               len += 6;
+       }
+
+       if (lcp->req_options & REQ_OPTION_MRU) {
+               guint16 mru;
+
+               mru = htons(lcp->mru);
+
+               lcp->options[len] = MRU;
+               lcp->options[len + 1] = 4;
+               memcpy(lcp->options + len + 2, &mru, sizeof(mru));
+
+               len += 4;
+       }
+
+       if (lcp->req_options & REQ_OPTION_ACFC) {
+               lcp->options[len] = ACFC;
+               lcp->options[len + 1] = 2;
+
+               len += 2;
+       }
+
+       if (lcp->req_options & REQ_OPTION_PFC) {
+               lcp->options[len] = PFC;
+               lcp->options[len + 1] = 2;
+
+               len += 2;
+       }
+
+       lcp->options_len = len;
+}
+
+static void lcp_reset_config_options(struct lcp_data *lcp)
+{
+       /* Using the default ACCM */
+
+       lcp_generate_config_options(lcp);
+}
+
+/*
+ * signal the Up event to the NCP
+ */
+static void lcp_up(struct pppcp_data *pppcp)
+{
+       ppp_lcp_up_notify(pppcp_get_ppp(pppcp));
+}
+
+/*
+ * signal the Down event to the NCP
+ */
+static void lcp_down(struct pppcp_data *pppcp)
+{
+       struct lcp_data *lcp = pppcp_get_data(pppcp);
+
+       lcp_reset_config_options(lcp);
+       pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
+       ppp_lcp_down_notify(pppcp_get_ppp(pppcp));
+}
+
+/*
+ * Indicate that the lower layer is not needed
+ * Should trigger Down event
+ */
+static void lcp_finished(struct pppcp_data *pppcp)
+{
+       ppp_lcp_finished_notify(pppcp_get_ppp(pppcp));
+}
+
+static void lcp_rca(struct pppcp_data *pppcp, const struct pppcp_packet *packet)
+{
+       struct ppp_option_iter iter;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+               switch (ppp_option_iter_get_type(&iter)) {
+               case ACCM:
+                       /*
+                        * RFC1662 Section 7.1
+                        * The Configuration Option is used to inform the peer
+                        * which control characters MUST remain mapped when
+                        * the peer sends them.
+                        */
+
+                       ppp_set_recv_accm(pppcp_get_ppp(pppcp),
+                                       get_host_long(data));
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void lcp_rcn_nak(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+       struct lcp_data *lcp = pppcp_get_data(pppcp);
+       struct ppp_option_iter iter;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               const guint8 *data = ppp_option_iter_get_data(&iter);
+
+               switch (ppp_option_iter_get_type(&iter)) {
+               case MRU:
+               {
+                       guint16 mru = get_host_short(data);
+
+                       if (mru < 2048) {
+                               lcp->mru = get_host_short(data);
+                               lcp->req_options |= REQ_OPTION_MRU;
+                       }
+
+                       break;
+               }
+               default:
+                       break;
+               }
+       }
+
+       lcp_generate_config_options(lcp);
+       pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
+}
+
+static void lcp_rcn_rej(struct pppcp_data *pppcp,
+                               const struct pppcp_packet *packet)
+{
+
+}
+
+static enum rcr_result lcp_rcr(struct pppcp_data *pppcp,
+                                       const struct pppcp_packet *packet,
+                                       guint8 **new_options, guint16 *new_len)
+{
+       GAtPPP *ppp = pppcp_get_ppp(pppcp);
+       struct ppp_option_iter iter;
+
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               switch (ppp_option_iter_get_type(&iter)) {
+               case AUTH_PROTO:
+               {
+                       const guint8 *option_data =
+                               ppp_option_iter_get_data(&iter);
+                       guint16 proto = get_host_short(option_data);
+                       guint8 method = option_data[2];
+                       guint8 *option;
+
+                       if ((proto == CHAP_PROTOCOL) && (method == MD5))
+                               break;
+
+                       /*
+                        * try to suggest CHAP & MD5.  If we are out
+                        * of memory, just reject.
+                        */
+
+                       option = g_try_malloc0(5);
+                       if (option == NULL)
+                               return RCR_REJECT;
+
+                       option[0] = AUTH_PROTO;
+                       option[1] = 5;
+                       put_network_short(&option[2], CHAP_PROTOCOL);
+                       option[4] = MD5;
+                       *new_options = option;
+                       *new_len = 5;
+                       return RCR_NAK;
+               }
+               case ACCM:
+               case PFC:
+               case ACFC:
+               case MRU:
+                       break;
+
+               case MAGIC_NUMBER:
+               {
+                       guint32 magic =
+                               get_host_long(ppp_option_iter_get_data(&iter));
+
+                       if (magic == 0)
+                               return RCR_REJECT;
+
+                       break;
+               }
+               default:
+                       return RCR_REJECT;
+               }
+       }
+
+       /* All options were found acceptable, apply them here and return */
+       ppp_option_iter_init(&iter, packet);
+
+       while (ppp_option_iter_next(&iter) == TRUE) {
+               switch (ppp_option_iter_get_type(&iter)) {
+               case ACCM:
+                       /*
+                        * RFC1662 Section 7.1
+                        * The Configuration Option is used to inform the peer
+                        * which control characters MUST remain mapped when
+                        * the peer sends them.
+                        */
+                       ppp_set_xmit_accm(ppp,
+                               get_host_long(ppp_option_iter_get_data(&iter)));
+                       break;
+               case AUTH_PROTO:
+                       ppp_set_auth(ppp, ppp_option_iter_get_data(&iter));
+                       break;
+               case MRU:
+                       ppp_set_mtu(ppp, ppp_option_iter_get_data(&iter));
+                       break;
+               case MAGIC_NUMBER:
+                       /* don't care */
+                       break;
+               case PFC:
+               {
+                       struct lcp_data *lcp = pppcp_get_data(pppcp);
+
+                       if (lcp->req_options & REQ_OPTION_PFC)
+                               ppp_set_xmit_pfc(ppp, TRUE);
+
+                       break;
+               }
+               case ACFC:
+               {
+                       struct lcp_data *lcp = pppcp_get_data(pppcp);
+
+                       if (lcp->req_options & REQ_OPTION_ACFC)
+                               ppp_set_xmit_acfc(ppp, TRUE);
+
+                       break;
+               }
+               }
+       }
+
+       return RCR_ACCEPT;
+}
+
+struct pppcp_proto lcp_proto = {
+       .proto                  = LCP_PROTOCOL,
+       .name                   = "lcp",
+       .supported_codes        = LCP_SUPPORTED_CODES,
+       .this_layer_up          = lcp_up,
+       .this_layer_down        = lcp_down,
+       .this_layer_finished    = lcp_finished,
+       .rca                    = lcp_rca,
+       .rcn_nak                = lcp_rcn_nak,
+       .rcn_rej                = lcp_rcn_rej,
+       .rcr                    = lcp_rcr,
+};
+
+void lcp_free(struct pppcp_data *pppcp)
+{
+       struct lcp_data *lcp = pppcp_get_data(pppcp);
+
+       g_free(lcp);
+       pppcp_free(pppcp);
+}
+
+struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean is_server)
+{
+       struct pppcp_data *pppcp;
+       struct lcp_data *lcp;
+
+       lcp = g_try_new0(struct lcp_data, 1);
+       if (lcp == NULL)
+               return NULL;
+
+       pppcp = pppcp_new(ppp, &lcp_proto, is_server, 0);
+       if (pppcp == NULL) {
+               g_free(lcp);
+               return NULL;
+       }
+
+       pppcp_set_data(pppcp, lcp);
+
+       lcp_reset_config_options(lcp);
+       pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
+
+       return pppcp;
+}
+
+void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled)
+{
+       struct lcp_data *lcp = pppcp_get_data(pppcp);
+       guint8 old = lcp->req_options;
+
+       if (enabled == TRUE)
+               lcp->req_options |= REQ_OPTION_ACFC;
+       else
+               lcp->req_options &= ~REQ_OPTION_ACFC;
+
+       if (lcp->req_options == old)
+               return;
+
+       lcp_generate_config_options(lcp);
+       pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
+}
+
+void lcp_set_pfc_enabled(struct pppcp_data *pppcp, gboolean enabled)
+{
+       struct lcp_data *lcp = pppcp_get_data(pppcp);
+       guint8 old = lcp->req_options;
+
+       if (enabled == TRUE)
+               lcp->req_options |= REQ_OPTION_PFC;
+       else
+               lcp->req_options &= ~REQ_OPTION_PFC;
+
+       if (lcp->req_options == old)
+               return;
+
+       lcp_generate_config_options(lcp);
+       pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
+}
diff --git a/gatchat/ppp_net.c b/gatchat/ppp_net.c
new file mode 100644 (file)
index 0000000..1609b99
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ *
+ *  PPP library with GLib integration
+ *
+ *  Copyright (C) 2009-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include "gatutil.h"
+#include "gatppp.h"
+#include "ppp.h"
+
+#define MAX_PACKET 1500
+
+struct ppp_net {
+       GAtPPP *ppp;
+       char *if_name;
+       GIOChannel *channel;
+       guint watch;
+       gint mtu;
+       struct ppp_header *ppp_packet;
+};
+
+gboolean ppp_net_set_mtu(struct ppp_net *net, guint16 mtu)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (net == NULL || mtu > MAX_PACKET)
+               return FALSE;
+
+       net->mtu = mtu;
+
+       sk = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return FALSE;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, net->if_name, sizeof(ifr.ifr_name));
+       ifr.ifr_mtu = mtu;
+
+       err = ioctl(sk, SIOCSIFMTU, (caddr_t) &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+void ppp_net_process_packet(struct ppp_net *net, const guint8 *packet,
+                               gsize plen)
+{
+       GIOStatus status;
+       gsize bytes_written;
+       guint16 len;
+
+       if (plen < 4)
+               return;
+
+       /* find the length of the packet to transmit */
+       len = get_host_short(&packet[2]);
+       status = g_io_channel_write_chars(net->channel, (gchar *) packet,
+                                               MIN(len, plen),
+                                               &bytes_written, NULL);
+
+       if (status != G_IO_STATUS_NORMAL)
+               return;
+}
+
+/*
+ * packets received by the tun interface need to be written to
+ * the modem.  So, just read a packet, write out to the modem
+ */
+static gboolean ppp_net_callback(GIOChannel *channel, GIOCondition cond,
+                               gpointer userdata)
+{
+       struct ppp_net *net = (struct ppp_net *) userdata;
+       GIOStatus status;
+       gsize bytes_read;
+       gchar *buf = (gchar *) net->ppp_packet->info;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+               return FALSE;
+
+       if (cond & G_IO_IN) {
+               /* leave space to add PPP protocol field */
+               status = g_io_channel_read_chars(channel, buf, net->mtu,
+                                                       &bytes_read, NULL);
+               if (bytes_read > 0)
+                       ppp_transmit(net->ppp, (guint8 *) net->ppp_packet,
+                                       bytes_read);
+
+               if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+const char *ppp_net_get_interface(struct ppp_net *net)
+{
+       return net->if_name;
+}
+
+struct ppp_net *ppp_net_new(GAtPPP *ppp, int fd)
+{
+       struct ppp_net *net;
+       GIOChannel *channel = NULL;
+       struct ifreq ifr;
+       int err;
+
+       net = g_try_new0(struct ppp_net, 1);
+       if (net == NULL)
+               goto badalloc;
+
+       net->ppp_packet = ppp_packet_new(MAX_PACKET, PPP_IP_PROTO);
+       if (net->ppp_packet == NULL)
+               goto error;
+
+       /*
+        * If the fd value is still the default one,
+        * open the tun interface and configure it.
+        */
+       memset(&ifr, 0, sizeof(ifr));
+
+       if (fd < 0) {
+               /* open a tun interface */
+               fd = open("/dev/net/tun", O_RDWR);
+               if (fd < 0)
+                       goto error;
+
+               ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+               strcpy(ifr.ifr_name, "ppp%d");
+
+               err = ioctl(fd, TUNSETIFF, (void *) &ifr);
+               if (err < 0)
+                       goto error;
+       } else {
+               err = ioctl(fd, TUNGETIFF, (void *) &ifr);
+               if (err < 0)
+                       goto error;
+       }
+
+       net->if_name = strdup(ifr.ifr_name);
+
+       /* create a channel for reading and writing to this interface */
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL)
+               goto error;
+
+       if (!g_at_util_setup_io(channel, 0))
+               goto error;
+
+       g_io_channel_set_buffered(channel, FALSE);
+
+       net->channel = channel;
+       net->watch = g_io_add_watch(channel,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       ppp_net_callback, net);
+       net->ppp = ppp;
+
+       net->mtu = MAX_PACKET;
+       return net;
+
+error:
+       if (channel)
+               g_io_channel_unref(channel);
+
+       g_free(net->if_name);
+       g_free(net->ppp_packet);
+       g_free(net);
+
+badalloc:
+       if (fd >= 0)
+               close(fd);
+
+       return NULL;
+}
+
+void ppp_net_free(struct ppp_net *net)
+{
+       if (net->watch) {
+               g_source_remove(net->watch);
+               net->watch = 0;
+       }
+
+       g_io_channel_unref(net->channel);
+
+       g_free(net->ppp_packet);
+       g_free(net->if_name);
+       g_free(net);
+}
+
+void ppp_net_suspend_interface(struct ppp_net *net)
+{
+       if (net == NULL || net->channel == NULL)
+               return;
+
+       if (net->watch == 0)
+               return;
+
+       g_source_remove(net->watch);
+       net->watch = 0;
+}
+
+void ppp_net_resume_interface(struct ppp_net *net)
+{
+       if (net == NULL || net->channel == NULL)
+               return;
+
+       net->watch = g_io_add_watch(net->channel,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       ppp_net_callback, net);
+}
diff --git a/gatchat/ringbuffer.c b/gatchat/ringbuffer.c
new file mode 100644 (file)
index 0000000..c2d4a00
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+
+#define MAX_SIZE 262144
+
+struct ring_buffer {
+       unsigned char *buffer;
+       unsigned int size;
+       unsigned int mask;
+       unsigned int in;
+       unsigned int out;
+};
+
+struct ring_buffer *ring_buffer_new(unsigned int size)
+{
+       unsigned int real_size = 1;
+       struct ring_buffer *buffer;
+
+       /* Find the next power of two for size */
+       while (real_size < size && real_size < MAX_SIZE)
+               real_size = real_size << 1;
+
+       if (real_size > MAX_SIZE)
+               return NULL;
+
+       buffer = g_slice_new(struct ring_buffer);
+       if (buffer == NULL)
+               return NULL;
+
+       buffer->buffer = g_slice_alloc(real_size);
+       if (buffer->buffer == NULL) {
+               g_free(buffer);
+               return NULL;
+       }
+
+       buffer->size = real_size;
+       buffer->mask = real_size - 1;
+       buffer->in = 0;
+       buffer->out = 0;
+
+       return buffer;
+}
+
+int ring_buffer_write(struct ring_buffer *buf, const void *data,
+                       unsigned int len)
+{
+       unsigned int end;
+       unsigned int offset;
+       const unsigned char *d = data; /* Needed to satisfy non-gcc compilers */
+
+       /* Determine how much we can actually write */
+       len = MIN(len, buf->size - buf->in + buf->out);
+
+       /* Determine how much to write before wrapping */
+       offset = buf->in & buf->mask;
+       end = MIN(len, buf->size - offset);
+       memcpy(buf->buffer+offset, d, end);
+
+       /* Now put the remainder on the beginning of the buffer */
+       memcpy(buf->buffer, d + end, len - end);
+
+       buf->in += len;
+
+       return len;
+}
+
+unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf,
+                                       unsigned int offset)
+{
+       return buf->buffer + ((buf->in + offset) & buf->mask);
+}
+
+int ring_buffer_avail_no_wrap(struct ring_buffer *buf)
+{
+       unsigned int offset = buf->in & buf->mask;
+       unsigned int len = buf->size - buf->in + buf->out;
+
+       return MIN(len, buf->size - offset);
+}
+
+int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len)
+{
+       len = MIN(len, buf->size - buf->in + buf->out);
+       buf->in += len;
+
+       return len;
+}
+
+int ring_buffer_read(struct ring_buffer *buf, void *data, unsigned int len)
+{
+       unsigned int end;
+       unsigned int offset;
+       unsigned char *d = data;
+
+       len = MIN(len, buf->in - buf->out);
+
+       /* Grab data from buffer starting at offset until the end */
+       offset = buf->out & buf->mask;
+       end = MIN(len, buf->size - offset);
+       memcpy(d, buf->buffer + offset, end);
+
+       /* Now grab remainder from the beginning */
+       memcpy(d + end, buf->buffer, len - end);
+
+       buf->out += len;
+
+       if (buf->out == buf->in)
+               buf->out = buf->in = 0;
+
+       return len;
+}
+
+int ring_buffer_drain(struct ring_buffer *buf, unsigned int len)
+{
+       len = MIN(len, buf->in - buf->out);
+
+       buf->out += len;
+
+       if (buf->out == buf->in)
+               buf->out = buf->in = 0;
+
+       return len;
+}
+
+int ring_buffer_len_no_wrap(struct ring_buffer *buf)
+{
+       unsigned int offset = buf->out & buf->mask;
+       unsigned int len = buf->in - buf->out;
+
+       return MIN(len, buf->size - offset);
+}
+
+unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf,
+                                       unsigned int offset)
+{
+       return buf->buffer + ((buf->out + offset) & buf->mask);
+}
+
+int ring_buffer_len(struct ring_buffer *buf)
+{
+       if (buf == NULL)
+               return -1;
+
+       return buf->in - buf->out;
+}
+
+void ring_buffer_reset(struct ring_buffer *buf)
+{
+       if (buf == NULL)
+               return;
+
+       buf->in = 0;
+       buf->out = 0;
+}
+
+int ring_buffer_avail(struct ring_buffer *buf)
+{
+       if (buf == NULL)
+               return -1;
+
+       return buf->size - buf->in + buf->out;
+}
+
+int ring_buffer_capacity(struct ring_buffer *buf)
+{
+       if (buf == NULL)
+               return -1;
+
+       return buf->size;
+}
+
+void ring_buffer_free(struct ring_buffer *buf)
+{
+       if (buf == NULL)
+               return;
+
+       g_slice_free1(buf->size, buf->buffer);
+       g_slice_free1(sizeof(struct ring_buffer), buf);
+}
diff --git a/gatchat/ringbuffer.h b/gatchat/ringbuffer.h
new file mode 100644 (file)
index 0000000..369960c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct ring_buffer;
+
+/*!
+ * Creates a new ring buffer with capacity size
+ */
+struct ring_buffer *ring_buffer_new(unsigned int size);
+
+/*!
+ * Frees the resources allocated for the ring buffer
+ */
+void ring_buffer_free(struct ring_buffer *buf);
+
+/*!
+ * Returns the capacity of the ring buffer
+ */
+int ring_buffer_capacity(struct ring_buffer *buf);
+
+/*!
+ * Resets the ring buffer, all data inside the buffer is lost
+ */
+void ring_buffer_reset(struct ring_buffer *buf);
+
+/*!
+ * Writes data of size len into the ring buffer buf.  Returns -1 if the
+ * write failed or the number of bytes written
+ */
+int ring_buffer_write(struct ring_buffer *buf, const void *data,
+                       unsigned int len);
+
+/*!
+ * Advances the write counter by len, this is meant to be used with
+ * the ring_buffer_write_ptr function.  Returns the number of bytes
+ * actually advanced (the capacity of the buffer)
+ */
+int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len);
+
+/*!
+ * Returns the write pointer with write offset specified by offset.  Careful
+ * not to write past the end of the buffer.  Use the ring_buffer_avail_no_wrap
+ * function, and ring_buffer_write_advance.
+ */
+unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf,
+                                       unsigned int offset);
+
+/*!
+ * Returns the number of free bytes available in the buffer
+ */
+int ring_buffer_avail(struct ring_buffer *buf);
+
+/*!
+ * Returns the number of free bytes available in the buffer without wrapping
+ */
+int ring_buffer_avail_no_wrap(struct ring_buffer *buf);
+
+/*!
+ * Reads data from the ring buffer buf into memory region pointed to by data.
+ * A maximum of len bytes will be read.  Returns -1 if the read failed or
+ * the number of bytes read
+ */
+int ring_buffer_read(struct ring_buffer *buf, void *data,
+                       unsigned int len);
+
+/*!
+ * Returns the read pointer with read offset specified by offset.  No bounds
+ * checking is performed.  Be careful not to read past the end of the buffer.
+ * Use the ring_buffer_len_no_wrap function, and ring_buffer_drain.
+ */
+unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf,
+                                       unsigned int offset);
+
+/*!
+ * Returns the number of bytes currently available to be read in the buffer
+ */
+int ring_buffer_len(struct ring_buffer *buf);
+
+/*!
+ * Returns the number of bytes currently available to be read in the buffer
+ * without wrapping.
+ */
+int ring_buffer_len_no_wrap(struct ring_buffer *buf);
+
+/*!
+ * Drains the ring buffer of len bytes.  Returns the number of bytes the
+ * read counter was actually advanced.
+ */
+int ring_buffer_drain(struct ring_buffer *buf, unsigned int len);
diff --git a/gatchat/test-qcdm.c b/gatchat/test-qcdm.c
new file mode 100644 (file)
index 0000000..37f4793
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "gattty.h"
+#include "gathdlc.h"
+
+static gboolean option_debug = FALSE;
+static gchar *option_device = NULL;
+
+static GMainLoop *event_loop;
+
+struct version_info {
+       char comp_date[11];
+       char comp_time[8];
+       char rel_date[11];
+       char rel_time[8];
+       char model[8];
+       guint8 scm;
+       guint8 mob_cai_rev;
+       guint8 mob_model;
+       guint16 mob_firmware_rev;
+       guint8 slot_cycle_index;
+       guint8 msm_ver;
+       guint8 unknown;
+} __attribute__ ((packed));
+
+static void parse_qcdm(const unsigned char *buf, gsize len)
+{
+       struct version_info *verinfo;
+       char str[12];
+       guint8 cmd = buf[0];
+
+       switch (cmd) {
+       case 0x00:
+               g_print("==> Version information\n");
+               verinfo = (struct version_info *) (buf + 1);
+               snprintf(str, 12, "%s", verinfo->comp_date);
+               g_print("Compiled Date: %s\n", str);
+               snprintf(str, 9, "%s", verinfo->comp_time);
+               g_print("Compiled Time: %s\n", str);
+               snprintf(str, 12, "%s", verinfo->rel_date);
+               g_print("Release Date: %s\n", str);
+               snprintf(str, 9, "%s", verinfo->rel_time);
+               g_print("Release Time: %s\n", str);
+               snprintf(str, 9, "%s", verinfo->model);
+               g_print("Model: %s\n", str);
+               g_print("MSM version: %d\n", verinfo->msm_ver);
+               break;
+       case 0x13:
+               g_print("==> Invalid command response\n");
+               break;
+       case 0x4b:
+               g_print("==> Subsystem response\n");
+               break;
+       case 0x51:
+               g_print("==> Features response\n");
+               break;
+       default:
+               g_print("==> Unknown command 0x%02x\n", cmd);
+               break;
+       }
+}
+
+static void hdlc_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (const char *) data, str);
+}
+
+static void hdlc_receive(const unsigned char *buf, gsize len, void *data)
+{
+       parse_qcdm(buf, len);
+}
+
+static void send_command(GAtHDLC *hdlc, guint8 cmd)
+{
+       unsigned char cmdbuf[1];
+
+       cmdbuf[0] = cmd;
+
+       g_at_hdlc_send(hdlc, cmdbuf, sizeof(cmdbuf));
+}
+
+static void send_subsys_command(GAtHDLC *hdlc, guint8 id, guint16 cmd)
+{
+       unsigned char cmdbuf[4];
+
+       cmdbuf[0] = 0x4b;
+       cmdbuf[1] = id;
+       cmdbuf[2] = cmd & 0xff;
+       cmdbuf[3] = cmd >> 8;
+
+       g_at_hdlc_send(hdlc, cmdbuf, sizeof(cmdbuf));
+}
+
+static GOptionEntry options[] = {
+       { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+                                               "Enable debugging" },
+       { "device", 'n', 0, G_OPTION_ARG_STRING, &option_device,
+                                               "Specify device" },
+       { NULL },
+};
+
+int main(int argc, char **argv)
+{
+       GOptionContext *context;
+       GError *err = NULL;
+       GIOChannel *channel;
+       GAtHDLC *hdlc;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+               if (err != NULL) {
+                       g_printerr("%s\n", err->message);
+                       g_error_free(err);
+                       return 1;
+               }
+
+               g_printerr("An unknown error occurred\n");
+               return 1;
+       }
+
+       g_option_context_free(context);
+
+       if (option_device == NULL)
+               option_device = g_strdup("/dev/ttyUSB1");
+
+       g_print("Device: %s\n", option_device);
+
+       channel = g_at_tty_open_qcdm(option_device);
+       if (channel == NULL) {
+               g_printerr("Failed to open QCDM device\n");
+               return 1;
+       }
+
+       event_loop = g_main_loop_new(NULL, FALSE);
+
+       hdlc = g_at_hdlc_new(channel);
+
+       g_io_channel_unref(channel);
+
+       if (hdlc == NULL)
+               return 1;
+
+       if (option_debug == TRUE)
+               g_at_hdlc_set_debug(hdlc, hdlc_debug, "HDLC");
+
+       g_at_hdlc_set_xmit_accm(hdlc, 0);
+       g_at_hdlc_set_recv_accm(hdlc, 0);
+
+       g_at_hdlc_set_receive(hdlc, hdlc_receive, NULL);
+
+       send_command(hdlc, 0x00);       /* Version info */
+       send_command(hdlc, 0x51);       /* Features query */
+
+       send_subsys_command(hdlc, 250, 7);      /* Novatel modem status */
+
+       g_main_loop_run(event_loop);
+
+       g_at_hdlc_unref(hdlc);
+
+       g_main_loop_unref(event_loop);
+
+       g_free(option_device);
+
+       return 0;
+}
diff --git a/gatchat/test-server.c b/gatchat/test-server.c
new file mode 100644 (file)
index 0000000..6f1d06d
--- /dev/null
@@ -0,0 +1,1160 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <utmp.h>
+#include <pty.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "gatserver.h"
+#include "gatppp.h"
+#include "ringbuffer.h"
+
+#define DEFAULT_TCP_PORT 12346
+#define DEFAULT_SOCK_PATH "./server_sock"
+#define IFCONFIG_PATH "/sbin/ifconfig"
+
+static gboolean data_mode = FALSE;
+static int modem_mode = 0;
+static int modem_creg = 0;
+static int modem_cgreg = 0;
+static int network_status = 4;
+static int network_attach = 0;
+
+struct sock_server{
+       int server_sock;
+};
+
+static GMainLoop *mainloop;
+static GAtServer *server;
+static GAtPPP *ppp;
+unsigned int server_watch;
+
+static gboolean server_cleanup(void)
+{
+       if (server_watch)
+               g_source_remove(server_watch);
+
+       if (ppp) {
+               g_at_ppp_unref(ppp);
+               ppp = NULL;
+       }
+
+       g_at_server_unref(server);
+       server = NULL;
+
+       unlink(DEFAULT_SOCK_PATH);
+
+       g_main_loop_quit(mainloop);
+
+       return FALSE;
+}
+
+static void server_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (char *) data, str);
+}
+
+static gboolean execute(const char *cmd)
+{
+       int status;
+
+       status = system(cmd);
+       if (status < 0) {
+               g_print("Failed to execute command: %s\n", strerror(errno));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void ppp_connect(const char *iface, const char *local, const char *peer,
+                       const char *dns1, const char *dns2,
+                       gpointer user)
+{
+       char buf[512];
+
+       g_print("Network Device: %s\n", iface);
+       g_print("IP Address: %s\n", local);
+       g_print("Peer IP Address: %s\n", peer);
+       g_print("Primary DNS Server: %s\n", dns1);
+       g_print("Secondary DNS Server: %s\n", dns2);
+
+       snprintf(buf, sizeof(buf), "%s %s up", IFCONFIG_PATH, iface);
+       execute(buf);
+
+       snprintf(buf, sizeof(buf), "%s %s %s pointopoint %s", IFCONFIG_PATH,
+                               iface, local, peer);
+       execute(buf);
+
+       snprintf(buf, sizeof(buf), "echo 1 > /proc/sys/net/ipv4/ip_forward");
+       execute(buf);
+}
+
+static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user)
+{
+       GAtServer *server = user;
+
+       g_print("PPP Link down: %d\n", reason);
+
+       g_at_ppp_unref(ppp);
+       ppp = NULL;
+
+       if (reason == G_AT_PPP_REASON_LINK_DEAD) {
+               g_at_server_unref(server);
+               server = NULL;
+               return;
+       }
+
+       g_at_server_resume(server);
+       g_at_server_set_debug(server, server_debug, "Server");
+
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_NO_CARRIER);
+       data_mode = FALSE;
+}
+
+static void open_ppp(gpointer user)
+{
+       GAtIO *io = g_at_server_get_io(server);
+
+       g_at_server_suspend(server);
+       g_at_ppp_listen(ppp, io);
+}
+
+static gboolean setup_ppp(GAtServer *server)
+{
+       /* open ppp */
+       ppp = g_at_ppp_server_new("192.168.1.1");
+       if (ppp == NULL)
+               return FALSE;
+
+       g_at_ppp_set_debug(ppp, server_debug, "PPP");
+
+       g_at_ppp_set_credentials(ppp, "", "");
+
+       /* set connect and disconnect callbacks */
+       g_at_ppp_set_connect_function(ppp, ppp_connect, server);
+       g_at_ppp_set_disconnect_function(ppp, ppp_disconnect, server);
+       g_at_ppp_set_server_info(ppp, "192.168.1.2",
+                                       "10.10.10.10", "10.10.10.11");
+
+       return TRUE;
+}
+
+static void cgmi_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               g_at_server_send_info(server, "oFono", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+       };
+}
+
+static void cgmm_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               g_at_server_send_info(server, "oFono pre-1.0", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+       };
+}
+
+static void cgmr_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[256];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               sprintf(buf, "oFono pre-1.0 version: %s", VERSION);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+       };
+}
+
+static void cgsn_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               g_at_server_send_info(server, "123456789", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+       };
+}
+
+static gboolean send_ok(gpointer user)
+{
+       GAtServer *server = user;
+
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+
+       return FALSE;
+}
+
+static void cfun_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[12];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_info(server, "+CFUN: (0-1)", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               snprintf(buf, sizeof(buf), "+CFUN: %d", modem_mode);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+       {
+               GAtResultIter iter;
+               int mode;
+
+               g_at_result_iter_init(&iter, cmd);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
+                       goto error;
+
+               if (mode != 0 && mode != 1)
+                       goto error;
+
+               if (modem_mode == mode) {
+                       g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+                       break;
+               }
+
+               modem_mode = mode;
+               g_timeout_add_seconds(1, send_ok, server);
+               break;
+       }
+       default:
+               goto error;
+       };
+
+       return;
+
+error:
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+}
+
+static void cpin_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       if (modem_mode == 0) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_info(server, "+CPIN: (READY)", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_info(server, "+CPIN: READY", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static gboolean do_netreg(gpointer user)
+{
+       GAtServer *server = user;
+       char buf[32];
+
+       if (data_mode)
+               return FALSE;
+
+       network_status = 1;
+
+       switch (modem_creg) {
+       case 1:
+       case 2:
+               snprintf(buf, sizeof(buf), "+CREG: %d", network_status);
+               g_at_server_send_unsolicited(server, buf);
+               break;
+       }
+
+       return FALSE;
+}
+
+static void cops_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       if (modem_mode == 0) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_timeout_add_seconds(3, send_ok, server);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_info(server, "+COPS: 0", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+       {
+               GAtServerResult result;
+               GAtResultIter iter;
+               int mode;
+
+               g_at_result_iter_init(&iter, cmd);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &mode) == TRUE) {
+                       if (mode == 0) {
+                               g_timeout_add_seconds(2, do_netreg, server);
+                               result = G_AT_SERVER_RESULT_OK;
+                       } else
+                               result = G_AT_SERVER_RESULT_ERROR;
+               } else
+                       result = G_AT_SERVER_RESULT_ERROR;
+
+               g_at_server_send_final(server, result);
+               break;
+       }
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void creg_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[20];
+
+       if (modem_mode == 0) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_info(server, "+CREG: (0-2)", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               snprintf(buf, sizeof(buf), "+CREG: %d,%d",
+                                               modem_creg, network_status);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+       {
+               GAtResultIter iter;
+               int mode;
+
+               g_at_result_iter_init(&iter, cmd);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
+                       goto error;
+
+               if (mode != 0 && mode != 1 && mode != 2)
+                       goto error;
+
+               modem_creg = mode;
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       }
+       default:
+               goto error;
+       };
+
+       return;
+
+error:
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+}
+
+static void cgreg_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[20];
+
+       if (modem_mode == 0) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_info(server, "+CGREG: (0-2)", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               snprintf(buf, sizeof(buf), "+CGREG: %d,%d",
+                                               modem_cgreg, network_status);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+       {
+               GAtResultIter iter;
+               int mode;
+
+               g_at_result_iter_init(&iter, cmd);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
+                       goto error;
+
+               if (mode != 0 && mode != 1 && mode != 2)
+                       goto error;
+
+               modem_cgreg = mode;
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       }
+       default:
+               goto error;
+       };
+
+       return;
+
+error:
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+}
+
+static void cgatt_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[12];
+
+       if (modem_mode == 0) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_info(server, "+CGATT: (0-1)", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               snprintf(buf, sizeof(buf), "+CGATT: %d", network_attach);
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+       {
+               GAtResultIter iter;
+               int mode;
+
+               g_at_result_iter_init(&iter, cmd);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
+                       goto error;
+
+               if (mode != 0 && mode != 1)
+                       goto error;
+
+               if (network_attach == mode) {
+                       g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+                       break;
+               }
+
+               network_attach = mode;
+               g_timeout_add_seconds(1, send_ok, server);
+               break;
+       }
+       default:
+               goto error;
+       };
+
+       return;
+
+error:
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+}
+
+static void cgdata_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       GAtIO *io;
+
+       if (modem_mode == 0) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               if (setup_ppp(server) == FALSE)
+                       goto error;
+
+               g_at_server_send_intermediate(server, "CONNECT");
+               data_mode = TRUE;
+
+               io = g_at_server_get_io(server);
+               g_at_io_set_write_done(io, open_ppp, server);
+               break;
+       default:
+error:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cgdcont_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       if (modem_mode == 0) {
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cimi_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               g_at_server_send_info(server, "246813579", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void csms_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_info(server, "+CSMS: 0,1,1,1", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_info(server, "+CSMS: (0)", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cmgf_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_info(server, "+CMGF: 0", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_info(server, "+CMGF: (0,1)", TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cpms_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[2048];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               sprintf(buf, "+CPMS: (\"SM\",\"ME\"),(\"SM\",\"ME\"),(\"SM\")");
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cnmi_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[2048];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               sprintf(buf, "+CNMI: (0,1,2,3),(0,1),(0,1,2),(0),(0,1)");
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cscs_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[2048];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               sprintf(buf, "+CSCS: \"GSM\",\"IRA\",\"UCS2\"");
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cmgl_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cpbs_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       char buf[2048];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               sprintf(buf, "+CPBS: (\"FD\",\"SM\",\"SN\")");
+               g_at_server_send_info(server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       default:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void dial_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *cmd, gpointer user)
+{
+       GAtResultIter iter;
+       const char *dial_str;
+       char c;
+
+       if (type != G_AT_SERVER_REQUEST_TYPE_SET)
+               goto error;
+
+       g_at_result_iter_init(&iter, cmd);
+       g_at_result_iter_next(&iter, "");
+
+       dial_str = g_at_result_iter_raw_line(&iter);
+       if (dial_str == NULL)
+               goto error;
+
+       g_print("dial call %s\n", dial_str);
+
+       c = *dial_str;
+       if (c == '*' || c == '#' || c == 'T' || c == 't') {
+               GAtIO *io = g_at_server_get_io(server);
+
+               if (setup_ppp(server) == FALSE)
+                       goto error;
+
+               g_at_server_send_intermediate(server, "CONNECT");
+               data_mode = TRUE;
+               g_at_io_set_write_done(io, open_ppp, server);
+       }
+
+       return;
+
+error:
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+}
+
+static void add_handler(GAtServer *server)
+{
+       g_at_server_set_debug(server, server_debug, "Server");
+
+       g_at_server_register(server, "+CGMI",    cgmi_cb,    NULL, NULL);
+       g_at_server_register(server, "+CGMM",    cgmm_cb,    NULL, NULL);
+       g_at_server_register(server, "+CGMR",    cgmr_cb,    NULL, NULL);
+       g_at_server_register(server, "+CGSN",    cgsn_cb,    NULL, NULL);
+       g_at_server_register(server, "+CFUN",    cfun_cb,    NULL, NULL);
+       g_at_server_register(server, "+CPIN",    cpin_cb,    NULL, NULL);
+       g_at_server_register(server, "+COPS",    cops_cb,    NULL, NULL);
+       g_at_server_register(server, "+CREG",    creg_cb,    NULL, NULL);
+       g_at_server_register(server, "+CGREG",   cgreg_cb,   NULL, NULL);
+       g_at_server_register(server, "+CGATT",   cgatt_cb,   NULL, NULL);
+       g_at_server_register(server, "+CGDATA",  cgdata_cb,  NULL, NULL);
+       g_at_server_register(server, "+CGDCONT", cgdcont_cb, NULL, NULL);
+       g_at_server_register(server, "+CIMI",    cimi_cb,    NULL, NULL);
+       g_at_server_register(server, "+CSMS",    csms_cb,    NULL, NULL);
+       g_at_server_register(server, "+CMGF",    cmgf_cb,    NULL, NULL);
+       g_at_server_register(server, "+CPMS",    cpms_cb,    NULL, NULL);
+       g_at_server_register(server, "+CNMI",    cnmi_cb,    NULL, NULL);
+       g_at_server_register(server, "+CSCS",    cscs_cb,    NULL, NULL);
+       g_at_server_register(server, "+CMGL",    cmgl_cb,    NULL, NULL);
+       g_at_server_register(server, "+CPBS",    cpbs_cb,    NULL, NULL);
+       g_at_server_register(server, "D",        dial_cb,    NULL, NULL);
+}
+
+static void server_destroy(gpointer user)
+{
+       struct sock_server *data = user;
+
+       g_free(data);
+}
+
+static void set_raw_mode(int fd)
+{
+       struct termios ti;
+
+       memset(&ti, 0, sizeof(ti));
+       tcgetattr(fd, &ti);
+       tcflush(fd, TCIOFLUSH);
+       cfmakeraw(&ti);
+       tcsetattr(fd, TCSANOW, &ti);
+}
+
+static gboolean create_tty(const char *modem_path)
+{
+       int master, slave;
+       char pty_name[256];
+       GIOChannel *server_io;
+
+       if (modem_path == NULL)
+               return FALSE;
+
+       if (openpty(&master, &slave, pty_name, NULL, NULL) < 0)
+               return FALSE;
+
+       set_raw_mode(slave);
+
+       g_print("new pty is created at %s\n", pty_name);
+
+       server_io = g_io_channel_unix_new(master);
+
+       server = g_at_server_new(server_io);
+       if (server == NULL) {
+               g_io_channel_shutdown(server_io, FALSE, NULL);
+               g_io_channel_unref(server_io);
+
+               return FALSE;
+       }
+
+       g_io_channel_unref(server_io);
+
+       return TRUE;
+}
+
+static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user)
+{
+       struct sockaddr saddr;
+       unsigned int len = sizeof(saddr);
+       int fd;
+       GIOChannel *client_io = NULL;
+       struct sock_server *data = user;
+
+       if (cond != G_IO_IN)
+               goto error;
+
+       fd = accept(data->server_sock, &saddr, &len);
+       if (fd == -1)
+               goto error;
+
+       client_io = g_io_channel_unix_new(fd);
+
+       server = g_at_server_new(client_io);
+       g_io_channel_unref(client_io);
+
+       if (server == NULL)
+               goto error;
+
+       add_handler(server);
+
+       return TRUE;
+
+error:
+       g_free(data);
+
+       return FALSE;
+}
+
+static struct sock_server *socket_common(int sk, struct sockaddr *addr,
+                                               const char *modem_path)
+{
+       struct sock_server *sock;
+       int reuseaddr = 1;
+
+       setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
+
+       if (bind(sk, addr, sizeof(struct sockaddr)) < 0) {
+               g_print("Can't bind socket: %s (%d)", strerror(errno), errno);
+
+               close(sk);
+
+               return NULL;
+       }
+
+       if (listen(sk, 1) < 0) {
+               g_print("Can't listen on socket: %s (%d)",
+                                               strerror(errno), errno);
+
+               close(sk);
+
+               return NULL;
+       }
+
+       sock = g_try_new0(struct sock_server, 1);
+       if (sock == NULL)
+               return FALSE;
+
+       sock->server_sock = sk;
+
+       return sock;
+}
+
+static gboolean create_tcp(const char *modem_path, int port)
+{
+       struct sockaddr_in addr;
+       int sk;
+       struct sock_server *server;
+       GIOChannel *server_io;
+
+       if (modem_path == NULL)
+               return FALSE;
+
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0) {
+               g_print("Can't create tcp/ip socket: %s (%d)\n",
+                                               strerror(errno), errno);
+               return FALSE;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       addr.sin_port = htons(port);
+
+       server = socket_common(sk, (struct sockaddr *) &addr, modem_path);
+       if (server == NULL)
+               return FALSE;
+
+       g_print("new tcp is created at tcp port %d\n", port);
+
+       server_io = g_io_channel_unix_new(sk);
+
+       g_io_channel_set_close_on_unref(server_io, TRUE);
+
+       server_watch = g_io_add_watch_full(server_io,
+                               G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               on_socket_connected, server, server_destroy);
+
+       g_io_channel_unref(server_io);
+
+       return TRUE;
+}
+
+static gboolean create_unix(const char *modem_path, const char *sock_path)
+{
+       struct sockaddr_un addr;
+       int sk;
+       struct sock_server *server;
+       GIOChannel *server_io;
+
+       if (modem_path == NULL)
+               return FALSE;
+
+       sk = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (sk < 0) {
+               g_print("Can't create unix socket: %s (%d)\n",
+                                               strerror(errno), errno);
+
+               return FALSE;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
+
+       /* Unlink any existing socket for this session */
+       unlink(addr.sun_path);
+
+       server = socket_common(sk, (struct sockaddr *) &addr, modem_path);
+       if (server == NULL)
+               return FALSE;
+
+       g_print("new unix socket is created at %s\n", sock_path);
+
+       server_io = g_io_channel_unix_new(sk);
+
+       g_io_channel_set_close_on_unref(server_io, TRUE);
+
+       server_watch = g_io_add_watch_full(server_io,
+                               G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               on_socket_connected, server, server_destroy);
+
+       g_io_channel_unref(server_io);
+
+       return TRUE;
+}
+
+static void test_server(int type)
+{
+       switch (type) {
+       case 0:
+               if (create_tty("/phonesim1") == FALSE)
+                       exit(1);
+
+               add_handler(server);
+               break;
+       case 1:
+               if (create_tcp("/phonesim1", DEFAULT_TCP_PORT) == FALSE)
+                       exit(1);
+               break;
+       case 2:
+               if (create_unix("/phonesim1", DEFAULT_SOCK_PATH) == FALSE)
+                       exit(1);
+               break;
+       }
+}
+
+static gboolean signal_cb(GIOChannel *channel, GIOCondition cond, gpointer data)
+{
+       int signal_fd = GPOINTER_TO_INT(data);
+       struct signalfd_siginfo si;
+       ssize_t res;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR))
+               return FALSE;
+
+       res = read(signal_fd, &si, sizeof(si));
+       if (res != sizeof(si))
+               return FALSE;
+
+       switch (si.ssi_signo) {
+       case SIGINT:
+               server_cleanup();
+               break;
+       case SIGTERM:
+               server_cleanup();
+               break;
+       default:
+               break;
+       }
+
+       return TRUE;
+}
+
+static int create_signal_io(void)
+{
+       sigset_t mask;
+       GIOChannel *signal_io;
+       int signal_fd, signal_source;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGTERM);
+       sigaddset(&mask, SIGINT);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               g_error("Can't set signal mask");
+               return 1;
+       }
+
+       signal_fd = signalfd(-1, &mask, 0);
+       if (signal_fd < 0) {
+               g_error("Can't create signal filedescriptor");
+               return 1;
+       }
+
+       signal_io = g_io_channel_unix_new(signal_fd);
+
+       g_io_channel_set_close_on_unref(signal_io, TRUE);
+
+       signal_source = g_io_add_watch(signal_io,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       signal_cb, GINT_TO_POINTER(signal_fd));
+
+       g_io_channel_unref(signal_io);
+
+       return signal_source;
+}
+
+static void usage(void)
+{
+       g_print("test-server - AT Server testing\n"
+               "Usage:\n");
+       g_print("\ttest-server [-t type]\n");
+       g_print("Types:\n"
+               "\t0: Pseudo TTY port (default)\n"
+               "\t1: TCP sock at port 12346)\n"
+               "\t2: Unix sock at ./server_sock\n");
+}
+
+int main(int argc, char **argv)
+{
+       int opt, signal_source;
+       int type = 0;
+
+       while ((opt = getopt(argc, argv, "ht:")) != EOF) {
+               switch (opt) {
+               case 't':
+                       type = atoi(optarg);
+                       break;
+               case 'h':
+                       usage();
+                       exit(1);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       test_server(type);
+
+       signal_source = create_signal_io();
+
+       mainloop = g_main_loop_new(NULL, FALSE);
+
+       g_main_loop_run(mainloop);
+
+       g_main_loop_unref(mainloop);
+
+       g_source_remove(signal_source);
+
+       return 0;
+}
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
new file mode 100644 (file)
index 0000000..a0583e6
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GDBUS_H
+#define __GDBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef void (* GDBusWatchFunction) (DBusConnection *connection,
+                                                       void *user_data);
+
+typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy);
+
+typedef void (* GDBusDestroyFunction) (void *user_data);
+
+typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending);
+
+typedef enum {
+       G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
+       G_DBUS_METHOD_FLAG_NOREPLY    = (1 << 1),
+       G_DBUS_METHOD_FLAG_ASYNC      = (1 << 2),
+} GDBusMethodFlags;
+
+typedef enum {
+       G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
+} GDBusSignalFlags;
+
+typedef enum {
+       G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
+} GDBusPropertyFlags;
+
+typedef enum {
+       G_DBUS_SECURITY_FLAG_DEPRECATED        = (1 << 0),
+       G_DBUS_SECURITY_FLAG_BUILTIN           = (1 << 1),
+       G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
+} GDBusSecurityFlags;
+
+typedef struct {
+       const char *name;
+       const char *signature;
+       const char *reply;
+       GDBusMethodFunction function;
+       GDBusMethodFlags flags;
+       unsigned int privilege;
+} GDBusMethodTable;
+
+typedef struct {
+       const char *name;
+       const char *signature;
+       GDBusSignalFlags flags;
+} GDBusSignalTable;
+
+typedef struct {
+       const char *name;
+       const char *type;
+       GDBusPropertyFlags flags;
+} GDBusPropertyTable;
+
+typedef struct {
+       unsigned int privilege;
+       const char *action;
+       GDBusSecurityFlags flags;
+       GDBusSecurityFunction function;
+} GDBusSecurityTable;
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name);
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+                                       __attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+                                       __attribute__((format(printf, 3, 4)));
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args);
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args);
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...);
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args);
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...);
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args);
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction function,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
new file mode 100644 (file)
index 0000000..8718da0
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#ifdef NEED_DBUS_WATCH_GET_UNIX_FD
+#define dbus_watch_get_unix_fd dbus_watch_get_fd
+#endif
+
+#include "gdbus.h"
+
+#define DISPATCH_TIMEOUT  0
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct timeout_handler {
+       guint id;
+       DBusTimeout *timeout;
+};
+
+struct watch_info {
+       guint id;
+       DBusWatch *watch;
+       DBusConnection *conn;
+};
+
+struct disconnect_data {
+       GDBusWatchFunction function;
+       void *user_data;
+};
+
+static gboolean disconnected_signal(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct disconnect_data *dc_data = data;
+
+       error("Got disconnected from the system message bus");
+
+       dc_data->function(conn, dc_data->user_data);
+
+       dbus_connection_unref(conn);
+
+       return TRUE;
+}
+
+static gboolean message_dispatch(void *data)
+{
+       DBusConnection *conn = data;
+
+       dbus_connection_ref(conn);
+
+       /* Dispatch messages */
+       while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
+
+       dbus_connection_unref(conn);
+
+       return FALSE;
+}
+
+static inline void queue_dispatch(DBusConnection *conn,
+                                               DBusDispatchStatus status)
+{
+       if (status == DBUS_DISPATCH_DATA_REMAINS)
+               g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn);
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct watch_info *info = data;
+       unsigned int flags = 0;
+       DBusDispatchStatus status;
+
+       dbus_connection_ref(info->conn);
+
+       if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
+       if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
+       if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
+       if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+
+       dbus_watch_handle(info->watch, flags);
+
+       status = dbus_connection_get_dispatch_status(info->conn);
+       queue_dispatch(info->conn, status);
+
+       dbus_connection_unref(info->conn);
+
+       return TRUE;
+}
+
+static void watch_info_free(void *data)
+{
+       struct watch_info *info = data;
+
+       if (info->id > 0) {
+               g_source_remove(info->id);
+               info->id = 0;
+       }
+
+       dbus_connection_unref(info->conn);
+
+       g_free(info);
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+       DBusConnection *conn = data;
+       GIOCondition cond = G_IO_HUP | G_IO_ERR;
+       GIOChannel *chan;
+       struct watch_info *info;
+       unsigned int flags;
+       int fd;
+
+       if (!dbus_watch_get_enabled(watch))
+               return TRUE;
+
+       info = g_new0(struct watch_info, 1);
+
+       fd = dbus_watch_get_unix_fd(watch);
+       chan = g_io_channel_unix_new(fd);
+
+       info->watch = watch;
+       info->conn = dbus_connection_ref(conn);
+
+       dbus_watch_set_data(watch, info, watch_info_free);
+
+       flags = dbus_watch_get_flags(watch);
+
+       if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
+       if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
+
+       info->id = g_io_add_watch(chan, cond, watch_func, info);
+
+       g_io_channel_unref(chan);
+
+       return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+       if (dbus_watch_get_enabled(watch))
+               return;
+
+       /* will trigger watch_info_free() */
+       dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+       /* Because we just exit on OOM, enable/disable is
+        * no different from add/remove */
+       if (dbus_watch_get_enabled(watch))
+               add_watch(watch, data);
+       else
+               remove_watch(watch, data);
+}
+
+static gboolean timeout_handler_dispatch(gpointer data)
+{
+       struct timeout_handler *handler = data;
+
+       handler->id = 0;
+
+       /* if not enabled should not be polled by the main loop */
+       if (!dbus_timeout_get_enabled(handler->timeout))
+               return FALSE;
+
+       dbus_timeout_handle(handler->timeout);
+
+       return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+       struct timeout_handler *handler = data;
+
+       if (handler->id > 0) {
+               g_source_remove(handler->id);
+               handler->id = 0;
+       }
+
+       g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+       int interval = dbus_timeout_get_interval(timeout);
+       struct timeout_handler *handler;
+
+       if (!dbus_timeout_get_enabled(timeout))
+               return TRUE;
+
+       handler = g_new0(struct timeout_handler, 1);
+
+       handler->timeout = timeout;
+
+       dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+       handler->id = g_timeout_add(interval, timeout_handler_dispatch,
+                                                               handler);
+
+       return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+       /* will trigger timeout_handler_free() */
+       dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+       if (dbus_timeout_get_enabled(timeout))
+               add_timeout(timeout, data);
+       else
+               remove_timeout(timeout, data);
+}
+
+static void dispatch_status(DBusConnection *conn,
+                                       DBusDispatchStatus status, void *data)
+{
+       if (!dbus_connection_get_is_connected(conn))
+               return;
+
+       queue_dispatch(conn, status);
+}
+
+static inline void setup_dbus_with_main_loop(DBusConnection *conn)
+{
+       dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
+                                               watch_toggled, conn, NULL);
+
+       dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
+                                               timeout_toggled, NULL, NULL);
+
+       dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+                                                               NULL, NULL);
+}
+
+static gboolean setup_bus(DBusConnection *conn, const char *name,
+                                               DBusError *error)
+{
+       gboolean result;
+       DBusDispatchStatus status;
+
+       if (name != NULL) {
+               result = g_dbus_request_name(conn, name, error);
+
+               if (error != NULL) {
+                       if (dbus_error_is_set(error) == TRUE)
+                               return FALSE;
+               }
+
+               if (result == FALSE)
+                       return FALSE;
+       }
+
+       setup_dbus_with_main_loop(conn);
+
+       status = dbus_connection_get_dispatch_status(conn);
+       queue_dispatch(conn, status);
+
+       return TRUE;
+}
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get_private(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error)
+{
+       int result;
+
+       result = dbus_bus_request_name(connection, name,
+                                       DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return FALSE;
+       }
+
+       if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+               if (error != NULL)
+                       dbus_set_error(error, name, "Name already in use");
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy)
+{
+       struct disconnect_data *dc_data;
+
+       dc_data = g_new0(struct disconnect_data, 1);
+
+       dc_data->function = function;
+       dc_data->user_data = user_data;
+
+       dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+       if (g_dbus_add_signal_watch(connection, NULL, NULL,
+                               DBUS_INTERFACE_LOCAL, "Disconnected",
+                               disconnected_signal, dc_data, g_free) == 0) {
+               error("Failed to add watch for D-Bus Disconnected signal");
+               g_free(dc_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
diff --git a/gdbus/object.c b/gdbus/object.c
new file mode 100644 (file)
index 0000000..d17a101
--- /dev/null
@@ -0,0 +1,868 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct generic_data {
+       unsigned int refcount;
+       GSList *interfaces;
+       char *introspect;
+};
+
+struct interface_data {
+       char *name;
+       const GDBusMethodTable *methods;
+       const GDBusSignalTable *signals;
+       const GDBusPropertyTable *properties;
+       void *user_data;
+       GDBusDestroyFunction destroy;
+};
+
+struct security_data {
+       GDBusPendingReply pending;
+       DBusMessage *message;
+       const GDBusMethodTable *method;
+       void *iface_user_data;
+};
+
+static void print_arguments(GString *gstr, const char *sig,
+                                               const char *direction)
+{
+       int i;
+
+       for (i = 0; sig[i]; i++) {
+               char type[32];
+               int struct_level, dict_level;
+               unsigned int len;
+               gboolean complete;
+
+               complete = FALSE;
+               struct_level = dict_level = 0;
+               memset(type, 0, sizeof(type));
+
+               /* Gather enough data to have a single complete type */
+               for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
+                       switch (sig[i]){
+                       case '(':
+                               struct_level++;
+                               break;
+                       case ')':
+                               struct_level--;
+                               if (struct_level <= 0 && dict_level <= 0)
+                                       complete = TRUE;
+                               break;
+                       case '{':
+                               dict_level++;
+                               break;
+                       case '}':
+                               dict_level--;
+                               if (struct_level <= 0 && dict_level <= 0)
+                                       complete = TRUE;
+                               break;
+                       case 'a':
+                               break;
+                       default:
+                               if (struct_level <= 0 && dict_level <= 0)
+                                       complete = TRUE;
+                               break;
+                       }
+
+                       type[len] = sig[i];
+
+                       if (complete)
+                               break;
+               }
+
+
+               if (direction)
+                       g_string_append_printf(gstr,
+                                       "\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
+                                       type, direction);
+               else
+                       g_string_append_printf(gstr,
+                                       "\t\t\t<arg type=\"%s\"/>\n",
+                                       type);
+       }
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+       const GDBusMethodTable *method;
+       const GDBusSignalTable *signal;
+
+       for (method = iface->methods; method && method->name; method++) {
+               if (!strlen(method->signature) && !strlen(method->reply))
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
+                                                               method->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
+                                                               method->name);
+                       print_arguments(gstr, method->signature, "in");
+                       print_arguments(gstr, method->reply, "out");
+                       g_string_append_printf(gstr, "\t\t</method>\n");
+               }
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               if (!strlen(signal->signature))
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
+                                                               signal->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
+                                                               signal->name);
+                       print_arguments(gstr, signal->signature, NULL);
+                       g_string_append_printf(gstr, "\t\t</signal>\n");
+               }
+       }
+}
+
+static void generate_introspection_xml(DBusConnection *conn,
+                               struct generic_data *data, const char *path)
+{
+       GSList *list;
+       GString *gstr;
+       char **children;
+       int i;
+
+       g_free(data->introspect);
+
+       gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+       g_string_append_printf(gstr, "<node>\n");
+
+       for (list = data->interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+
+               g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
+                                                               iface->name);
+
+               generate_interface_xml(gstr, iface);
+
+               g_string_append_printf(gstr, "\t</interface>\n");
+       }
+
+       if (!dbus_connection_list_registered(conn, path, &children))
+               goto done;
+
+       for (i = 0; children[i]; i++)
+               g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
+                                                               children[i]);
+
+       dbus_free_string_array(children);
+
+done:
+       g_string_append_printf(gstr, "</node>\n");
+
+       data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusMessage *introspect(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       DBusMessage *reply;
+
+       if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
+               error("Unexpected signature to introspect call");
+               return NULL;
+       }
+
+       if (data->introspect == NULL)
+               generate_introspection_xml(connection, data,
+                                               dbus_message_get_path(message));
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+                       DBusMessage *message, const GDBusMethodTable *method,
+                                                       void *iface_user_data)
+{
+       DBusMessage *reply;
+
+       reply = method->function(connection, message, iface_user_data);
+
+       if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+               if (reply != NULL)
+                       dbus_message_unref(reply);
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+               if (reply == NULL)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (reply == NULL)
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+       dbus_connection_send(connection, reply, NULL);
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               process_message(connection, secdata->message,
+                               secdata->method, secdata->iface_user_data);
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+               DBusMessage *reply;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               reply = g_dbus_create_error_valist(secdata->message,
+                                                       name, format, args);
+               if (reply != NULL) {
+                       dbus_connection_send(connection, reply, NULL);
+                       dbus_message_unref(reply);
+               }
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+
+       g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+       va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+struct builtin_security_data {
+       DBusConnection *conn;
+       GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+       struct builtin_security_data *data = user_data;
+
+       if (authorized == TRUE)
+               g_dbus_pending_success(data->conn, data->pending);
+       else
+               g_dbus_pending_error(data->conn, data->pending,
+                                               DBUS_ERROR_AUTH_FAILED, NULL);
+
+       g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending)
+{
+       struct builtin_security_data *data;
+
+       data = g_new0(struct builtin_security_data, 1);
+       data->conn = conn;
+       data->pending = pending;
+
+       if (polkit_check_authorization(conn, action, interaction,
+                               builtin_security_result, data, 30000) < 0)
+               g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+                       const GDBusMethodTable *method, void *iface_user_data)
+{
+       const GDBusSecurityTable *security;
+
+       for (security = security_table; security && security->privilege;
+                                                               security++) {
+               struct security_data *secdata;
+               gboolean interaction;
+
+               if (security->privilege != method->privilege)
+                       continue;
+
+               secdata = g_new(struct security_data, 1);
+               secdata->pending = next_pending++;
+               secdata->message = dbus_message_ref(msg);
+               secdata->method = method;
+               secdata->iface_user_data = iface_user_data;
+
+               pending_security = g_slist_prepend(pending_security, secdata);
+
+               if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+                       interaction = TRUE;
+               else
+                       interaction = FALSE;
+
+               if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+                                                       security->function)
+                       security->function(conn, security->action,
+                                               interaction, secdata->pending);
+               else
+                       builtin_security_function(conn, security->action,
+                                               interaction, secdata->pending);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+       struct generic_data *data = user_data;
+
+       g_free(data->introspect);
+       g_free(data);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+                                               const char *name)
+{
+       GSList *list;
+
+       if (name == NULL)
+               return NULL;
+
+       for (list = interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+               if (!strcmp(name, iface->name))
+                       return iface;
+       }
+
+       return NULL;
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const GDBusMethodTable *method;
+       const char *interface;
+
+       interface = dbus_message_get_interface(message);
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (method = iface->methods; method &&
+                       method->name && method->function; method++) {
+               if (dbus_message_is_method_call(message, iface->name,
+                                                       method->name) == FALSE)
+                       continue;
+
+               if (dbus_message_has_signature(message,
+                                               method->signature) == FALSE)
+                       continue;
+
+               if (check_privilege(connection, message, method,
+                                               iface->user_data) == TRUE)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+
+               return process_message(connection, message, method,
+                                                       iface->user_data);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+       .unregister_function    = generic_unregister,
+       .message_function       = generic_message,
+};
+
+static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
+{
+       struct generic_data *data = NULL;
+       char *parent_path, *slash;
+
+       parent_path = g_strdup(child_path);
+       slash = strrchr(parent_path, '/');
+       if (slash == NULL)
+               goto done;
+
+       if (slash == parent_path && parent_path[1] != '\0')
+               parent_path[1] = '\0';
+       else
+               *slash = '\0';
+
+       if (!strlen(parent_path))
+               goto done;
+
+       if (dbus_connection_get_object_path_data(conn, parent_path,
+                                                       (void *) &data) == FALSE) {
+               goto done;
+       }
+
+       invalidate_parent_data(conn, parent_path);
+
+       if (data == NULL)
+               goto done;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+done:
+       g_free(parent_path);
+}
+
+static GDBusMethodTable introspect_methods[] = {
+       { "Introspect", "",     "s", introspect },
+       { }
+};
+
+static void add_interface(struct generic_data *data, const char *name,
+                               const GDBusMethodTable *methods,
+                               const GDBusSignalTable *signals,
+                               const GDBusPropertyTable *properties,
+                               void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct interface_data *iface;
+
+       iface = g_new0(struct interface_data, 1);
+       iface->name = g_strdup(name);
+       iface->methods = methods;
+       iface->signals = signals;
+       iface->properties = properties;
+       iface->user_data = user_data;
+       iface->destroy = destroy;
+
+       data->interfaces = g_slist_append(data->interfaces, iface);
+}
+
+static struct generic_data *object_path_ref(DBusConnection *connection,
+                                                       const char *path)
+{
+       struct generic_data *data;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == TRUE) {
+               if (data != NULL) {
+                       data->refcount++;
+                       return data;
+               }
+       }
+
+       data = g_new0(struct generic_data, 1);
+       data->refcount = 1;
+
+       data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+       if (!dbus_connection_register_object_path(connection, path,
+                                               &generic_table, data)) {
+               g_free(data->introspect);
+               g_free(data);
+               return NULL;
+       }
+
+       invalidate_parent_data(connection, path);
+
+       add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
+                       introspect_methods, NULL, NULL, data, NULL);
+
+       return data;
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+       struct interface_data *iface;
+
+       iface = find_interface(data->interfaces, name);
+       if (iface == NULL)
+               return FALSE;
+
+       data->interfaces = g_slist_remove(data->interfaces, iface);
+
+       if (iface->destroy)
+               iface->destroy(iface->user_data);
+
+       g_free(iface->name);
+       g_free(iface);
+
+       return TRUE;
+}
+
+static void object_path_unref(DBusConnection *connection, const char *path)
+{
+       struct generic_data *data = NULL;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return;
+
+       if (data == NULL)
+               return;
+
+       data->refcount--;
+
+       if (data->refcount > 0)
+               return;
+
+       remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+
+       invalidate_parent_data(connection, path);
+
+       dbus_connection_unregister_object_path(connection, path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+                               const char *interface, const char *name,
+                               const char **args)
+{
+       struct generic_data *data = NULL;
+       struct interface_data *iface;
+       const GDBusSignalTable *signal;
+
+       *args = NULL;
+       if (!dbus_connection_get_object_path_data(conn, path,
+                                       (void *) &data) || data == NULL) {
+               error("dbus_connection_emit_signal: path %s isn't registered",
+                               path);
+               return FALSE;
+       }
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL) {
+               error("dbus_connection_emit_signal: %s does not implement %s",
+                               path, interface);
+               return FALSE;
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               if (!strcmp(signal->name, name)) {
+                       *args = signal->signature;
+                       break;
+               }
+       }
+
+       if (*args == NULL) {
+               error("No signal named %s on interface %s", name, interface);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static dbus_bool_t emit_signal_valist(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name,
+                                               int first,
+                                               va_list var_args)
+{
+       DBusMessage *signal;
+       dbus_bool_t ret;
+       const char *signature, *args;
+
+       if (!check_signal(conn, path, interface, name, &args))
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, name);
+       if (signal == NULL) {
+               error("Unable to allocate new %s.%s signal", interface,  name);
+               return FALSE;
+       }
+
+       ret = dbus_message_append_args_valist(signal, first, var_args);
+       if (!ret)
+               goto fail;
+
+       signature = dbus_message_get_signature(signal);
+       if (strcmp(args, signature) != 0) {
+               error("%s.%s: expected signature'%s' but got '%s'",
+                               interface, name, args, signature);
+               ret = FALSE;
+               goto fail;
+       }
+
+       ret = dbus_connection_send(conn, signal, NULL);
+
+fail:
+       dbus_message_unref(signal);
+
+       return ret;
+}
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy)
+{
+       struct generic_data *data;
+
+       data = object_path_ref(connection, path);
+       if (data == NULL)
+               return FALSE;
+
+       if (find_interface(data->interfaces, name)) {
+               object_path_unref(connection, path);
+               return FALSE;
+       }
+
+       add_interface(data, name, methods, signals,
+                       properties, user_data, destroy);
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name)
+{
+       struct generic_data *data = NULL;
+
+       if (path == NULL)
+               return FALSE;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return FALSE;
+
+       if (data == NULL)
+               return FALSE;
+
+       if (remove_interface(data, name) == FALSE)
+               return FALSE;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       object_path_unref(connection, path);
+
+       return TRUE;
+}
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+       if (security_table != NULL)
+               return FALSE;
+
+       security_table = security;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+       security_table = NULL;
+
+       return TRUE;
+}
+
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args)
+{
+       char str[1024];
+
+       vsnprintf(str, sizeof(str), format, args);
+
+       return dbus_message_new_error(message, name, str);
+}
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, format);
+
+       reply = g_dbus_create_error_valist(message, name, format, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, type);
+
+       reply = g_dbus_create_reply_valist(message, type, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
+{
+       dbus_bool_t result;
+
+       if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+               dbus_message_set_no_reply(message, TRUE);
+
+       result = dbus_connection_send(connection, message, NULL);
+
+       dbus_message_unref(message);
+
+       return result;
+}
+
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return FALSE;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+
+       return g_dbus_send_message(connection, reply);
+}
+
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = g_dbus_send_reply_valist(connection, message, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args)
+{
+       return emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+}
diff --git a/gdbus/polkit.c b/gdbus/polkit.c
new file mode 100644 (file)
index 0000000..9e95fa3
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+                                       const char *key, const char *str)
+{
+       DBusMessageIter dict, entry, value;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+       dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_STRING_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(&dict, &entry);
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+                               const char *action, dbus_uint32_t flags)
+{
+       const char *busname = dbus_bus_get_unique_name(conn);
+       const char *kind = "system-bus-name";
+       const char *cancel = "";
+       DBusMessageIter subject;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+                                                       NULL, &subject);
+       dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+       add_dict_with_string_value(&subject, "name", busname);
+       dbus_message_iter_close_container(iter, &subject);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+       add_empty_string_dict(iter);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+       DBusMessageIter result;
+       dbus_bool_t authorized, challenge;
+
+       dbus_message_iter_recurse(iter, &result);
+
+       dbus_message_iter_get_basic(&result, &authorized);
+       dbus_message_iter_get_basic(&result, &challenge);
+
+       return authorized;
+}
+
+struct authorization_data {
+       void (*function) (dbus_bool_t authorized, void *user_data);
+       void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+       struct authorization_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       dbus_bool_t authorized = FALSE;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+               goto done;
+
+       dbus_message_iter_init(reply, &iter);
+
+       authorized = parse_result(&iter);
+
+done:
+       if (data->function != NULL)
+               data->function(authorized, data->user_data);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout)
+{
+       struct authorization_data *data;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+       dbus_uint32_t flags = 0x00000000;
+
+       if (conn == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+                               AUTHORITY_INTF, "CheckAuthorization");
+       if (msg == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       if (interaction == TRUE)
+               flags |= 0x00000001;
+
+       if (action == NULL)
+               action = "org.freedesktop.policykit.exec";
+
+       dbus_message_iter_init_append(msg, &iter);
+       add_arguments(conn, &iter, action, flags);
+
+       if (dbus_connection_send_with_reply(conn, msg,
+                                               &call, timeout) == FALSE) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, authorization_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(msg);
+
+       return 0;
+}
diff --git a/gdbus/watch.c b/gdbus/watch.c
new file mode 100644 (file)
index 0000000..fba58c3
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *listeners = NULL;
+
+struct service_data {
+       DBusConnection *conn;
+       DBusPendingCall *call;
+       char *name;
+       const char *owner;
+       guint id;
+       struct filter_callback *callback;
+};
+
+struct filter_callback {
+       GDBusWatchFunction conn_func;
+       GDBusWatchFunction disc_func;
+       GDBusSignalFunction signal_func;
+       GDBusDestroyFunction destroy_func;
+       struct service_data *data;
+       void *user_data;
+       guint id;
+};
+
+struct filter_data {
+       DBusConnection *connection;
+       DBusHandleMessageFunction handle_func;
+       char *name;
+       char *owner;
+       char *path;
+       char *interface;
+       char *member;
+       char *argument;
+       GSList *callbacks;
+       GSList *processed;
+       guint name_watch;
+       gboolean lock;
+       gboolean registered;
+};
+
+static struct filter_data *filter_data_find(DBusConnection *connection,
+                                                       const char *name,
+                                                       const char *owner,
+                                                       const char *path,
+                                                       const char *interface,
+                                                       const char *member,
+                                                       const char *argument)
+{
+       GSList *current;
+
+       for (current = listeners;
+                       current != NULL; current = current->next) {
+               struct filter_data *data = current->data;
+
+               if (connection != data->connection)
+                       continue;
+
+               if (name && data->name &&
+                               g_str_equal(name, data->name) == FALSE)
+                       continue;
+
+               if (owner && data->owner &&
+                               g_str_equal(owner, data->owner) == FALSE)
+                       continue;
+
+               if (path && data->path &&
+                               g_str_equal(path, data->path) == FALSE)
+                       continue;
+
+               if (interface && data->interface &&
+                               g_str_equal(interface, data->interface) == FALSE)
+                       continue;
+
+               if (member && data->member &&
+                               g_str_equal(member, data->member) == FALSE)
+                       continue;
+
+               if (argument && data->argument &&
+                               g_str_equal(argument, data->argument) == FALSE)
+                       continue;
+
+               return data;
+       }
+
+       return NULL;
+}
+
+static void format_rule(struct filter_data *data, char *rule, size_t size)
+{
+       const char *sender;
+       int offset;
+
+       offset = snprintf(rule, size, "type='signal'");
+       sender = data->name ? : data->owner;
+
+       if (sender)
+               offset += snprintf(rule + offset, size - offset,
+                               ",sender='%s'", sender);
+       if (data->path)
+               offset += snprintf(rule + offset, size - offset,
+                               ",path='%s'", data->path);
+       if (data->interface)
+               offset += snprintf(rule + offset, size - offset,
+                               ",interface='%s'", data->interface);
+       if (data->member)
+               offset += snprintf(rule + offset, size - offset,
+                               ",member='%s'", data->member);
+       if (data->argument)
+               snprintf(rule + offset, size - offset,
+                               ",arg0='%s'", data->argument);
+}
+
+static gboolean add_match(struct filter_data *data,
+                               DBusHandleMessageFunction filter)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+       dbus_error_init(&err);
+
+       dbus_bus_add_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Adding match rule \"%s\" failed: %s", rule,
+                               err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       data->handle_func = filter;
+       data->registered = TRUE;
+
+       return TRUE;
+}
+
+static gboolean remove_match(struct filter_data *data)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+
+       dbus_error_init(&err);
+
+       dbus_bus_remove_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Removing owner match rule for %s failed: %s",
+                               rule, err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static struct filter_data *filter_data_get(DBusConnection *connection,
+                                       DBusHandleMessageFunction filter,
+                                       const char *sender,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *member,
+                                       const char *argument)
+{
+       struct filter_data *data;
+       const char *name = NULL, *owner = NULL;
+
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) {
+               if (!dbus_connection_add_filter(connection,
+                                       message_filter, NULL, NULL)) {
+                       error("dbus_connection_add_filter() failed");
+                       return NULL;
+               }
+       }
+
+       if (sender == NULL)
+               goto proceed;
+
+       if (sender[0] == ':')
+               owner = sender;
+       else
+               name = sender;
+
+proceed:
+       data = filter_data_find(connection, name, owner, path, interface,
+                                       member, argument);
+       if (data)
+               return data;
+
+       data = g_new0(struct filter_data, 1);
+
+       data->connection = dbus_connection_ref(connection);
+       data->name = name ? g_strdup(name) : NULL;
+       data->owner = owner ? g_strdup(owner) : NULL;
+       data->path = g_strdup(path);
+       data->interface = g_strdup(interface);
+       data->member = g_strdup(member);
+       data->argument = g_strdup(argument);
+
+       if (!add_match(data, filter)) {
+               g_free(data);
+               return NULL;
+       }
+
+       listeners = g_slist_append(listeners, data);
+
+       return data;
+}
+
+static struct filter_callback *filter_data_find_callback(
+                                               struct filter_data *data,
+                                               guint id)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+       for (l = data->processed; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+
+       return NULL;
+}
+
+static void filter_data_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next)
+               g_free(l->data);
+
+       g_slist_free(data->callbacks);
+       g_dbus_remove_watch(data->connection, data->name_watch);
+       g_free(data->name);
+       g_free(data->owner);
+       g_free(data->path);
+       g_free(data->interface);
+       g_free(data->member);
+       g_free(data->argument);
+       dbus_connection_unref(data->connection);
+       g_free(data);
+}
+
+static void filter_data_call_and_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->disc_func)
+                       cb->disc_func(data->connection, cb->user_data);
+               if (cb->destroy_func)
+                       cb->destroy_func(cb->user_data);
+               g_free(cb);
+       }
+
+       filter_data_free(data);
+}
+
+static struct filter_callback *filter_data_add_callback(
+                                               struct filter_data *data,
+                                               GDBusWatchFunction connect,
+                                               GDBusWatchFunction disconnect,
+                                               GDBusSignalFunction signal,
+                                               GDBusDestroyFunction destroy,
+                                               void *user_data)
+{
+       struct filter_callback *cb = NULL;
+
+       cb = g_new0(struct filter_callback, 1);
+
+       cb->conn_func = connect;
+       cb->disc_func = disconnect;
+       cb->signal_func = signal;
+       cb->destroy_func = destroy;
+       cb->user_data = user_data;
+       cb->id = ++listener_id;
+
+       if (data->lock)
+               data->processed = g_slist_append(data->processed, cb);
+       else
+               data->callbacks = g_slist_append(data->callbacks, cb);
+
+       return cb;
+}
+
+static void service_data_free(struct service_data *data)
+{
+       struct filter_callback *callback = data->callback;
+
+       dbus_connection_unref(data->conn);
+
+       if (data->call)
+               dbus_pending_call_unref(data->call);
+
+       if (data->id)
+               g_source_remove(data->id);
+
+       g_free(data->name);
+       g_free(data);
+
+       callback->data = NULL;
+}
+
+static gboolean filter_data_remove_callback(struct filter_data *data,
+                                               struct filter_callback *cb)
+{
+       DBusConnection *connection;
+
+       data->callbacks = g_slist_remove(data->callbacks, cb);
+       data->processed = g_slist_remove(data->processed, cb);
+
+       /* Cancel pending operations */
+       if (cb->data) {
+               if (cb->data->call)
+                       dbus_pending_call_cancel(cb->data->call);
+               service_data_free(cb->data);
+       }
+
+       if (cb->destroy_func)
+               cb->destroy_func(cb->user_data);
+
+       g_free(cb);
+
+       /* Don't remove the filter if other callbacks exist or data is lock
+        * processing callbacks */
+       if (data->callbacks || data->lock)
+               return TRUE;
+
+       if (data->registered && !remove_match(data))
+               return FALSE;
+
+       connection = dbus_connection_ref(data->connection);
+       listeners = g_slist_remove(listeners, data);
+       filter_data_free(data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                       NULL);
+       if (data == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       dbus_connection_unref(connection);
+
+       return TRUE;
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (cb->signal_func && !cb->signal_func(connection, message,
+                                                       cb->user_data)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void update_name_cache(const char *name, const char *owner)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               g_free(data->owner);
+               data->owner = g_strdup(owner);
+       }
+}
+
+static const char *check_name_cache(const char *name)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               return data->owner;
+       }
+
+       return NULL;
+}
+
+static DBusHandlerResult service_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+       char *name, *old, *new;
+
+       if (!dbus_message_get_args(message, NULL,
+                               DBUS_TYPE_STRING, &name,
+                               DBUS_TYPE_STRING, &old,
+                               DBUS_TYPE_STRING, &new,
+                               DBUS_TYPE_INVALID)) {
+               error("Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       update_name_cache(name, new);
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (*new == '\0') {
+                       if (cb->disc_func)
+                               cb->disc_func(connection, cb->user_data);
+               } else {
+                       if (cb->conn_func)
+                               cb->conn_func(connection, cb->user_data);
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               /* Only auto remove if it is a bus name watch */
+               if (data->argument[0] == ':' &&
+                               (cb->conn_func == NULL || cb->disc_func == NULL)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data;
+       const char *sender, *path, *iface, *member, *arg = NULL;
+
+       /* Only filter signals */
+       if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       sender = dbus_message_get_sender(message);
+       path = dbus_message_get_path(message);
+       iface = dbus_message_get_interface(message);
+       member = dbus_message_get_member(message);
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
+
+       /* Sender is always bus name */
+       data = filter_data_find(connection, NULL, sender, path, iface, member,
+                                       arg);
+       if (data == NULL) {
+               error("Got %s.%s signal which has no listeners", iface, member);
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (data->handle_func) {
+               data->lock = TRUE;
+
+               data->handle_func(connection, message, data);
+
+               data->callbacks = data->processed;
+               data->processed = NULL;
+               data->lock = FALSE;
+       }
+
+       if (data->callbacks)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       remove_match(data);
+
+       listeners = g_slist_remove(listeners, data);
+       filter_data_free(data);
+
+       /* Remove filter if there no listener left for the connection */
+       data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                       NULL);
+       if (data == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean update_service(void *user_data)
+{
+       struct service_data *data = user_data;
+       struct filter_callback *cb = data->callback;
+
+       update_name_cache(data->name, data->owner);
+       if (cb->conn_func)
+               cb->conn_func(data->conn, cb->user_data);
+
+       service_data_free(data);
+
+       return FALSE;
+}
+
+static void service_reply(DBusPendingCall *call, void *user_data)
+{
+       struct service_data *data = user_data;
+       DBusMessage *reply;
+       DBusError err;
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply))
+               goto fail;
+
+       if (dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_STRING, &data->owner,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto fail;
+
+       update_service(data);
+
+       goto done;
+
+fail:
+       error("%s", err.message);
+       dbus_error_free(&err);
+       service_data_free(data);
+done:
+       dbus_message_unref(reply);
+}
+
+static void check_service(DBusConnection *connection,
+                                       const char *name,
+                                       struct filter_callback *callback)
+{
+       DBusMessage *message;
+       struct service_data *data;
+
+       data = g_try_malloc0(sizeof(*data));
+       if (data == NULL) {
+               error("Can't allocate data structure");
+               return;
+       }
+
+       data->conn = dbus_connection_ref(connection);
+       data->name = g_strdup(name);
+       data->callback = callback;
+       callback->data = data;
+
+       data->owner = check_name_cache(name);
+       if (data->owner != NULL) {
+               data->id = g_idle_add(update_service, data);
+               return;
+       }
+
+       message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+                       DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
+       if (message == NULL) {
+               error("Can't allocate new message");
+               g_free(data);
+               return;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                                       &data->call, -1) == FALSE) {
+               error("Failed to execute method call");
+               g_free(data);
+               goto done;
+       }
+
+       if (data->call == NULL) {
+               error("D-Bus connection not available");
+               g_free(data);
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       if (name == NULL)
+               return 0;
+
+       data = filter_data_get(connection, service_filter, NULL, NULL,
+                               DBUS_INTERFACE_DBUS, "NameOwnerChanged",
+                               name);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (connect)
+               check_service(connection, name, cb);
+
+       return cb->id;
+}
+
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction func,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       return g_dbus_add_service_watch(connection, name, NULL, func,
+                                                       user_data, destroy);
+}
+
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       data = filter_data_get(connection, signal_filter, sender, path,
+                               interface, member, NULL);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (data->name != NULL && data->name_watch == 0)
+               data->name_watch = g_dbus_add_service_watch(connection,
+                                                       data->name, NULL,
+                                                       NULL, NULL, NULL);
+
+       return cb->id;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+       GSList *ldata;
+
+       if (id == 0)
+               return FALSE;
+
+       for (ldata = listeners; ldata; ldata = ldata->next) {
+               data = ldata->data;
+
+               cb = filter_data_find_callback(data, id);
+               if (cb) {
+                       filter_data_remove_callback(data, cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+       struct filter_data *data;
+
+       while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
+                                       NULL, NULL))) {
+               listeners = g_slist_remove(listeners, data);
+               filter_data_call_and_free(data);
+       }
+
+       dbus_connection_remove_filter(connection, message_filter, NULL);
+}
diff --git a/gisi/client.c b/gisi/client.c
new file mode 100644 (file)
index 0000000..7512441
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "client.h"
+
+struct _GIsiClient {
+       GIsiModem *modem;
+       unsigned timeout;
+       uint8_t resource;
+};
+
+uint8_t g_isi_client_resource(GIsiClient *client)
+{
+       return client != NULL ? client->resource : 0;
+}
+
+GIsiModem *g_isi_client_modem(GIsiClient *client)
+{
+       return client != NULL ? client->modem : NULL;
+}
+
+GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource)
+{
+       GIsiClient *client;
+
+       if (modem == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       client  = g_try_new0(GIsiClient, 1);
+       if (client == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       client->timeout = G_ISI_CLIENT_DEFAULT_TIMEOUT;
+       client->resource = resource;
+       client->modem = modem;
+
+       return client;
+}
+
+void g_isi_client_reset(GIsiClient *client)
+{
+       g_isi_remove_pending_by_owner(client->modem, client->resource, client);
+};
+
+void g_isi_client_destroy(GIsiClient *client)
+{
+       if (client == NULL)
+               return;
+
+       g_isi_client_reset(client);
+       g_free(client);
+}
+
+void g_isi_client_set_timeout(GIsiClient *client, unsigned timeout)
+{
+       if (client == NULL)
+               return;
+
+       client->timeout = timeout;
+}
+
+gboolean g_isi_client_send(GIsiClient *client,
+                       const void *__restrict msg, size_t len,
+                       GIsiNotifyFunc notify, void *data,
+                       GDestroyNotify destroy)
+{
+       GIsiPending *op;
+
+       op = g_isi_request_send(client->modem, client->resource, msg, len,
+                               client->timeout, notify, data, destroy);
+
+       g_isi_pending_set_owner(op, client);
+
+       return op != NULL;
+}
+
+gboolean g_isi_client_send_with_timeout(GIsiClient *client,
+                               const void *__restrict buf, size_t len,
+                               unsigned timeout,
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy)
+{
+       GIsiPending *op;
+
+       op = g_isi_request_send(client->modem, client->resource, buf, len,
+                               timeout, notify, data, destroy);
+
+       g_isi_pending_set_owner(op, client);
+
+       return op != NULL;
+}
+
+gboolean g_isi_client_vsend(GIsiClient *client,
+                       const struct iovec *iov, size_t iovlen,
+                       GIsiNotifyFunc notify, void *data,
+                       GDestroyNotify destroy)
+{
+       GIsiPending *op;
+
+       op = g_isi_request_vsend(client->modem, client->resource, iov, iovlen,
+                               client->timeout, notify, data, destroy);
+
+       g_isi_pending_set_owner(op, client);
+
+       return op != NULL;
+}
+
+gboolean g_isi_client_vsend_with_timeout(GIsiClient *client,
+                               const struct iovec *__restrict iov,
+                               size_t iovlen, unsigned timeout,
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy)
+{
+       GIsiPending *op;
+
+       op = g_isi_request_vsend(client->modem, client->resource, iov, iovlen,
+                                       timeout, notify, data, destroy);
+
+       g_isi_pending_set_owner(op, client);
+
+       return op != NULL;
+}
+
+gboolean g_isi_client_ind_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data)
+{
+       GIsiPending *op;
+
+       op = g_isi_ind_subscribe(client->modem, client->resource, type,
+                                       notify, data, NULL);
+
+       g_isi_pending_set_owner(op, client);
+
+       return op != NULL;
+}
+
+gboolean g_isi_client_ntf_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data)
+{
+       GIsiPending *op;
+
+       op = g_isi_ntf_subscribe(client->modem, client->resource, type,
+                                       notify, data, NULL);
+
+       g_isi_pending_set_owner(op, client);
+
+       return op != NULL;
+}
+
+gboolean g_isi_client_verify(GIsiClient *client, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy)
+{
+       GIsiPending *op;
+
+       op = g_isi_resource_ping(client->modem, client->resource,
+                                       notify, data, destroy);
+
+       g_isi_pending_set_owner(op, client);
+
+       return op != NULL;
+}
diff --git a/gisi/client.h b/gisi/client.h
new file mode 100644 (file)
index 0000000..8224cd2
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_CLIENT_H
+#define __GISI_CLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <glib/gtypes.h>
+
+#include "modem.h"
+
+#define G_ISI_CLIENT_DEFAULT_TIMEOUT   (5)
+
+struct _GIsiClient;
+typedef struct _GIsiClient GIsiClient;
+
+GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource);
+GIsiModem *g_isi_client_modem(GIsiClient *client);
+uint8_t g_isi_client_resource(GIsiClient *client);
+void g_isi_client_reset(GIsiClient *client);
+void g_isi_client_destroy(GIsiClient *client);
+
+void g_isi_client_set_timeout(GIsiClient *client, unsigned timeout);
+
+gboolean g_isi_client_send(GIsiClient *client,
+                       const void *__restrict msg, size_t len,
+                       GIsiNotifyFunc notify, void *data,
+                       GDestroyNotify destroy);
+
+gboolean g_isi_client_vsend(GIsiClient *client,
+                       const struct iovec *iov, size_t iovlen,
+                       GIsiNotifyFunc notify, void *data,
+                       GDestroyNotify destroy);
+
+gboolean g_isi_client_send_with_timeout(GIsiClient *client,
+                               const void *__restrict msg,
+                               size_t len, unsigned timeout,
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy);
+
+gboolean g_isi_client_vsend_with_timeout(GIsiClient *client,
+                               const struct iovec *iov,
+                               size_t iovlen, unsigned timeout,
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy);
+
+gboolean g_isi_client_ind_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data);
+gboolean g_isi_client_ntf_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data);
+
+gboolean g_isi_client_verify(GIsiClient *client, GIsiNotifyFunc notify,
+                               void *data, GDestroyNotify destroy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_CLIENT_H */
diff --git a/gisi/common.h b/gisi/common.h
new file mode 100644 (file)
index 0000000..c3fe9fd
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_COMMON_H
+#define __GISI_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PN_COMMGR                              0x10
+#define PN_NAMESERVICE                         0xDB
+#define PN_FIREWALL                            0x43
+#define COMMON_TIMEOUT                         5
+
+enum message_id {
+       PNS_NAME_ADD_REQ =                      0x05,
+       PNS_NAME_REMOVE_REQ =                   0x07,
+       PNS_SUBSCRIBED_RESOURCES_IND =          0x10,
+       PNS_SUBSCRIBED_RESOURCES_EXTEND_IND =   0x12,
+       COMM_ISI_VERSION_GET_REQ =              0x12,
+       COMM_ISI_VERSION_GET_RESP =             0x13,
+       COMM_ISA_ENTITY_NOT_REACHABLE_RESP =    0x14,
+       COMM_SERVICE_NOT_AUTHENTICATED_RESP =   0x17,
+       COMMON_MESSAGE =                        0xF0,
+};
+
+enum GIsiPhonetDevice {
+       PN_DEV_PC =     0x10,   /* PC Suite */
+       PN_DEV_HOST =   0x00,   /* Host modem */
+       PN_DEV_MODEM =  0x60,   /* Modem */
+       PN_DEV_SOS =    0x6C,   /* Symbian or Linux */
+};
+
+enum GIsiMessageType {
+       GISI_MESSAGE_TYPE_REQ,
+       GISI_MESSAGE_TYPE_IND,
+       GISI_MESSAGE_TYPE_NTF,
+       GISI_MESSAGE_TYPE_RESP,
+       GISI_MESSAGE_TYPE_COMMON,       /* ISI version, namely */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_COMMON_H */
diff --git a/gisi/iter.c b/gisi/iter.c
new file mode 100644 (file)
index 0000000..3cc1ae3
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <arpa/inet.h>
+
+#include "iter.h"
+
+static inline void bcd_to_mccmnc(const uint8_t *restrict bcd,
+                                       char *mcc, char *mnc)
+{
+       mcc[0] = '0' + (bcd[0] & 0x0F);
+       mcc[1] = '0' + ((bcd[0] & 0xF0) >> 4);
+       mcc[2] = '0' + (bcd[1] & 0x0F);
+       mcc[3] = '\0';
+
+       mnc[0] = '0' + (bcd[2] & 0x0F);
+       mnc[1] = '0' + ((bcd[2] & 0xF0) >> 4);
+       mnc[2] = (bcd[1] & 0xF0) == 0xF0 ? '\0' : '0' +
+                       (bcd[1] & 0xF0);
+       mnc[3] = '\0';
+}
+
+void g_isi_sb_iter_init_full(GIsiSubBlockIter *iter, const GIsiMessage *msg,
+                               size_t used, gboolean longhdr,
+                               uint16_t sub_blocks)
+{
+       const uint8_t *data = g_isi_msg_data(msg);
+       size_t len = g_isi_msg_data_len(msg);
+
+       if (data == NULL)
+               len = used = 0;
+
+       iter->cursor = longhdr ? 4 : 2;
+       iter->start = (uint8_t *) data + used;
+       iter->end = iter->start + len;
+       iter->longhdr = longhdr;
+       iter->sub_blocks = len > used ? sub_blocks : 0;
+}
+
+void g_isi_sb_iter_init(GIsiSubBlockIter *iter, const GIsiMessage *msg,
+                       size_t used)
+{
+       const uint8_t *data = g_isi_msg_data(msg);
+       size_t len = g_isi_msg_data_len(msg);
+
+       if (data == NULL)
+               len = used = 0;
+
+       iter->cursor = 2;
+       iter->start = (uint8_t *) data + used;
+       iter->end = iter->start + len;
+       iter->longhdr = FALSE;
+       iter->sub_blocks = len > used ? iter->start[-1] : 0;
+}
+
+void g_isi_sb_subiter_init(GIsiSubBlockIter *outer, GIsiSubBlockIter *inner,
+                               size_t used)
+{
+       size_t len = g_isi_sb_iter_get_len(outer);
+
+       if (outer->start + len > outer->end ||
+                       outer->start + used > outer->end)
+               len = used = 0;
+
+       inner->cursor = 2;
+       inner->start = outer->start + used;
+       inner->end = inner->start + len;
+       inner->longhdr = FALSE;
+       inner->sub_blocks = len > used ? inner->start[-1] : 0;
+}
+
+void g_isi_sb_subiter_init_full(GIsiSubBlockIter *outer,
+                               GIsiSubBlockIter *inner, size_t used,
+                               gboolean longhdr, uint16_t sub_blocks)
+{
+       size_t len = g_isi_sb_iter_get_len(outer);
+
+       if (outer->start + len > outer->end ||
+                       outer->start + used > outer->end)
+               len = used = 0;
+
+       inner->cursor = longhdr ? 4 : 2;
+       inner->start = outer->start + used;
+       inner->end = inner->start + len;
+       inner->longhdr = longhdr;
+       inner->sub_blocks = len > used ? sub_blocks : 0;
+}
+
+gboolean g_isi_sb_iter_is_valid(const GIsiSubBlockIter *iter)
+{
+       if (iter == NULL)
+               return FALSE;
+
+       if (iter->sub_blocks == 0)
+               return FALSE;
+
+       if (iter->start + (iter->longhdr ? 4 : 2) > iter->end)
+               return FALSE;
+
+       if (iter->start + g_isi_sb_iter_get_len(iter) > iter->end)
+               return FALSE;
+
+       return TRUE;
+}
+
+int g_isi_sb_iter_get_id(const GIsiSubBlockIter *iter)
+{
+       if (iter->longhdr)
+               return (iter->start[0] << 8) | iter->start[1];
+
+       return iter->start[0];
+}
+
+size_t g_isi_sb_iter_get_len(const GIsiSubBlockIter *iter)
+{
+       if (iter->longhdr)
+               return (iter->start[2] << 8) | iter->start[3];
+
+       return iter->start[1];
+}
+
+gboolean g_isi_sb_iter_get_data(const GIsiSubBlockIter *restrict iter,
+                               void **data, unsigned pos)
+{
+       if ((size_t) pos > g_isi_sb_iter_get_len(iter)
+                       || iter->start + pos > iter->end)
+               return FALSE;
+
+       *data = (void *) iter->start + pos;
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_get_byte(const GIsiSubBlockIter *restrict iter,
+                               uint8_t *byte, unsigned pos)
+{
+       if ((size_t) pos > g_isi_sb_iter_get_len(iter)
+                       || iter->start + pos > iter->end)
+               return FALSE;
+
+       *byte = iter->start[pos];
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_get_word(const GIsiSubBlockIter *restrict iter,
+                               uint16_t *word, unsigned pos)
+{
+       uint16_t val;
+
+       if (pos + 1 > g_isi_sb_iter_get_len(iter))
+               return FALSE;
+
+       memcpy(&val, iter->start + pos, sizeof(uint16_t));
+       *word = ntohs(val);
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_get_dword(const GIsiSubBlockIter *restrict iter,
+                                       uint32_t *dword, unsigned pos)
+{
+       uint32_t val;
+
+       if (pos + 3 > g_isi_sb_iter_get_len(iter))
+               return FALSE;
+
+       memcpy(&val, iter->start + pos, sizeof(uint32_t));
+       *dword = ntohl(val);
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_eat_byte(GIsiSubBlockIter *restrict iter,
+                               uint8_t *byte)
+{
+       if (!g_isi_sb_iter_get_byte(iter, byte, iter->cursor))
+               return FALSE;
+
+       iter->cursor += 1;
+       return TRUE;
+}
+gboolean g_isi_sb_iter_eat_word(GIsiSubBlockIter *restrict iter,
+                               uint16_t *word)
+{
+       if (!g_isi_sb_iter_get_word(iter, word, iter->cursor))
+               return FALSE;
+
+       iter->cursor += 2;
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_eat_dword(GIsiSubBlockIter *restrict iter,
+                               uint32_t *dword)
+{
+       if (!g_isi_sb_iter_get_dword(iter, dword, iter->cursor))
+               return FALSE;
+
+       iter->cursor += 4;
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_get_oper_code(const GIsiSubBlockIter *restrict iter,
+                                       char *mcc, char *mnc, unsigned pos)
+{
+       if (pos + 2 > g_isi_sb_iter_get_len(iter))
+               return FALSE;
+
+       bcd_to_mccmnc(iter->start + pos, mcc, mnc);
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_eat_oper_code(GIsiSubBlockIter *restrict iter,
+                                       char *mcc, char *mnc)
+{
+       if (!g_isi_sb_iter_get_oper_code(iter, mcc, mnc, iter->cursor))
+               return FALSE;
+
+       iter->cursor += 3;
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_get_alpha_tag(const GIsiSubBlockIter *restrict iter,
+                                       char **utf8, size_t len, unsigned pos)
+{
+       uint8_t *ucs2 = NULL;
+
+       if (pos > g_isi_sb_iter_get_len(iter))
+               return FALSE;
+
+       if (utf8 == NULL || len == 0 || pos + len > g_isi_sb_iter_get_len(iter))
+               return FALSE;
+
+       ucs2 = iter->start + pos;
+
+       if (ucs2 + len > iter->end)
+               return FALSE;
+
+       *utf8 = g_convert((const char *) ucs2, len, "UTF-8//TRANSLIT",
+                               "UCS-2BE", NULL, NULL, NULL);
+       return *utf8 != NULL;
+}
+
+gboolean g_isi_sb_iter_eat_alpha_tag(GIsiSubBlockIter *restrict iter,
+                                       char **utf8, size_t len)
+{
+       if (!g_isi_sb_iter_get_alpha_tag(iter, utf8, len, iter->cursor))
+               return FALSE;
+
+       iter->cursor += len;
+       return TRUE;
+}
+gboolean g_isi_sb_iter_get_latin_tag(const GIsiSubBlockIter *restrict iter,
+                                       char **latin, size_t len, unsigned pos)
+{
+       uint8_t *str = NULL;
+
+       if (pos > g_isi_sb_iter_get_len(iter))
+               return FALSE;
+
+       if (latin == NULL || len == 0)
+               return FALSE;
+
+       if (pos + len > g_isi_sb_iter_get_len(iter))
+               return FALSE;
+
+       str = iter->start + pos;
+
+       if (str + len > iter->end)
+               return FALSE;
+
+       *latin = g_strndup((char *) str, len);
+
+       return *latin != NULL;
+}
+
+gboolean g_isi_sb_iter_eat_latin_tag(GIsiSubBlockIter *restrict iter,
+                                       char **latin, size_t len)
+{
+       if (!g_isi_sb_iter_get_latin_tag(iter, latin, len, iter->cursor))
+               return FALSE;
+
+       iter->cursor += len;
+       return TRUE;
+}
+gboolean g_isi_sb_iter_next(GIsiSubBlockIter *iter)
+{
+       uint8_t len = g_isi_sb_iter_get_len(iter);
+
+       if (len == 0)
+               len = iter->longhdr ? 4 : 2;
+
+       if (iter->sub_blocks == 0)
+               return FALSE;
+
+       if (iter->start + len > iter->end)
+               return FALSE;
+
+
+       iter->cursor = iter->longhdr ? 4 : 2;
+       iter->start += len;
+       iter->sub_blocks--;
+
+       return TRUE;
+}
+
+gboolean g_isi_sb_iter_get_struct(const GIsiSubBlockIter *restrict iter,
+                                       void **type, size_t len, unsigned pos)
+{
+       if (iter->start + pos + len > iter->end)
+               return FALSE;
+
+       return g_isi_sb_iter_get_data(iter, type, pos);
+}
diff --git a/gisi/iter.h b/gisi/iter.h
new file mode 100644 (file)
index 0000000..52ca6b5
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_ITER_H
+#define __GISI_ITER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include "message.h"
+
+struct _GIsiSubBlockIter {
+       uint8_t *start;
+       uint8_t *end;
+       gboolean longhdr;
+       uint16_t cursor;
+       uint16_t sub_blocks;
+};
+typedef struct _GIsiSubBlockIter GIsiSubBlockIter;
+
+void g_isi_sb_iter_init(GIsiSubBlockIter *iter, const GIsiMessage *msg,
+                       size_t used);
+void g_isi_sb_iter_init_full(GIsiSubBlockIter *iter, const GIsiMessage *msg,
+                               size_t used, gboolean longhdr,
+                               uint16_t sub_blocks);
+void g_isi_sb_subiter_init(GIsiSubBlockIter *outer, GIsiSubBlockIter *inner,
+                               size_t used);
+void g_isi_sb_subiter_init_full(GIsiSubBlockIter *out, GIsiSubBlockIter *in,
+                               size_t used, gboolean longhdr,
+                               uint16_t sub_blocks);
+gboolean g_isi_sb_iter_is_valid(const GIsiSubBlockIter *iter);
+
+gboolean g_isi_sb_iter_next(GIsiSubBlockIter *iter);
+
+int g_isi_sb_iter_get_id(const GIsiSubBlockIter *iter);
+size_t g_isi_sb_iter_get_len(const GIsiSubBlockIter *iter);
+
+gboolean g_isi_sb_iter_get_data(const GIsiSubBlockIter *restrict iter,
+                               void **data, unsigned pos);
+gboolean g_isi_sb_iter_get_byte(const GIsiSubBlockIter *restrict iter,
+                               uint8_t *byte, unsigned pos);
+gboolean g_isi_sb_iter_get_word(const GIsiSubBlockIter *restrict iter,
+                               uint16_t *word, unsigned pos);
+gboolean g_isi_sb_iter_get_dword(const GIsiSubBlockIter *restrict iter,
+                                       uint32_t *dword, unsigned pos);
+gboolean g_isi_sb_iter_eat_byte(GIsiSubBlockIter *restrict iter,
+                               uint8_t *byte);
+gboolean g_isi_sb_iter_eat_word(GIsiSubBlockIter *restrict iter,
+                               uint16_t *word);
+gboolean g_isi_sb_iter_eat_dword(GIsiSubBlockIter *restrict iter,
+                               uint32_t *dword);
+gboolean g_isi_sb_iter_get_oper_code(const GIsiSubBlockIter *restrict iter,
+                                       char *mcc, char *mnc, unsigned pos);
+gboolean g_isi_sb_iter_eat_oper_code(GIsiSubBlockIter *restrict iter,
+                                       char *mcc, char *mnc);
+gboolean g_isi_sb_iter_get_alpha_tag(const GIsiSubBlockIter *restrict iter,
+                                       char **utf8, size_t len, unsigned pos);
+gboolean g_isi_sb_iter_eat_alpha_tag(GIsiSubBlockIter *restrict iter,
+                                       char **utf8, size_t len);
+gboolean g_isi_sb_iter_get_latin_tag(const GIsiSubBlockIter *restrict iter,
+                                       char **ascii, size_t len, unsigned pos);
+gboolean g_isi_sb_iter_eat_latin_tag(GIsiSubBlockIter *restrict iter,
+                                       char **ascii, size_t len);
+gboolean g_isi_sb_iter_get_struct(const GIsiSubBlockIter *restrict iter,
+                                       void **ptr, size_t len, unsigned pos);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_ITER_H */
diff --git a/gisi/message.c b/gisi/message.c
new file mode 100644 (file)
index 0000000..87cfd9f
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <glib.h>
+
+#include "message.h"
+
+int g_isi_msg_version_major(const GIsiMessage *msg)
+{
+       if (msg == NULL || msg->version == NULL)
+               return -1;
+
+       return msg->version->major;
+}
+
+int g_isi_msg_version_minor(const GIsiMessage *msg)
+{
+       if (msg == NULL || msg->version == NULL)
+               return -1;
+
+       return msg->version->minor;
+}
+
+int g_isi_msg_error(const GIsiMessage *msg)
+{
+       return msg != NULL ? -msg->error : -EINVAL;
+}
+
+const char *g_isi_msg_strerror(const GIsiMessage *msg)
+{
+       return strerror(-g_isi_msg_error(msg));
+}
+
+uint8_t g_isi_msg_resource(const GIsiMessage *msg)
+{
+       if (msg == NULL || msg->addr == NULL)
+               return 0;
+
+       return msg->addr->spn_resource;
+}
+
+uint16_t g_isi_msg_object(const GIsiMessage *msg)
+{
+       if (msg == NULL || msg->addr == NULL)
+               return 0;
+
+       return (msg->addr->spn_dev << 8) | msg->addr->spn_obj;
+}
+
+uint8_t g_isi_msg_id(const GIsiMessage *msg)
+{
+       const uint8_t *buf;
+
+       if (msg == NULL || msg->data == NULL || msg->len < 2)
+               return 0;
+
+       buf = msg->data;
+
+       return buf[1];
+}
+
+uint8_t g_isi_msg_utid(const GIsiMessage *msg)
+{
+       const uint8_t *buf;
+
+       if (msg == NULL || msg->data == NULL || msg->len < 2)
+               return 0;
+
+       buf = msg->data;
+
+       return buf[0];
+}
+
+size_t g_isi_msg_data_len(const GIsiMessage *msg)
+{
+       if (msg == NULL || msg->data == NULL)
+               return 0;
+
+       return msg->len - 2;
+}
+
+const void *g_isi_msg_data(const GIsiMessage *msg)
+{
+       if (msg == NULL || msg->data == NULL)
+               return NULL;
+
+       return (void *)msg->data + 2;
+}
+
+gboolean g_isi_msg_data_get_byte(const GIsiMessage *msg, unsigned offset,
+                                       uint8_t *byte)
+{
+       const uint8_t *buf = g_isi_msg_data(msg);
+
+       if (buf == NULL || g_isi_msg_data_len(msg) < offset)
+               return FALSE;
+
+       if (byte != NULL)
+               *byte = buf[offset];
+
+       return TRUE;
+}
+
+gboolean g_isi_msg_data_get_word(const GIsiMessage *msg, unsigned offset,
+                                       uint16_t *word)
+{
+       const uint8_t *buf = g_isi_msg_data(msg);
+       uint16_t val;
+
+       if (buf == NULL || g_isi_msg_data_len(msg) < offset + 1)
+               return FALSE;
+
+       memcpy(&val, buf + offset, sizeof(uint16_t));
+
+       if (word != NULL)
+               *word = ntohs(val);
+
+       return TRUE;
+}
+
+gboolean g_isi_msg_data_get_struct(const GIsiMessage *msg, unsigned offset,
+                                       const void **type, size_t len)
+{
+       if (g_isi_msg_data_len(msg) < offset + len)
+               return FALSE;
+
+       if (type != NULL)
+               *type = g_isi_msg_data(msg) + offset;
+
+       return TRUE;
+}
diff --git a/gisi/message.h b/gisi/message.h
new file mode 100644 (file)
index 0000000..5bdd7ba
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_MESSAGE_H
+#define __GISI_MESSAGE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <glib/gtypes.h>
+
+#include "phonet.h"
+
+struct _GIsiVersion {
+       int major;
+       int minor;
+};
+typedef struct _GIsiVersion GIsiVersion;
+
+struct _GIsiMessage {
+       struct sockaddr_pn *addr;
+       GIsiVersion *version;
+       int error;
+       const void *__restrict data;
+       size_t len;
+       void *private;
+};
+typedef struct _GIsiMessage GIsiMessage;
+
+int g_isi_msg_version_major(const GIsiMessage *msg);
+int g_isi_msg_version_minor(const GIsiMessage *msg);
+int g_isi_msg_error(const GIsiMessage *msg);
+const char *g_isi_msg_strerror(const GIsiMessage *msg);
+uint8_t g_isi_msg_resource(const GIsiMessage *msg);
+uint16_t g_isi_msg_object(const GIsiMessage *msg);
+
+uint8_t g_isi_msg_id(const GIsiMessage *msg);
+uint8_t g_isi_msg_utid(const GIsiMessage *msg);
+size_t g_isi_msg_data_len(const GIsiMessage *msg);
+const void *g_isi_msg_data(const GIsiMessage *msg);
+
+gboolean g_isi_msg_data_get_byte(const GIsiMessage *msg, unsigned offset,
+                                       uint8_t *byte);
+gboolean g_isi_msg_data_get_word(const GIsiMessage *msg, unsigned offset,
+                                       uint16_t *word);
+gboolean g_isi_msg_data_get_struct(const GIsiMessage *msg, unsigned offset,
+                                       const void **type, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_MESSAGE_H */
diff --git a/gisi/modem.c b/gisi/modem.c
new file mode 100644 (file)
index 0000000..aee24af
--- /dev/null
@@ -0,0 +1,1257 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <net/if.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "message.h"
+#include "common.h"
+#include "modem.h"
+#include "socket.h"
+
+#define ISIDBG(m, fmt, ...)                            \
+       if ((m) != NULL && (m)->debug != NULL)          \
+               m->debug("gisi: "fmt, ##__VA_ARGS__);
+
+struct _GIsiServiceMux {
+       GIsiModem *modem;
+       GSList *pending;
+       GIsiVersion version;
+       uint8_t resource;
+       uint8_t last_utid;
+       uint16_t object;
+       unsigned subscriptions;
+       unsigned registrations;
+       gboolean reachable;
+       gboolean version_pending;
+};
+typedef struct _GIsiServiceMux GIsiServiceMux;
+
+struct _GIsiModem {
+       unsigned index;
+       uint8_t device;
+       GHashTable *services;
+       gboolean subs_source;
+       int req_fd;
+       int ind_fd;
+       guint req_watch;
+       guint ind_watch;
+       GIsiDebugFunc debug;
+       GIsiNotifyFunc trace;
+       void *opaque;
+       unsigned long flags;
+};
+
+struct _GIsiPending {
+       enum GIsiMessageType type;
+       GIsiServiceMux *service;
+       gpointer owner;
+       guint timeout;
+       GIsiNotifyFunc notify;
+       GDestroyNotify destroy;
+       void *data;
+       uint8_t utid;
+       uint8_t msgid;
+};
+
+static GIsiServiceMux *service_get(GIsiModem *modem, uint8_t resource)
+{
+       GIsiServiceMux *mux;
+       int key = resource;
+
+       mux = g_hash_table_lookup(modem->services, GINT_TO_POINTER(key));
+       if (mux != NULL)
+               return mux;
+
+       mux = g_try_new0(GIsiServiceMux, 1);
+       if (mux == NULL)
+               return NULL;
+
+       g_hash_table_insert(modem->services, GINT_TO_POINTER(key), mux);
+
+       mux->modem = modem;
+       mux->resource = resource;
+       mux->version.major = -1;
+       mux->version.minor = -1;
+       mux->reachable = FALSE;
+       mux->version_pending = FALSE;
+
+       return mux;
+}
+
+static gint utid_equal(gconstpointer a, gconstpointer b)
+{
+       const GIsiPending *pa = a;
+       const GIsiPending *pb = b;
+
+       return pa->utid - pb->utid;
+}
+
+static const char *pend_type_to_str(enum GIsiMessageType type)
+{
+       switch (type) {
+       case GISI_MESSAGE_TYPE_REQ:
+               return "REQ";
+       case GISI_MESSAGE_TYPE_IND:
+               return "IND";
+       case GISI_MESSAGE_TYPE_NTF:
+               return "NTF";
+       case GISI_MESSAGE_TYPE_RESP:
+               return "RESP";
+       case GISI_MESSAGE_TYPE_COMMON:
+               return "COMMON";
+       }
+       return "UNKNOWN";
+}
+
+static void pending_dispatch(GIsiPending *pend, GIsiMessage *msg)
+{
+       GIsiModem *modem;
+
+       if (pend->notify == NULL)
+               return;
+
+       modem = pend->service->modem;
+
+       ISIDBG(modem, "%s %s to %p [res=0x%02X, id=0x%02X, utid=0x%02X]",
+               g_isi_msg_strerror(msg), pend_type_to_str(pend->type), pend,
+               g_isi_msg_resource(msg), g_isi_msg_id(msg),
+               g_isi_msg_utid(msg));
+
+       pend->notify(msg, pend->data);
+}
+
+static void pending_remove_and_dispatch(GIsiPending *op, GIsiMessage *msg)
+{
+       GIsiModem *modem;
+
+       op->service->pending = g_slist_remove(op->service->pending, op);
+
+       if (op->notify == NULL || msg == NULL)
+               goto destroy;
+
+       modem = op->service->modem;
+
+       ISIDBG(modem, "%s %s to %p [res=0x%02X, id=0x%02X, utid=0x%02X]",
+               g_isi_msg_error(msg) ? g_isi_msg_strerror(msg) : "normal",
+               pend_type_to_str(op->type), op,
+               g_isi_msg_resource(msg), g_isi_msg_id(msg),
+               g_isi_msg_utid(msg));
+
+       op->notify(msg, op->data);
+
+destroy:
+       if (op->timeout > 0)
+               g_source_remove(op->timeout);
+
+       if (op->destroy != NULL)
+               op->destroy(op->data);
+
+       g_free(op);
+}
+
+static void service_dispatch(GIsiServiceMux *mux, GIsiMessage *msg,
+                               gboolean is_indication)
+{
+       uint8_t msgid = g_isi_msg_id(msg);
+       uint8_t utid = g_isi_msg_utid(msg);
+
+       GSList *l = mux->pending;
+
+       while (l != NULL) {
+               GSList *next = l->next;
+               GIsiPending *pend = l->data;
+
+               /*
+                * REQs, NTFs and INDs are dispatched on message ID.  While
+                * INDs have the unique transaction ID set to zero, NTFs
+                * typically mirror the UTID of the request that set up the
+                * session, and REQs can naturally have any transaction ID.
+                *
+                * RESPs are dispatched on unique transaction ID, explicitly
+                * ignoring the msgid.  A RESP also completes a transaction,
+                * so it needs to be removed after being notified of.
+                *
+                * Version query responses are dispatched in a similar fashion
+                * as RESPs, but based on the pending type and the message ID.
+                * Some of these may be synthesized, but nevertheless need to
+                * be removed.
+                */
+               if (pend->type < GISI_MESSAGE_TYPE_RESP
+                               && pend->msgid == msgid) {
+
+                       pending_dispatch(pend, msg);
+
+               } else if (pend->type == GISI_MESSAGE_TYPE_RESP &&
+                               !is_indication && pend->utid == utid) {
+
+                       pending_remove_and_dispatch(pend, msg);
+                       break;
+
+               } else if (pend->type == GISI_MESSAGE_TYPE_COMMON &&
+                               msgid == COMMON_MESSAGE &&
+                               pend->msgid == COMM_ISI_VERSION_GET_REQ) {
+
+                       pending_remove_and_dispatch(pend, msg);
+               }
+
+               l = next;
+       }
+}
+
+static void common_message_decode(GIsiServiceMux *mux, GIsiMessage *msg)
+{
+       uint8_t code;
+       uint8_t major;
+       uint8_t minor;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &code))
+               return;
+
+       switch (code) {
+       case COMM_ISA_ENTITY_NOT_REACHABLE_RESP:
+               mux->reachable = FALSE;
+               msg->error = ENOENT;
+               break;
+
+       case COMM_ISI_VERSION_GET_RESP:
+
+               if (g_isi_msg_data_get_byte(msg, 1, &major) &&
+                               g_isi_msg_data_get_byte(msg, 2, &minor)) {
+                       mux->version.major = major;
+                       mux->version.minor = minor;
+               }
+               /* fall through */
+
+       default:
+               /*
+                * PN_SIM doesn't support ISI version, but sends a
+                * garbage message as a response. Work around this
+                * modem wart.
+                */
+               mux->object = g_isi_msg_object(msg);
+               mux->version_pending = FALSE;
+               mux->reachable = TRUE;
+               break;
+       }
+       msg->version = &mux->version;
+}
+
+static void firewall_notify_handle(GIsiModem *modem, GIsiMessage *msg)
+{
+       uint8_t id;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &id))
+               return;
+
+       ISIDBG(modem, "firewall blocked message 0x%02X", id);
+}
+
+static gboolean isi_callback(GIOChannel *channel, GIOCondition cond,
+                               gpointer data)
+{
+       GIsiModem *modem = data;
+       int len;
+       int fd;
+
+       if (cond & (G_IO_NVAL|G_IO_HUP)) {
+               ISIDBG(modem, "Unexpected event on PhoNet channel %p", channel);
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(channel);
+       len = g_isi_phonet_peek_length(channel);
+
+       if (len > 0) {
+               struct sockaddr_pn addr;
+               uint32_t buf[(len + 3) / 4];
+
+               GIsiServiceMux *mux;
+               GIsiMessage msg;
+               unsigned key;
+
+               len = g_isi_phonet_read(channel, buf, len, &addr);
+               if (len < 2)
+                       return TRUE;
+
+               msg.addr = &addr;
+               msg.error = 0;
+               msg.data = buf;
+               msg.len = len;
+
+               if (modem->trace != NULL)
+                       modem->trace(&msg, NULL);
+
+               key = addr.spn_resource;
+               mux = g_hash_table_lookup(modem->services,
+                                               GINT_TO_POINTER(key));
+               if (mux == NULL) {
+                       /*
+                        * Unfortunately, the FW report has the wrong
+                        * resource ID in the N900 modem.
+                        */
+                       if (key == PN_FIREWALL)
+                               firewall_notify_handle(modem, &msg);
+
+                       return TRUE;
+               }
+
+               msg.version = &mux->version;
+
+               if (g_isi_msg_id(&msg) == COMMON_MESSAGE)
+                       common_message_decode(mux, &msg);
+
+               service_dispatch(mux, &msg, fd == modem->ind_fd);
+       }
+       return TRUE;
+}
+
+static gboolean modem_subs_update(gpointer data)
+{
+       GHashTableIter iter;
+       gpointer keyptr, value;
+
+       GIsiModem *modem = data;
+       gboolean legacy = modem->flags & GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE;
+       struct sockaddr_pn commgr = {
+               .spn_family = AF_PHONET,
+               .spn_resource = PN_COMMGR,
+               .spn_dev = modem->device,
+       };
+       uint8_t msg[4 + 1024] = {
+               0,      /* UTID */
+               legacy ? PNS_SUBSCRIBED_RESOURCES_IND :
+                       PNS_SUBSCRIBED_RESOURCES_EXTEND_IND,
+               0,      /* Count */
+               0,      /* Filler */
+       };
+       uint8_t count = 0;
+       size_t len;
+
+       modem->subs_source = 0;
+
+       g_hash_table_iter_init(&iter, modem->services);
+
+       while (g_hash_table_iter_next(&iter, &keyptr, &value)) {
+               GIsiServiceMux *mux = value;
+
+               if (mux->subscriptions == 0)
+                       continue;
+
+               if (legacy)
+                       msg[3 + count] = mux->resource;
+               else
+                       /* Resource field is 32bit and Little-endian */
+                       msg[4 + count * 4 + 3] = mux->resource;
+
+               count++;
+       }
+
+       len = legacy ? 3 + count : 4 + count * 4;
+       msg[2] = count;
+
+       sendto(modem->ind_fd, msg, len, MSG_NOSIGNAL, (void *) &commgr,
+               sizeof(commgr));
+
+       return FALSE;
+}
+
+static void modem_subs_update_when_idle(GIsiModem *modem)
+{
+       if (modem->subs_source > 0)
+               return;
+
+       modem->subs_source = g_idle_add(modem_subs_update, modem);
+}
+
+static void service_name_register(GIsiServiceMux *mux)
+{
+       struct sockaddr_pn namesrv = {
+               .spn_family = AF_PHONET,
+               .spn_resource = PN_NAMESERVICE,
+               .spn_dev = mux->modem->device,
+       };
+       uint8_t msg[] = {
+               0, PNS_NAME_ADD_REQ, 0, 0,
+               0, 0, 0, mux->resource, /* 32-bit Big-Endian name */
+               0, 0,                   /* device/object */
+               0, 0,                   /* filler */
+       };
+       uint16_t object = 0;
+
+       if (ioctl(mux->modem->req_fd, SIOCPNGETOBJECT, &object) < 0) {
+               ISIDBG(mux->modem, "ioctl(SIOCPNGETOBJECT): %s",
+                       strerror(errno));
+               return;
+       }
+
+       /* Fill in the object ID */
+       msg[8] = object >> 8;
+       msg[9] = object & 0xFF;
+
+       sendto(mux->modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL,
+               (void *) &namesrv, sizeof(namesrv));
+}
+
+static void service_name_deregister(GIsiServiceMux *mux)
+{
+       struct sockaddr_pn namesrv = {
+               .spn_family = AF_PHONET,
+               .spn_resource = PN_NAMESERVICE,
+               .spn_dev = mux->modem->device,
+       };
+       const uint8_t msg[] = {
+               0, PNS_NAME_REMOVE_REQ, 0, 0,
+               0, 0, 0, mux->resource,
+       };
+
+       sendto(mux->modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL,
+               (void *) &namesrv, sizeof(namesrv));
+}
+
+static void pending_destroy(gpointer value, gpointer user)
+{
+       GIsiPending *op = value;
+
+       if (op == NULL)
+               return;
+
+       if (op->timeout > 0)
+               g_source_remove(op->timeout);
+
+       if (op->destroy != NULL)
+               op->destroy(op->data);
+
+       g_free(op);
+}
+
+static void service_finalize(gpointer value)
+{
+       GIsiServiceMux *mux = value;
+       GIsiModem *modem = mux->modem;
+
+       if (mux->subscriptions > 0)
+               modem_subs_update_when_idle(modem);
+
+       if (mux->registrations > 0)
+               service_name_deregister(mux);
+
+       g_slist_foreach(mux->pending, pending_destroy, NULL);
+       g_slist_free(mux->pending);
+       g_free(mux);
+}
+
+GIsiModem *g_isi_modem_create(unsigned index)
+{
+       GIsiModem *modem;
+       GIOChannel *inds;
+       GIOChannel *reqs;
+
+       if (index == 0) {
+               errno = ENODEV;
+               return NULL;
+       }
+
+       modem = g_try_new0(GIsiModem, 1);
+       if (modem == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       inds = g_isi_phonet_new(index);
+       reqs = g_isi_phonet_new(index);
+
+       if (inds == NULL || reqs == NULL) {
+               g_free(modem);
+               return NULL;
+       }
+
+       modem->req_fd = g_io_channel_unix_get_fd(reqs);
+       modem->req_watch = g_io_add_watch(reqs,
+                                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
+                                       isi_callback, modem);
+       modem->ind_fd = g_io_channel_unix_get_fd(inds);
+       modem->ind_watch = g_io_add_watch(inds,
+                                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
+                                       isi_callback, modem);
+
+       g_io_channel_unref(reqs);
+       g_io_channel_unref(inds);
+
+       modem->index = index;
+       modem->services = g_hash_table_new_full(g_direct_hash, NULL,
+                                               NULL, service_finalize);
+
+       return modem;
+}
+
+GIsiModem *g_isi_modem_create_by_name(const char *name)
+{
+       return g_isi_modem_create(if_nametoindex(name));
+}
+
+void *g_isi_modem_set_userdata(GIsiModem *modem, void *data)
+{
+       void *old;
+
+       if (modem == NULL)
+               return NULL;
+
+       old = modem->opaque;
+       modem->opaque = data;
+
+       return old;
+}
+
+void *g_isi_modem_get_userdata(GIsiModem *modem)
+{
+       if (modem == NULL)
+               return NULL;
+
+       return modem->opaque;
+}
+
+unsigned long g_isi_modem_flags(GIsiModem *modem)
+{
+       if (modem == NULL)
+               return 0;
+
+       return modem->flags;
+}
+
+void g_isi_modem_set_flags(GIsiModem *modem, unsigned long flags)
+{
+       if (modem == NULL)
+               return;
+
+       modem->flags = flags;
+}
+
+uint8_t g_isi_modem_device(GIsiModem *modem)
+{
+       if (modem == NULL)
+               return 0;
+
+       return modem->device;
+}
+
+int g_isi_modem_set_device(GIsiModem *modem, uint8_t remote)
+{
+       if (modem == NULL)
+               return -EINVAL;
+
+       if (remote != PN_DEV_HOST && remote != PN_DEV_MODEM)
+               return -EINVAL;
+
+       modem->device = remote;
+
+       return 0;
+}
+
+static uint8_t service_next_utid(GIsiServiceMux *mux)
+{
+       if (mux->last_utid == 0x00 || mux->last_utid == 0xFF)
+               return 1;
+
+       return mux->last_utid + 1;
+}
+
+static void service_subs_incr(GIsiServiceMux *mux)
+{
+       GIsiModem *modem = mux->modem;
+
+       mux->subscriptions++;
+
+       if (mux->subscriptions == 1)
+               modem_subs_update_when_idle(modem);
+}
+
+static void service_subs_decr(GIsiServiceMux *mux)
+{
+       GIsiModem *modem = mux->modem;
+
+       if (mux->subscriptions == 0)
+               return;
+
+       mux->subscriptions--;
+
+       if (mux->subscriptions == 0)
+               modem_subs_update_when_idle(modem);
+}
+
+static void service_regs_incr(GIsiServiceMux *mux)
+{
+       mux->registrations++;
+
+       if (mux->registrations == 1)
+               service_name_register(mux);
+}
+
+static void service_regs_decr(GIsiServiceMux *mux)
+{
+       if (mux->registrations == 0)
+               return;
+
+       mux->registrations--;
+
+       if (mux->registrations == 0)
+               service_name_deregister(mux);
+}
+
+void g_isi_modem_destroy(GIsiModem *modem)
+{
+       if (modem == NULL)
+               return;
+
+       g_hash_table_remove_all(modem->services);
+
+       if (modem->subs_source > 0) {
+               g_source_remove(modem->subs_source);
+               modem_subs_update(modem);
+       }
+
+       g_hash_table_unref(modem->services);
+
+       if (modem->ind_watch > 0)
+               g_source_remove(modem->ind_watch);
+
+       if (modem->req_watch > 0)
+               g_source_remove(modem->req_watch);
+
+       g_free(modem);
+}
+
+unsigned g_isi_modem_index(GIsiModem *modem)
+{
+       return modem != NULL ? modem->index : 0;
+}
+
+GIsiPending *g_isi_request_send(GIsiModem *modem, uint8_t resource,
+                                       const void *__restrict buf, size_t len,
+                                       unsigned timeout, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy)
+{
+       struct sockaddr_pn dst = {
+               .spn_family = AF_PHONET,
+               .spn_resource = resource,
+               .spn_dev = modem->device,
+       };
+
+       return g_isi_request_sendto(modem, &dst, buf, len, timeout, notify,
+                                       data, destroy);
+};
+
+GIsiPending *g_isi_request_vsend(GIsiModem *modem, uint8_t resource,
+                                       const struct iovec *__restrict iov,
+                                       size_t iovlen, unsigned timeout,
+                                       GIsiNotifyFunc notify, void *data,
+                                       GDestroyNotify destroy)
+{
+       struct sockaddr_pn dst = {
+               .spn_family = AF_PHONET,
+               .spn_resource = resource,
+               .spn_dev = modem->device,
+       };
+
+       return g_isi_request_vsendto(modem, &dst, iov, iovlen, timeout, notify,
+                                       data, destroy);
+}
+
+GIsiPending *g_isi_request_sendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                                       const void *__restrict buf, size_t len,
+                                       unsigned timeout, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy)
+{
+       const struct iovec iov = {
+               .iov_base = (void *)buf,
+               .iov_len = len,
+       };
+
+       return g_isi_request_vsendto(modem, dst, &iov, 1, timeout, notify, data,
+                                       destroy);
+}
+
+static void vtrace(struct sockaddr_pn *dst,
+                       const struct iovec *__restrict iov, size_t iovlen,
+                       size_t total_len, GIsiNotifyFunc trace)
+{
+       uint8_t buffer[total_len];
+       uint8_t *ptr = buffer;
+       GIsiMessage msg = {
+               .addr = dst,
+               .data = (const void *)buffer,
+               .len = total_len,
+       };
+       size_t i;
+
+       for (i = 0; i < iovlen; i++) {
+               memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
+               ptr += iov[i].iov_len;
+       }
+
+       trace(&msg, NULL);
+}
+
+static gboolean resp_timeout(gpointer data)
+{
+       GIsiPending *op = data;
+       GIsiMessage msg = {
+               .error = ETIMEDOUT,
+       };
+
+       op->timeout = 0;
+
+       pending_remove_and_dispatch(op, &msg);
+
+       return FALSE;
+}
+
+GIsiPending *g_isi_request_vsendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                                       const struct iovec *__restrict iov,
+                                       size_t iovlen, unsigned timeout,
+                                       GIsiNotifyFunc notify, void *data,
+                                       GDestroyNotify destroy)
+{
+       struct iovec _iov[1 + iovlen];
+       struct msghdr msg = {
+               .msg_name = (void *)dst,
+               .msg_namelen = sizeof(struct sockaddr_pn),
+               .msg_iov = _iov,
+               .msg_iovlen = 1 + iovlen,
+               .msg_control = NULL,
+               .msg_controllen = 0,
+               .msg_flags = 0,
+       };
+       ssize_t ret;
+       size_t i, len;
+
+       GIsiServiceMux *mux;
+       GIsiPending *resp;
+
+       if (modem == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       mux = service_get(modem, dst->spn_resource);
+       if (mux == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       resp = g_try_new0(GIsiPending, 1);
+       if (resp == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       resp->type = GISI_MESSAGE_TYPE_RESP;
+       resp->utid = service_next_utid(mux);
+       resp->service = mux;
+       resp->notify = notify;
+       resp->destroy = destroy;
+       resp->data = data;
+
+       if (g_slist_find_custom(mux->pending, resp, utid_equal)) {
+               /*
+                * FIXME: perhaps retry with randomized access after
+                * initial miss. Although if the rate at which
+                * requests are sent is so high that the unique
+                * transaction ID wraps, it's likely there is
+                * something wrong and we might as well fail here.
+                */
+               ISIDBG(modem, "ERROR: UTID wrapped, modem busy");
+               errno = EBUSY;
+               goto error;
+       }
+
+       _iov[0].iov_base = &resp->utid;
+       _iov[0].iov_len = 1;
+
+       for (i = 0, len = 1; i < iovlen; i++) {
+               _iov[1 + i] = iov[i];
+               len += iov[i].iov_len;
+       }
+
+       if (modem->trace != NULL)
+               vtrace(dst, _iov, 1 + iovlen, len, modem->trace);
+
+       ret = sendmsg(modem->req_fd, &msg, MSG_NOSIGNAL);
+       if (ret == -1)
+               goto error;
+
+       if (ret != (ssize_t)len) {
+               errno = EMSGSIZE;
+               goto error;
+       }
+
+       mux->pending = g_slist_prepend(mux->pending, resp);
+
+       if (timeout > 0)
+               resp->timeout = g_timeout_add_seconds(timeout, resp_timeout,
+                                                       resp);
+
+       mux->last_utid = resp->utid;
+       return resp;
+
+error:
+       g_free(resp);
+       return NULL;
+}
+
+uint8_t g_isi_request_utid(GIsiPending *resp)
+{
+       return resp != NULL ? resp->utid : 0;
+}
+
+void g_isi_pending_remove(GIsiPending *op)
+{
+       if (op == NULL)
+               return;
+
+       if (op->type == GISI_MESSAGE_TYPE_IND)
+               service_subs_decr(op->service);
+
+       if (op->type == GISI_MESSAGE_TYPE_REQ)
+               service_regs_decr(op->service);
+
+       if (op->type == GISI_MESSAGE_TYPE_RESP && op->notify != NULL) {
+               GIsiMessage msg = {
+                       .error = ESHUTDOWN,
+               };
+
+               pending_remove_and_dispatch(op, &msg);
+               return;
+       }
+
+       op->service->pending = g_slist_remove(op->service->pending, op);
+
+       pending_destroy(op, NULL);
+}
+
+static void foreach_destroy(GIsiPending *op)
+{
+       if (op->type == GISI_MESSAGE_TYPE_IND)
+               service_subs_decr(op->service);
+
+       if (op->type == GISI_MESSAGE_TYPE_REQ)
+               service_regs_decr(op->service);
+
+       if (op->type == GISI_MESSAGE_TYPE_RESP && op->notify != NULL) {
+               GIsiMessage msg = {
+                       .error = ESHUTDOWN,
+               };
+
+               pending_dispatch(op, &msg);
+       }
+
+       pending_destroy(op, NULL);
+}
+
+void g_isi_pending_set_owner(GIsiPending *op, gpointer owner)
+{
+       if (op == NULL)
+               return;
+
+       op->owner = owner;
+}
+
+void g_isi_remove_pending_by_owner(GIsiModem *modem, uint8_t resource,
+                                       gpointer owner)
+{
+       GIsiServiceMux *mux;
+       GSList *l;
+       GSList *next;
+       GIsiPending *op;
+       GSList *owned = NULL;
+
+       mux = service_get(modem, resource);
+       if (mux == NULL)
+               return;
+
+       for (l = mux->pending; l != NULL; l = next) {
+               next = l->next;
+               op = l->data;
+
+               if (op->owner != owner)
+                       continue;
+
+               mux->pending = g_slist_remove_link(mux->pending, l);
+
+               l->next = owned;
+               owned = l;
+       }
+
+       for (l = owned; l != NULL; l = l->next) {
+               op = l->data;
+
+               foreach_destroy(op);
+       }
+
+       g_slist_free(owned);
+}
+
+GIsiPending *g_isi_ntf_subscribe(GIsiModem *modem, uint8_t resource,
+                                       uint8_t msgid, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy)
+{
+       GIsiServiceMux *mux;
+       GIsiPending *ntf;
+
+       mux = service_get(modem, resource);
+       if (mux == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ntf = g_try_new0(GIsiPending, 1);
+       if (ntf == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ntf->type = GISI_MESSAGE_TYPE_NTF;
+       ntf->service = mux;
+       ntf->notify = notify;
+       ntf->data = data;
+       ntf->destroy = destroy;
+       ntf->msgid = msgid;
+
+       mux->pending = g_slist_append(mux->pending, ntf);
+
+       ISIDBG(modem, "Subscribed to %s (%p) [res=0x%02X, id=0x%02X]",
+               pend_type_to_str(ntf->type), ntf, resource, msgid);
+
+       return ntf;
+}
+
+GIsiPending *g_isi_service_bind(GIsiModem *modem, uint8_t resource,
+                               uint8_t msgid, GIsiNotifyFunc notify,
+                               void *data, GDestroyNotify destroy)
+{
+       GIsiServiceMux *mux;
+       GIsiPending *srv;
+
+       mux = service_get(modem, resource);
+       if (mux == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       srv = g_try_new0(GIsiPending, 1);
+       if (srv == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       srv->type = GISI_MESSAGE_TYPE_REQ;
+       srv->service = mux;
+       srv->notify = notify;
+       srv->data = data;
+       srv->destroy = destroy;
+       srv->msgid = msgid;
+
+       mux->pending = g_slist_append(mux->pending, srv);
+
+       ISIDBG(modem, "Bound service for %s (%p) [res=0x%02X, id=0x%02X]",
+               pend_type_to_str(srv->type), srv, resource, msgid);
+
+       service_regs_incr(mux);
+
+       return srv;
+}
+
+GIsiPending *g_isi_ind_subscribe(GIsiModem *modem, uint8_t resource,
+                                       uint8_t msgid, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy)
+{
+       GIsiServiceMux *mux;
+       GIsiPending *ind;
+
+       mux = service_get(modem, resource);
+       if (mux == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ind = g_try_new0(GIsiPending, 1);
+       if (ind == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ind->type = GISI_MESSAGE_TYPE_IND;
+       ind->service = mux;
+       ind->notify = notify;
+       ind->data = data;
+       ind->destroy = destroy;
+       ind->msgid = msgid;
+
+       mux->pending = g_slist_append(mux->pending, ind);
+
+       ISIDBG(modem, "Subscribed for %s (%p) [res=0x%02X, id=0x%02X]",
+               pend_type_to_str(ind->type), ind, resource, msgid);
+
+       service_subs_incr(mux);
+
+       return ind;
+}
+
+int g_isi_response_send(GIsiModem *modem, const GIsiMessage *req,
+                               const void *__restrict buf, size_t len)
+{
+       const struct iovec iov = {
+               .iov_base = (void *)buf,
+               .iov_len = len,
+       };
+
+       return g_isi_response_vsend(modem, req, &iov, 1);
+}
+
+int g_isi_response_vsend(GIsiModem *modem, const GIsiMessage *req,
+                               const struct iovec *__restrict iov,
+                               size_t iovlen)
+{
+       struct iovec _iov[1 + iovlen];
+       uint8_t utid;
+       size_t i;
+
+       utid = g_isi_msg_utid(req);
+
+       _iov[0].iov_base = &utid;
+       _iov[0].iov_len = 1;
+
+       for (i = 0; i < iovlen; i++)
+               _iov[1 + i] = iov[i];
+
+       return g_isi_modem_vsendto(modem, req->addr, _iov, 1 + iovlen);
+}
+
+int g_isi_modem_send(GIsiModem *modem, uint8_t resource,
+                       const void *__restrict buf, size_t len)
+{
+       struct sockaddr_pn dst = {
+               .spn_family = AF_PHONET,
+               .spn_resource = resource,
+               .spn_dev = modem->device,
+       };
+
+       return g_isi_modem_sendto(modem, &dst, buf, len);
+}
+
+int g_isi_modem_vsend(GIsiModem *modem, uint8_t resource,
+                               const struct iovec *__restrict iov,
+                               size_t iovlen)
+{
+       struct sockaddr_pn dst = {
+               .spn_family = AF_PHONET,
+               .spn_resource = resource,
+               .spn_dev = modem->device,
+       };
+
+       return g_isi_modem_vsendto(modem, &dst, iov, iovlen);
+}
+
+int g_isi_modem_sendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                       const void *__restrict buf, size_t len)
+{
+       const struct iovec iov = {
+               .iov_base = (void *)buf,
+               .iov_len = len,
+       };
+
+       return g_isi_modem_vsendto(modem, dst, &iov, 1);
+}
+
+int g_isi_modem_vsendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                               const struct iovec *__restrict iov,
+                               size_t iovlen)
+{
+       struct msghdr msg = {
+               .msg_name = (void *)dst,
+               .msg_namelen = sizeof(struct sockaddr_pn),
+               .msg_iov = (struct iovec *)iov,
+               .msg_iovlen = iovlen,
+               .msg_control = NULL,
+               .msg_controllen = 0,
+               .msg_flags = 0,
+       };
+       ssize_t ret;
+       size_t i, len;
+       GIsiServiceMux *mux;
+
+       if (modem == NULL)
+               return -EINVAL;
+
+       mux = service_get(modem, dst->spn_resource);
+       if (mux == NULL)
+               return -ENOMEM;
+
+       for (i = 0, len = 0; i < iovlen; i++)
+               len += iov[i].iov_len;
+
+       if (modem->trace != NULL)
+               vtrace(dst, iov, iovlen, len, modem->trace);
+
+       ret = sendmsg(modem->req_fd, &msg, MSG_NOSIGNAL);
+       if (ret == -1)
+               return -errno;
+
+       if (ret != (ssize_t)len)
+               return -EMSGSIZE;
+
+       return 0;
+}
+
+void g_isi_modem_set_trace(GIsiModem *modem, GIsiNotifyFunc trace)
+{
+       if (modem == NULL)
+               return;
+
+       modem->trace = trace;
+}
+
+void g_isi_modem_set_debug(GIsiModem *modem, GIsiDebugFunc debug)
+{
+       if (modem == NULL)
+               return;
+
+       modem->debug = debug;
+}
+
+static int version_get_send(GIsiModem *modem, GIsiPending *ping)
+{
+       GIsiServiceMux *mux = ping->service;
+       struct sockaddr_pn dst = {
+               .spn_family = AF_PHONET,
+               .spn_resource = mux->resource,
+               .spn_dev = modem->device,
+       };
+       uint8_t msg[] = {
+               ping->utid,     /* UTID */
+               COMMON_MESSAGE,
+               COMM_ISI_VERSION_GET_REQ,
+               0,              /* Filler */
+       };
+       ssize_t ret;
+
+       if (g_slist_find_custom(mux->pending, ping, utid_equal))
+               return -EBUSY;
+
+       ret = sendto(modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL,
+                       (void *)&dst, sizeof(dst));
+
+       if (ret == -1)
+               return -errno;
+
+       if (ret != (ssize_t)sizeof(msg))
+               return -EMSGSIZE;
+
+       mux->last_utid = ping->utid;
+       mux->version_pending = TRUE;
+       return 0;
+}
+
+static gboolean reachable_notify(gpointer data)
+{
+       GIsiPending *pong = data;
+       GIsiServiceMux *mux = pong->service;
+
+       struct sockaddr_pn addr = {
+               .spn_resource = mux->resource,
+               .spn_dev = mux->object >> 8,
+               .spn_obj = mux->object & 0xff,
+       };
+       GIsiMessage msg = {
+               .version = &mux->version,
+               .addr = &addr,
+       };
+
+       pending_remove_and_dispatch(pong, &msg);
+
+       return FALSE;
+}
+
+GIsiPending *g_isi_resource_ping(GIsiModem *modem, uint8_t resource,
+                                       GIsiNotifyFunc notify, void *data,
+                                       GDestroyNotify destroy)
+{
+       GIsiServiceMux *mux;
+       GIsiPending *ping;
+       int ret;
+
+       mux = service_get(modem, resource);
+       if (mux == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ping = g_try_new0(GIsiPending, 1);
+       if (ping == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ping->type = GISI_MESSAGE_TYPE_COMMON;
+       ping->utid = service_next_utid(mux);
+       ping->service = mux;
+       ping->notify = notify;
+       ping->data = data;
+       ping->destroy = destroy;
+       ping->msgid = COMM_ISI_VERSION_GET_REQ;
+
+       if (mux->reachable) {
+               g_idle_add(reachable_notify, ping);
+               return ping;
+       }
+
+       if (!mux->version_pending) {
+               ret = version_get_send(modem, ping);
+               if (ret < 0) {
+                       g_free(ping);
+                       errno = ret;
+                       return NULL;
+               }
+               mux->last_utid = ping->utid;
+       }
+
+       ping->timeout = g_timeout_add_seconds(COMMON_TIMEOUT, resp_timeout,
+                                               ping);
+       mux->pending = g_slist_prepend(mux->pending, ping);
+       mux->version_pending = TRUE;
+
+       ISIDBG(modem, "Ping sent %s (%p) [res=0x%02X]",
+               pend_type_to_str(ping->type), ping, resource);
+
+       return ping;
+}
diff --git a/gisi/modem.h b/gisi/modem.h
new file mode 100644 (file)
index 0000000..0a741f6
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_MODEM_H
+#define __GISI_MODEM_H
+
+#include <stdint.h>
+#include <glib/gtypes.h>
+
+#include "phonet.h"
+#include "message.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum GIsiModemFlags {
+       GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE = 1,
+};
+
+struct _GIsiModem;
+typedef struct _GIsiModem GIsiModem;
+
+struct _GIsiPending;
+typedef struct _GIsiPending GIsiPending;
+
+typedef void (*GIsiNotifyFunc)(const GIsiMessage *msg, void *opaque);
+typedef void (*GIsiDebugFunc)(const char *fmt, ...);
+
+GIsiModem *g_isi_modem_create(unsigned index);
+GIsiModem *g_isi_modem_create_by_name(const char *name);
+void g_isi_modem_destroy(GIsiModem *modem);
+
+unsigned g_isi_modem_index(GIsiModem *modem);
+
+uint8_t g_isi_modem_device(GIsiModem *modem);
+int g_isi_modem_set_device(GIsiModem *modem, uint8_t dev);
+
+void g_isi_modem_set_trace(GIsiModem *modem, GIsiNotifyFunc notify);
+void g_isi_modem_set_debug(GIsiModem *modem, GIsiDebugFunc debug);
+
+void *g_isi_modem_set_userdata(GIsiModem *modem, void *data);
+void *g_isi_modem_get_userdata(GIsiModem *modem);
+
+unsigned long g_isi_modem_flags(GIsiModem *modem);
+void g_isi_modem_set_flags(GIsiModem *modem, unsigned long flags);
+
+GIsiPending *g_isi_request_send(GIsiModem *modem, uint8_t resource,
+                                       const void *__restrict buf, size_t len,
+                                       unsigned timeout, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy);
+
+GIsiPending *g_isi_request_vsend(GIsiModem *modem, uint8_t resource,
+                                       const struct iovec *__restrict iov,
+                                       size_t iovlen, unsigned timeout,
+                                       GIsiNotifyFunc notify, void *data,
+                                       GDestroyNotify destroy);
+
+GIsiPending *g_isi_request_sendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                                       const void *__restrict buf, size_t len,
+                                       unsigned timeout, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy);
+
+GIsiPending *g_isi_request_vsendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                                       const struct iovec *__restrict iov,
+                                       size_t iovlen, unsigned timeout,
+                                       GIsiNotifyFunc notify, void *data,
+                                       GDestroyNotify destroy);
+
+int g_isi_modem_send(GIsiModem *modem, uint8_t resource,
+                       const void *__restrict buf, size_t len);
+
+int g_isi_modem_vsend(GIsiModem *modem, uint8_t resource,
+                               const struct iovec *__restrict iov,
+                               size_t iovlen);
+
+int g_isi_modem_sendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                       const void *__restrict buf, size_t len);
+
+int g_isi_modem_vsendto(GIsiModem *modem, struct sockaddr_pn *dst,
+                               const struct iovec *__restrict iov,
+                               size_t iovlen);
+
+uint8_t g_isi_request_utid(GIsiPending *resp);
+
+GIsiPending *g_isi_ind_subscribe(GIsiModem *modem, uint8_t resource,
+                                       uint8_t type, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy);
+
+GIsiPending *g_isi_ntf_subscribe(GIsiModem *modem, uint8_t resource,
+                                       uint8_t type, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy);
+
+GIsiPending *g_isi_service_bind(GIsiModem *modem, uint8_t resource,
+                               uint8_t type, GIsiNotifyFunc notify,
+                               void *data, GDestroyNotify destroy);
+
+int g_isi_response_send(GIsiModem *modem, const GIsiMessage *req,
+                       const void *__restrict buf, size_t len);
+
+int g_isi_response_vsend(GIsiModem *modem, const GIsiMessage *req,
+                               const struct iovec *__restrict iov,
+                               size_t iovlen);
+
+void g_isi_pending_remove(GIsiPending *op);
+void g_isi_pending_set_owner(GIsiPending *op, gpointer owner);
+void g_isi_remove_pending_by_owner(GIsiModem *modem, uint8_t resource,
+                                       gpointer owner);
+
+GIsiPending *g_isi_resource_ping(GIsiModem *modem, uint8_t resource,
+                                       GIsiNotifyFunc notify, void *data,
+                                       GDestroyNotify destroy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_MODEM_H */
diff --git a/gisi/netlink.c b/gisi/netlink.c
new file mode 100644 (file)
index 0000000..816c481
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270 /* libc!? */
+#endif
+#include "phonet.h"
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "netlink.h"
+
+#ifndef ARPHRD_PHONET
+#define ARPHRD_PHONET (820)
+#endif
+
+/*
+ * GCC -Wcast-align does not like rtlink alignment macros,
+ * fixed macros by Andrzej Zaborowski <balrogg@gmail.com>.
+ */
+#undef IFA_RTA
+#define IFA_RTA(r)  ((struct rtattr *)(void *)(((char *)(r)) \
+       + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+
+#undef IFLA_RTA
+#define IFLA_RTA(r)  ((struct rtattr *)(void *)(((char *)(r)) \
+       + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+
+#undef NLMSG_NEXT
+#define NLMSG_NEXT(nlh, len)    ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+       (struct nlmsghdr *)(void *)(((char *)(nlh)) \
+       + NLMSG_ALIGN((nlh)->nlmsg_len)))
+
+#undef RTA_NEXT
+#define RTA_NEXT(rta, attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+       (struct rtattr *)(void *)(((char *)(rta)) \
+       + RTA_ALIGN((rta)->rta_len)))
+
+#define SIZE_NLMSG (16384)
+
+struct _GIsiPhonetNetlink {
+       GIsiModem *modem;
+       GIsiPhonetNetlinkFunc callback;
+       void *opaque;
+       guint watch;
+};
+
+static GSList *netlink_list;
+
+static void bring_up(unsigned ifindex)
+{
+       struct ifreq req = { .ifr_ifindex = ifindex, };
+       int fd = socket(PF_LOCAL, SOCK_DGRAM, 0);
+
+       if (ioctl(fd, SIOCGIFNAME, &req)
+               || ioctl(fd, SIOCGIFFLAGS, &req))
+               goto error;
+
+       req.ifr_flags |= IFF_UP | IFF_RUNNING;
+       ioctl(fd, SIOCSIFFLAGS, &req);
+error:
+       close(fd);
+}
+
+static int pn_netlink_socket(void)
+{
+       int fd;
+       int bufsize = SIZE_NLMSG;
+
+       fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       if (fd == -1)
+               return -1;
+
+       if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize))) {
+               int error = errno;
+               close(fd), fd = -1;
+               errno = error;
+       }
+
+       return fd;
+}
+
+static void pn_netlink_link(GIsiPhonetNetlink *self, struct nlmsghdr *nlh)
+{
+       const struct ifinfomsg *ifi;
+       const struct rtattr *rta;
+       int len;
+       const char *ifname = NULL;
+       enum GIsiPhonetLinkState st;
+       unsigned interface;
+
+       ifi = NLMSG_DATA(nlh);
+       len = IFA_PAYLOAD(nlh);
+
+       if (ifi->ifi_type != ARPHRD_PHONET)
+               return;
+
+       interface = g_isi_modem_index(self->modem);
+       if (interface != 0 && interface != (unsigned)ifi->ifi_index)
+               return;
+
+#define UP (IFF_UP | IFF_LOWER_UP | IFF_RUNNING)
+
+       if (nlh->nlmsg_type == RTM_DELLINK)
+               st = PN_LINK_REMOVED;
+       else if ((ifi->ifi_flags & UP) != UP)
+               st = PN_LINK_DOWN;
+       else
+               st = PN_LINK_UP;
+
+       for (rta = IFLA_RTA(ifi); RTA_OK(rta, len);
+               rta = RTA_NEXT(rta, len)) {
+
+               if (rta->rta_type == IFLA_IFNAME)
+                       ifname = RTA_DATA(rta);
+       }
+
+       if (ifname && self->modem)
+               self->callback(self->modem, st, ifname, self->opaque);
+
+#undef UP
+}
+
+/* Parser Netlink messages */
+static gboolean pn_netlink_process(GIOChannel *channel, GIOCondition cond,
+                                       gpointer data)
+{
+       struct {
+               struct nlmsghdr nlh;
+               char buf[SIZE_NLMSG];
+       } resp;
+       struct iovec iov = { &resp, sizeof(resp), };
+       struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, };
+       ssize_t ret;
+       struct nlmsghdr *nlh;
+       int fd = g_io_channel_unix_get_fd(channel);
+       GIsiPhonetNetlink *self = data;
+
+       if (cond & (G_IO_NVAL|G_IO_HUP))
+               return FALSE;
+
+       ret = recvmsg(fd, &msg, 0);
+       if (ret == -1)
+               return TRUE;
+
+       if (msg.msg_flags & MSG_TRUNC) {
+               g_printerr("Netlink message of %zu bytes truncated at %zu\n",
+                               ret, sizeof(resp));
+               return TRUE;
+       }
+
+       for (nlh = &resp.nlh; NLMSG_OK(nlh, (size_t)ret);
+                       nlh = NLMSG_NEXT(nlh, ret)) {
+
+               if (nlh->nlmsg_type == NLMSG_DONE)
+                       break;
+
+               switch (nlh->nlmsg_type) {
+               case NLMSG_ERROR: {
+                       struct nlmsgerr *err = NLMSG_DATA(nlh);
+                       if (err->error)
+                               g_printerr("Netlink error: %s",
+                                               strerror(-err->error));
+                       return TRUE;
+               }
+               case RTM_NEWLINK:
+               case RTM_DELLINK:
+                       pn_netlink_link(self, nlh);
+                       break;
+               }
+       }
+       return TRUE;
+}
+
+/* Dump current links */
+static int pn_netlink_getlink(int fd)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct ifinfomsg ifi;
+       } req = {
+               .nlh = {
+                       .nlmsg_type = RTM_GETLINK,
+                       .nlmsg_len = sizeof(req),
+                       .nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH,
+                       .nlmsg_pid = getpid(),
+               },
+               .ifi = {
+                       .ifi_family = AF_UNSPEC,
+                       .ifi_type = ARPHRD_PHONET,
+                       .ifi_change = 0xffFFffFF,
+               }
+       };
+
+       struct sockaddr_nl addr = { .nl_family = AF_NETLINK, };
+
+       return sendto(fd, &req, sizeof(req), 0,
+                     (struct sockaddr *)&addr, sizeof(addr));
+}
+
+GIsiPhonetNetlink *g_isi_pn_netlink_by_modem(GIsiModem *modem)
+{
+       GSList *m;
+
+       for (m = netlink_list; m; m = m->next) {
+               GIsiPhonetNetlink *self = m->data;
+
+               if (g_isi_modem_index(modem) == g_isi_modem_index(self->modem))
+                       return self;
+       }
+
+       return NULL;
+}
+
+GIsiPhonetNetlink *g_isi_pn_netlink_start(GIsiModem *modem,
+                                               GIsiPhonetNetlinkFunc cb,
+                                               void *data)
+{
+       GIOChannel *chan;
+       GIsiPhonetNetlink *self;
+       int fd;
+       unsigned group = RTNLGRP_LINK;
+       unsigned interface;
+
+       fd = pn_netlink_socket();
+       if (fd == -1)
+               return NULL;
+
+       self = g_try_new0(GIsiPhonetNetlink, 1);
+       if (self == NULL)
+               goto error;
+
+       fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
+
+       if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+                      &group, sizeof(group)))
+               goto error;
+
+       interface = g_isi_modem_index(modem);
+       if (interface)
+               bring_up(interface);
+
+       pn_netlink_getlink(fd);
+
+       chan = g_io_channel_unix_new(fd);
+       if (chan == NULL)
+               goto error;
+
+       g_io_channel_set_close_on_unref(chan, TRUE);
+       g_io_channel_set_encoding(chan, NULL, NULL);
+       g_io_channel_set_buffered(chan, FALSE);
+
+       self->callback = cb;
+       self->opaque = data;
+       self->modem = modem;
+       self->watch = g_io_add_watch(chan, G_IO_IN|G_IO_ERR|G_IO_HUP,
+                                       pn_netlink_process, self);
+       g_io_channel_unref(chan);
+
+       netlink_list = g_slist_prepend(netlink_list, self);
+
+       return self;
+
+error:
+       close(fd);
+       free(self);
+       return NULL;
+}
+
+void g_isi_pn_netlink_stop(GIsiPhonetNetlink *self)
+{
+       if (self == NULL)
+               return;
+
+       netlink_list = g_slist_remove(netlink_list, self);
+       g_source_remove(self->watch);
+       g_free(self);
+}
+
+static int pn_netlink_getack(int fd)
+{
+       struct {
+               struct nlmsghdr nlh;
+               char buf[SIZE_NLMSG];
+       } resp;
+       struct iovec iov = { &resp, sizeof(resp), };
+       struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, };
+       ssize_t ret;
+       struct nlmsghdr *nlh = &resp.nlh;
+
+       ret = recvmsg(fd, &msg, 0);
+       if (ret == -1)
+               return -errno;
+
+       if (msg.msg_flags & MSG_TRUNC)
+               return -EIO;
+
+       for (; NLMSG_OK(nlh, (size_t)ret); nlh = NLMSG_NEXT(nlh, ret)) {
+
+               if (nlh->nlmsg_type == NLMSG_DONE)
+                       return 0;
+
+               if (nlh->nlmsg_type == NLMSG_ERROR) {
+                       struct nlmsgerr *err = NLMSG_DATA(nlh);
+                       return err->error;
+               }
+       }
+
+       return -EIO;
+}
+
+/* Set local address */
+static int pn_netlink_setaddr(uint32_t ifa_index, uint8_t ifa_local)
+{
+       struct ifaddrmsg *ifa;
+       struct rtattr *rta;
+       uint32_t reqlen = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(*ifa))
+                               + RTA_SPACE(1));
+       struct req {
+               struct nlmsghdr nlh;
+               char buf[512];
+       } req = {
+               .nlh = {
+                       .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+                       .nlmsg_type = RTM_NEWADDR,
+                       .nlmsg_pid = getpid(),
+                       .nlmsg_len = reqlen,
+               },
+       };
+       int fd;
+       int error;
+       struct sockaddr_nl addr = { .nl_family = AF_NETLINK, };
+
+       ifa = NLMSG_DATA(&req.nlh);
+       ifa->ifa_family = AF_PHONET;
+       ifa->ifa_prefixlen = 0;
+       ifa->ifa_index = ifa_index;
+
+       rta = IFA_RTA(ifa);
+       rta->rta_type = IFA_LOCAL;
+       rta->rta_len = RTA_LENGTH(1);
+       *(uint8_t *)RTA_DATA(rta) = ifa_local;
+
+       fd = pn_netlink_socket();
+       if (fd == -1)
+               return -errno;
+
+       if (sendto(fd, &req, reqlen, 0, (void *)&addr, sizeof(addr)) == -1)
+               error = -errno;
+       else
+               error = pn_netlink_getack(fd);
+
+       close(fd);
+
+       return error;
+}
+
+int g_isi_pn_netlink_set_address(GIsiModem *modem, uint8_t local)
+{
+       uint32_t ifindex = g_isi_modem_index(modem);
+
+       if (ifindex == 0)
+               return -ENODEV;
+
+       if (local != PN_DEV_PC && local != PN_DEV_SOS)
+               return -EINVAL;
+
+       return pn_netlink_setaddr(ifindex, local);
+}
diff --git a/gisi/netlink.h b/gisi/netlink.h
new file mode 100644 (file)
index 0000000..dcf8908
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <gisi/modem.h>
+#include <gisi/common.h>
+
+#ifndef __GISI_PN_NETLINK_H
+#define __GISI_PN_NETLINK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _GIsiPhonetNetlink;
+typedef struct _GIsiPhonetNetlink GIsiPhonetNetlink;
+
+enum GIsiPhonetLinkState {
+       PN_LINK_REMOVED,
+       PN_LINK_DOWN,
+       PN_LINK_UP,
+};
+
+typedef void (*GIsiPhonetNetlinkFunc)(GIsiModem *modem,
+                                       enum GIsiPhonetLinkState st,
+                                       const char *iface, void *data);
+
+GIsiPhonetNetlink *g_isi_pn_netlink_by_modem(GIsiModem *modem);
+
+GIsiPhonetNetlink *g_isi_pn_netlink_start(GIsiModem *idx,
+                                               GIsiPhonetNetlinkFunc cb,
+                                               void *data);
+
+void g_isi_pn_netlink_stop(GIsiPhonetNetlink *self);
+int g_isi_pn_netlink_set_address(GIsiModem *modem, uint8_t local);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_PN_NETLINK_H */
diff --git a/gisi/pep.c b/gisi/pep.c
new file mode 100644 (file)
index 0000000..2f724ce
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "phonet.h"
+#include "socket.h"
+#include "pep.h"
+
+struct _GIsiPEP {
+       GIsiPEPCallback ready;
+       void *opaque;
+       int gprs_fd;
+       guint source;
+       uint16_t handle;
+};
+
+
+static gboolean g_isi_pep_callback(GIOChannel *channel, GIOCondition cond,
+                                       gpointer data)
+
+{
+       GIsiPEP *pep = data;
+       int fd = g_io_channel_unix_get_fd(channel);
+       int encap = PNPIPE_ENCAP_IP;
+
+       if (cond & (G_IO_HUP|G_IO_NVAL))
+               return FALSE;
+
+       fd = accept(fd, NULL, NULL);
+       if (fd == -1)
+               return TRUE;
+       fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+       if (setsockopt(fd, SOL_PNPIPE, PNPIPE_ENCAP, &encap, sizeof(encap))) {
+               close(fd);
+               return TRUE;
+       }
+       pep->gprs_fd = fd;
+
+       if (pep->ready != NULL)
+               pep->ready(pep, pep->opaque);
+
+       return FALSE;
+}
+
+GIsiPEP *g_isi_pep_create(GIsiModem *modem, GIsiPEPCallback cb, void *opaque)
+{
+       unsigned ifi = g_isi_modem_index(modem);
+       GIsiPEP *pep = NULL;
+       GIOChannel *channel;
+       int fd;
+       char buf[IF_NAMESIZE];
+
+       fd = socket(PF_PHONET, SOCK_SEQPACKET, 0);
+       if (fd == -1)
+               return NULL;
+
+       fcntl(fd, F_SETFD, FD_CLOEXEC);
+       fcntl(fd, F_SETFL, O_NONBLOCK|fcntl(fd, F_GETFL));
+
+       if (if_indextoname(ifi, buf) == NULL)
+               goto error;
+
+       if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, IF_NAMESIZE) != 0)
+               goto error;
+
+       pep = g_try_malloc(sizeof(GIsiPEP));
+       if (pep == NULL)
+               goto error;
+
+       pep->ready = cb;
+       pep->opaque = opaque;
+       pep->gprs_fd = -1;
+       pep->handle = 0;
+
+       if (listen(fd, 1) || ioctl(fd, SIOCPNGETOBJECT, &pep->handle))
+               goto error;
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+       pep->source = g_io_add_watch(channel,
+                                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
+                                       g_isi_pep_callback, pep);
+       g_io_channel_unref(channel);
+
+       return pep;
+
+error:
+       close(fd);
+       g_free(pep);
+       return NULL;
+}
+
+uint16_t g_isi_pep_get_object(const GIsiPEP *pep)
+{
+       return pep->handle;
+}
+
+void g_isi_pep_destroy(GIsiPEP *pep)
+{
+       if (pep->gprs_fd != -1)
+               close(pep->gprs_fd);
+       else
+               g_source_remove(pep->source);
+       g_free(pep);
+}
+
+unsigned g_isi_pep_get_ifindex(const GIsiPEP *pep)
+{
+       unsigned ifi;
+       socklen_t len = sizeof(ifi);
+
+       g_assert(pep->gprs_fd != -1);
+
+       getsockopt(pep->gprs_fd, SOL_PNPIPE, PNPIPE_IFINDEX, &ifi, &len);
+       return ifi;
+}
+
+char *g_isi_pep_get_ifname(const GIsiPEP *pep, char *ifname)
+{
+       if (pep->gprs_fd == -1)
+               return NULL;
+
+       return if_indextoname(g_isi_pep_get_ifindex(pep), ifname);
+}
diff --git a/gisi/pep.h b/gisi/pep.h
new file mode 100644 (file)
index 0000000..4057057
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_PEP_H
+#define __GISI_PEP_H
+
+#include "modem.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _GIsiPEP GIsiPEP;
+typedef void (*GIsiPEPCallback)(GIsiPEP *pep, void *opaque);
+
+GIsiPEP *g_isi_pep_create(GIsiModem *modem, GIsiPEPCallback cb, void *data);
+void g_isi_pep_destroy(GIsiPEP *pep);
+uint16_t g_isi_pep_get_object(const GIsiPEP *pep);
+unsigned g_isi_pep_get_ifindex(const GIsiPEP *pep);
+char *g_isi_pep_get_ifname(const GIsiPEP *pep, char *ifname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_PEP_H */
diff --git a/gisi/phonet.h b/gisi/phonet.h
new file mode 100644 (file)
index 0000000..edf36ec
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef NETPHONET_PHONET_H
+#define NETPHONET_PHONET_H
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#ifndef AF_PHONET
+#define AF_PHONET 35
+#define PF_PHONET AF_PHONET
+#endif
+
+#define PN_PROTO_TRANSPORT     0
+#define PN_PROTO_PHONET                1
+#define PN_PROTO_PIPE          2
+
+#define SOL_PNPIPE             275
+
+#define PNPIPE_ENCAP           1
+#define PNPIPE_IFINDEX         2
+
+#define PNPIPE_ENCAP_NONE      0
+#define PNPIPE_ENCAP_IP                1
+
+#define SIOCPNGETOBJECT                (SIOCPROTOPRIVATE + 0)
+#define SIOCPNADDRESOURCE      (SIOCPROTOPRIVATE + 14)
+#define SIOCPNDELRESOURCE      (SIOCPROTOPRIVATE + 15)
+
+struct sockaddr_pn {
+       sa_family_t spn_family;
+       uint8_t spn_obj;
+       uint8_t spn_dev;
+       uint8_t spn_resource;
+       uint8_t __pad[sizeof(struct sockaddr) - (sizeof(sa_family_t) + 3)];
+} __attribute__ ((packed));
+
+#include <linux/rtnetlink.h>
+#ifndef RTNLGRP_PHONET_IFADDR
+#define RTNLGRP_PHONET_IFADDR 21
+#endif
+
+#endif
diff --git a/gisi/pipe.c b/gisi/pipe.c
new file mode 100644 (file)
index 0000000..5c5d12f
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "client.h"
+#include "pipe.h"
+
+#define PN_PIPE                        0xD9
+#define PN_PIPE_INVALID_HANDLE 0xFF
+
+struct isi_pipe_create_req {
+       uint8_t cmd;
+       uint8_t state_after;
+       uint8_t priority;
+
+       uint8_t device1;
+       uint8_t object1;
+       uint8_t type1;
+       uint8_t pad;
+
+       uint8_t device2;
+       uint8_t object2;
+       uint8_t type2;
+       uint8_t n_sb;
+};
+
+struct isi_pipe_enable_req {
+       uint8_t cmd;
+       uint8_t pipe_handle;
+       uint8_t pad;
+};
+
+struct isi_pipe_reset_req {
+       uint8_t cmd;
+       uint8_t pipe_handle;
+       uint8_t state_after;
+};
+
+struct isi_pipe_remove_req {
+       uint8_t cmd;
+       uint8_t pipe_handle;
+};
+
+struct isi_pipe_resp {
+       uint8_t pipe_handle;
+       uint8_t error_code;
+       uint8_t error1;
+       uint8_t error2;
+};
+
+enum isi_pipe_message_id {
+       PNS_PIPE_CREATE_REQ,
+       PNS_PIPE_CREATE_RESP,
+       PNS_PIPE_REMOVE_REQ,
+       PNS_PIPE_REMOVE_RESP,
+       PNS_PIPE_RESET_REQ,
+       PNS_PIPE_RESET_RESP,
+       PNS_PIPE_ENABLE_REQ,
+       PNS_PIPE_ENABLE_RESP,
+       PNS_PIPE_REDIRECT_REQ,
+       PNS_PIPE_REDIRECT_RESP,
+       PNS_PIPE_DISABLE_REQ,
+       PNS_PIPE_DISABLE_RESP,
+};
+
+enum pn_pipe_error {   /* error codes */
+       PN_PIPE_ERR_NO_ERROR,
+       PN_PIPE_ERR_INVALID_PARAM,
+       PN_PIPE_ERR_INVALID_HANDLE,
+       PN_PIPE_ERR_INVALID_CTRL_ID,
+       PN_PIPE_ERR_NOT_ALLOWED,
+       PN_PIPE_ERR_PEP_IN_USE,
+       PN_PIPE_ERR_OVERLOAD,
+       PN_PIPE_ERR_DEV_DISCONNECTED,
+       PN_PIPE_ERR_TIMEOUT,
+       PN_PIPE_ERR_ALL_PIPES_IN_USE,
+       PN_PIPE_ERR_GENERAL,
+       PN_PIPE_ERR_NOT_SUPPORTED,
+};
+
+enum pn_pipe_state {   /* initial pipe state */
+       PN_PIPE_DISABLE,
+       PN_PIPE_ENABLE,
+};
+
+enum pn_msg_priority {
+       PN_MSG_PRIORITY_LOW = 1,
+       PN_MSG_PRIORITY_HIGH,
+};
+
+struct _GIsiPipe {
+       GIsiClient *client;
+       GIsiPipeHandler handler;
+       GIsiPipeErrorHandler error_handler;
+       void *opaque;
+       int error;
+       uint8_t handle;
+       gboolean enabled;
+       gboolean enabling;
+};
+
+static int g_isi_pipe_error(enum pn_pipe_error code)
+{
+       switch (code) {
+       case PN_PIPE_ERR_NO_ERROR:
+               return 0;
+       case PN_PIPE_ERR_INVALID_PARAM:
+               return -EINVAL;
+       case PN_PIPE_ERR_INVALID_HANDLE:
+               return -EBADF;
+       case PN_PIPE_ERR_INVALID_CTRL_ID:
+               return -ENOTSUP;
+       case PN_PIPE_ERR_NOT_ALLOWED:
+               return -EPERM;
+       case PN_PIPE_ERR_PEP_IN_USE:
+               return -EBUSY;
+       case PN_PIPE_ERR_OVERLOAD:
+               return -ENOBUFS;
+       case PN_PIPE_ERR_DEV_DISCONNECTED:
+               return -ENETDOWN;
+       case PN_PIPE_ERR_TIMEOUT:
+               return -ETIMEDOUT;
+       case PN_PIPE_ERR_ALL_PIPES_IN_USE:
+               return -ENFILE;
+       case PN_PIPE_ERR_GENERAL:
+               return -EAGAIN;
+       case PN_PIPE_ERR_NOT_SUPPORTED:
+               return -ENOSYS;
+       }
+       return -EBADMSG;
+}
+
+static void g_isi_pipe_handle_error(GIsiPipe *pipe, uint8_t code)
+{
+       int err = g_isi_pipe_error(code);
+
+       if (err == 0)
+               return;
+
+       pipe->error = err;
+
+       if (pipe->error_handler)
+               pipe->error_handler(pipe);
+}
+
+static void g_isi_pipe_created(const GIsiMessage *msg, void *data)
+{
+       struct isi_pipe_resp *resp;
+       size_t len = sizeof(struct isi_pipe_resp);
+       GIsiPipe *pipe = data;
+
+       if (g_isi_msg_error(msg) < 0) {
+               g_isi_pipe_handle_error(pipe, PN_PIPE_ERR_TIMEOUT);
+               return;
+       }
+
+       if (g_isi_msg_id(msg) != PNS_PIPE_CREATE_RESP)
+               return;
+
+       if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &resp, len))
+               return;
+
+       if (resp->pipe_handle == PN_PIPE_INVALID_HANDLE) {
+               g_isi_pipe_handle_error(pipe, resp->error_code);
+               return;
+       }
+
+       pipe->handle = resp->pipe_handle;
+
+       if (pipe->enabling)
+               g_isi_pipe_start(pipe);
+
+       if (pipe->handler)
+               pipe->handler(pipe);
+}
+
+/**
+ * Create a Phonet pipe in disabled state and with low priority.
+ * @param modem ISI modem to create a pipe with
+ * @param created optional callback for created event
+ * @param obj1 Object handle of the first end point
+ * @param obj2 Object handle of the second end point
+ * @param type1 Type of the first end point
+ * @param type2 Type of the second end point
+ * @return a pipe object on success, NULL on error.
+ */
+GIsiPipe *g_isi_pipe_create(GIsiModem *modem, GIsiPipeHandler cb, uint16_t obj1,
+                               uint16_t obj2, uint8_t type1, uint8_t type2)
+{
+       struct isi_pipe_create_req msg = {
+               .cmd = PNS_PIPE_CREATE_REQ,
+               .state_after = PN_PIPE_DISABLE,
+               .priority = PN_MSG_PRIORITY_LOW,
+               .device1 = obj1 >> 8,
+               .object1 = obj1 & 0xff,
+               .type1 = type1,
+               .device2 = obj2 >> 8,
+               .object2 = obj2 & 0xff,
+               .type2 = type2,
+               .n_sb = 0,
+       };
+       size_t len = sizeof(msg);
+       GIsiPipe *pipe;
+
+       pipe = g_try_new0(GIsiPipe, 1);
+       if (pipe == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       pipe->client = g_isi_client_create(modem, PN_PIPE);
+       if (pipe->client == NULL) {
+               errno = ENOMEM;
+               g_free(pipe);
+               return NULL;
+       }
+
+       pipe->handler = cb;
+       pipe->error_handler = NULL;
+       pipe->error = 0;
+       pipe->enabling = FALSE;
+       pipe->enabled = FALSE;
+       pipe->handle = PN_PIPE_INVALID_HANDLE;
+
+       if (g_isi_client_send(pipe->client, &msg, len,
+                                       g_isi_pipe_created, pipe, NULL))
+               return pipe;
+
+       g_isi_client_destroy(pipe->client);
+       g_free(pipe);
+
+       return NULL;
+}
+
+static void g_isi_pipe_enabled(const GIsiMessage *msg, void *data)
+{
+       GIsiPipe *pipe = data;
+       const struct isi_pipe_resp *resp;
+       size_t len = sizeof(struct isi_pipe_resp);
+
+       if (g_isi_msg_error(msg) < 0) {
+               g_isi_pipe_handle_error(pipe, PN_PIPE_ERR_TIMEOUT);
+               return;
+       }
+
+       if (g_isi_msg_id(msg) != PNS_PIPE_ENABLE_RESP)
+               return;
+
+       if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &resp, len))
+               return;
+
+       if (pipe->handle != resp->pipe_handle)
+               return;
+
+       g_isi_pipe_handle_error(pipe, resp->error_code);
+
+       pipe->enabling = FALSE;
+
+       if (!pipe->error)
+               pipe->enabled = TRUE;
+}
+
+static void g_isi_pipe_enable(GIsiPipe *pipe)
+{
+       struct isi_pipe_enable_req msg = {
+               .cmd = PNS_PIPE_ENABLE_REQ,
+               .pipe_handle = pipe->handle,
+       };
+       size_t len = sizeof(msg);
+
+       g_isi_client_send(pipe->client, &msg, len,
+                               g_isi_pipe_enabled, pipe, NULL);
+}
+
+/**
+ * Enable a pipe, i.e. turn on data transfer between the two end points.
+ * @param pipe pipe as returned from g_isi_pipe_create()
+ * @return 0 on success or an error code
+ */
+int g_isi_pipe_start(GIsiPipe *pipe)
+{
+       if (pipe->error)
+               return pipe->error;
+
+       if (pipe->enabling || pipe->enabled)
+               return 0;
+
+       if (pipe->handle != PN_PIPE_INVALID_HANDLE)
+               g_isi_pipe_enable(pipe);
+       else
+               pipe->enabling = TRUE;
+
+       return 0;
+}
+
+/* Not very useful, it will never have time to trigger */
+static void g_isi_pipe_removed(const GIsiMessage *msg, void *data)
+{
+       GIsiPipe *pipe = data;
+       struct isi_pipe_resp *resp;
+       size_t len = sizeof(struct isi_pipe_resp);
+
+       if (g_isi_msg_error(msg) < 0) {
+               g_isi_pipe_handle_error(pipe, PN_PIPE_ERR_TIMEOUT);
+               return;
+       }
+
+       if (g_isi_msg_id(msg) != PNS_PIPE_REMOVE_RESP)
+               return;
+
+       if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &resp, len))
+               return;
+
+       if (pipe->handle != resp->pipe_handle)
+               return;
+
+       pipe->handle = PN_PIPE_INVALID_HANDLE;
+       pipe->error = -EPIPE;
+}
+
+
+static void g_isi_pipe_remove(GIsiPipe *pipe)
+{
+       struct isi_pipe_remove_req msg = {
+               .cmd = PNS_PIPE_REMOVE_REQ,
+               .pipe_handle = pipe->handle,
+       };
+       size_t len = sizeof(msg);
+
+       g_isi_client_send(pipe->client, &msg, len,
+                               g_isi_pipe_removed, pipe, NULL);
+}
+
+/**
+ * Destroy a pipe. If it was connected, it is removed.
+ * @param pipe pipe as returned from g_isi_pipe_create()
+ */
+void g_isi_pipe_destroy(GIsiPipe *pipe)
+{
+       if (!pipe->error)
+               g_isi_pipe_remove(pipe);
+
+       g_isi_client_destroy(pipe->client);
+       g_free(pipe);
+}
+
+void g_isi_pipe_set_error_handler(GIsiPipe *pipe, GIsiPipeErrorHandler cb)
+{
+       pipe->error_handler = cb;
+}
+
+int g_isi_pipe_get_error(const GIsiPipe *pipe)
+{
+       return pipe->error;
+}
+
+void *g_isi_pipe_set_userdata(GIsiPipe *pipe, void *opaque)
+{
+       void *old = pipe->opaque;
+
+       pipe->opaque = opaque;
+       return old;
+}
+
+void *g_isi_pipe_get_userdata(GIsiPipe *pipe)
+{
+       return pipe->opaque;
+}
+
+/**
+ * Return a pipe handle.
+ * @param pipe a ready-made pipe with handler data present. Available
+ * after the pipe creation callback is called.
+ * @return uint8_t handle.
+ */
+
+uint8_t g_isi_pipe_get_handle(GIsiPipe *pipe)
+{
+       return pipe->handle;
+}
diff --git a/gisi/pipe.h b/gisi/pipe.h
new file mode 100644 (file)
index 0000000..f3ab2c2
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_PIPE_H
+#define __GISI_PIPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _GIsiPipe;
+typedef struct _GIsiPipe GIsiPipe;
+
+typedef void (*GIsiPipeHandler)(GIsiPipe *pipe);
+typedef void (*GIsiPipeErrorHandler)(GIsiPipe *pipe);
+
+GIsiPipe *g_isi_pipe_create(GIsiModem *modem, GIsiPipeHandler cb, uint16_t obj1,
+                               uint16_t obj2, uint8_t type1, uint8_t type2);
+void g_isi_pipe_destroy(GIsiPipe *pipe);
+
+void g_isi_pipe_set_error_handler(GIsiPipe *pipe, GIsiPipeErrorHandler cb);
+int g_isi_pipe_get_error(const GIsiPipe *pipe);
+void *g_isi_pipe_set_userdata(GIsiPipe *pipe, void *data);
+void *g_isi_pipe_get_userdata(GIsiPipe *pipe);
+uint8_t g_isi_pipe_get_handle(GIsiPipe *pipe);
+
+int g_isi_pipe_start(GIsiPipe *pipe);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_PIPE_H */
diff --git a/gisi/server.c b/gisi/server.c
new file mode 100644 (file)
index 0000000..468ce71
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glib.h>
+#include "phonet.h"
+
+#include "server.h"
+
+struct _GIsiServer {
+       GIsiModem *modem;
+       GIsiVersion version;
+       uint8_t resource;
+};
+
+uint8_t g_isi_server_resource(GIsiServer *server)
+{
+       return server != NULL ? server->resource : 0;
+}
+
+GIsiModem *g_isi_server_modem(GIsiServer *server)
+{
+       return server != NULL ? server->modem : 0;
+}
+
+GIsiServer *g_isi_server_create(GIsiModem *modem, uint8_t resource,
+                               GIsiVersion *version)
+{
+       GIsiServer *server;
+
+       if (modem == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       server  = g_try_new0(GIsiServer, 1);
+       if (server == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       if (version != NULL)
+               memcpy(&server->version, version, sizeof(GIsiVersion));
+
+       server->resource = resource;
+       server->modem = modem;
+
+       return server;
+}
+
+void g_isi_server_destroy(GIsiServer *server)
+{
+       if (server == NULL)
+               return;
+
+       g_isi_remove_pending_by_owner(server->modem, server->resource, server);
+
+       g_free(server);
+}
+
+int g_isi_server_send(GIsiServer *server, const GIsiMessage *req,
+                       const void *__restrict buf, size_t len)
+{
+       if (server == NULL)
+               return -EINVAL;
+
+       return g_isi_response_send(server->modem, req, buf, len);
+}
+
+int g_isi_server_vsend(GIsiServer *server, const GIsiMessage *req,
+                       const struct iovec *iov, size_t iovlen)
+{
+       if (server == NULL)
+               return -EINVAL;
+
+       return g_isi_response_vsend(server->modem, req, iov, iovlen);
+}
+
+GIsiPending *g_isi_server_handle(GIsiServer *server, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data)
+{
+       GIsiPending *op;
+
+       op = g_isi_service_bind(server->modem, server->resource, type,
+                               notify, data, NULL);
+
+       g_isi_pending_set_owner(op, server);
+
+       return op;
+}
diff --git a/gisi/server.h b/gisi/server.h
new file mode 100644 (file)
index 0000000..514e0dd
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GISI_SERVER_H
+#define __GISI_SERVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/uio.h>
+
+#include "message.h"
+#include "modem.h"
+
+struct _GIsiServer;
+typedef struct _GIsiServer GIsiServer;
+
+GIsiServer *g_isi_server_create(GIsiModem *modem, uint8_t resource,
+                               GIsiVersion *version);
+uint8_t g_isi_server_resource(GIsiServer *server);
+GIsiModem *g_isi_server_modem(GIsiServer *server);
+void g_isi_server_destroy(GIsiServer *server);
+
+int g_isi_server_send(GIsiServer *server, const GIsiMessage *req,
+                       const void *__restrict data, size_t len);
+
+int g_isi_server_vsend(GIsiServer *server, const GIsiMessage *req,
+                       const struct iovec *iov, size_t iovlen);
+
+GIsiPending *g_isi_server_handle(GIsiServer *server, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GISI_SERVER_H */
diff --git a/gisi/socket.c b/gisi/socket.c
new file mode 100644 (file)
index 0000000..2428f5d
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <fcntl.h>
+#include <glib.h>
+
+#include "phonet.h"
+#include "socket.h"
+
+GIOChannel *g_isi_phonet_new(unsigned ifindex)
+{
+       GIOChannel *channel;
+       struct sockaddr_pn addr = {
+               .spn_family = AF_PHONET,
+       };
+       char buf[IF_NAMESIZE];
+
+       int fd = socket(PF_PHONET, SOCK_DGRAM, 0);
+       if (fd == -1)
+               return NULL;
+
+       fcntl(fd, F_SETFD, FD_CLOEXEC);
+       /* Use blocking mode on purpose. */
+
+       if (ifindex == 0)
+               g_warning("Unspecified modem interface index");
+       else if (if_indextoname(ifindex, buf) == NULL ||
+               setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, IF_NAMESIZE))
+               goto error;
+
+       if (bind(fd, (void *)&addr, sizeof(addr)))
+               goto error;
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+       return channel;
+
+error:
+       close(fd);
+       return NULL;
+}
+
+size_t g_isi_phonet_peek_length(GIOChannel *channel)
+{
+       int len;
+       int fd = g_io_channel_unix_get_fd(channel);
+
+       return ioctl(fd, FIONREAD, &len) ? 0 : len;
+}
+
+ssize_t g_isi_phonet_read(GIOChannel *channel, void *restrict buf, size_t len,
+                               struct sockaddr_pn *addr)
+{
+       socklen_t addrlen = sizeof(struct sockaddr_pn);
+       ssize_t ret;
+
+       ret = recvfrom(g_io_channel_unix_get_fd(channel), buf, len,
+                       MSG_DONTWAIT, (void *)addr, &addrlen);
+       if (ret == -1)
+               return -1;
+
+       return ret;
+}
diff --git a/gisi/socket.h b/gisi/socket.h
new file mode 100644 (file)
index 0000000..f1877ad
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+GIOChannel *g_isi_phonet_new(unsigned int ifindex);
+size_t g_isi_phonet_peek_length(GIOChannel *io);
+ssize_t g_isi_phonet_read(GIOChannel *io, void *restrict buf, size_t len,
+                               struct sockaddr_pn *addr);
diff --git a/include/audio-settings.h b/include/audio-settings.h
new file mode 100644 (file)
index 0000000..2b6a577
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_AUDIO_SETTINGS_H
+#define __OFONO_AUDIO_SETTINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_audio_settings;
+
+struct ofono_audio_settings_driver {
+       const char *name;
+       int (*probe)(struct ofono_audio_settings *as,
+                               unsigned int vendor, void *data);
+       void (*remove)(struct ofono_audio_settings *as);
+};
+
+void ofono_audio_settings_active_notify(struct ofono_audio_settings *as,
+                                               ofono_bool_t active);
+void ofono_audio_settings_mode_notify(struct ofono_audio_settings *as,
+                                               const char *mode);
+
+int ofono_audio_settings_driver_register(const struct ofono_audio_settings_driver *d);
+void ofono_audio_settings_driver_unregister(const struct ofono_audio_settings_driver *d);
+
+struct ofono_audio_settings *ofono_audio_settings_create(struct ofono_modem *modem,
+                       unsigned int vendor, const char *driver, void *data);
+
+void ofono_audio_settings_register(struct ofono_audio_settings *as);
+void ofono_audio_settings_remove(struct ofono_audio_settings *as);
+
+void ofono_audio_settings_set_data(struct ofono_audio_settings *as, void *data);
+void *ofono_audio_settings_get_data(struct ofono_audio_settings *as);
+
+struct ofono_modem *ofono_audio_settings_get_modem(struct ofono_audio_settings *as);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_AUDIO_SETTINGS_H */
diff --git a/include/call-barring.h b/include/call-barring.h
new file mode 100644 (file)
index 0000000..9907e19
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CALL_BARRING_H
+#define __OFONO_CALL_BARRING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_call_barring;
+
+typedef void (*ofono_call_barring_set_cb_t)(const struct ofono_error *error,
+                                               void *data);
+typedef void (*ofono_call_barring_query_cb_t)(const struct ofono_error *error,
+                                       int status, void *data);
+
+struct ofono_call_barring_driver {
+       const char *name;
+       int (*probe)(struct ofono_call_barring *cb, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_call_barring *cb);
+       void (*set)(struct ofono_call_barring *barr, const char *lock,
+                       int enable, const char *passwd, int cls,
+                       ofono_call_barring_set_cb_t cb, void *data);
+       void (*query)(struct ofono_call_barring *barr, const char *lock,
+                       int cls, ofono_call_barring_query_cb_t cb, void *data);
+       void (*set_passwd)(struct ofono_call_barring *barr, const char *lock,
+                       const char *old_passwd, const char *new_passwd,
+                       ofono_call_barring_set_cb_t cb, void *data);
+};
+
+int ofono_call_barring_driver_register(const struct ofono_call_barring_driver *d);
+void ofono_call_barring_driver_unregister(const struct ofono_call_barring_driver *d);
+
+struct ofono_call_barring *ofono_call_barring_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+
+void ofono_call_barring_register(struct ofono_call_barring *cb);
+void ofono_call_barring_remove(struct ofono_call_barring *cb);
+
+void ofono_call_barring_set_data(struct ofono_call_barring *cb, void *data);
+void *ofono_call_barring_get_data(struct ofono_call_barring *cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CALL_BARRING_H */
diff --git a/include/call-forwarding.h b/include/call-forwarding.h
new file mode 100644 (file)
index 0000000..28cde06
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CALL_FORWARDING_H
+#define __OFONO_CALL_FORWARDING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_call_forwarding;
+
+/* 27.007 Section 7.11 Call Forwarding */
+struct ofono_call_forwarding_condition {
+       int status;
+       int cls;
+       struct ofono_phone_number phone_number;
+       int time;
+};
+
+typedef void (*ofono_call_forwarding_set_cb_t)(const struct ofono_error *error,
+                                               void *data);
+typedef void (*ofono_call_forwarding_query_cb_t)(const struct ofono_error *error,
+                       int total,
+                       const struct ofono_call_forwarding_condition *list,
+                       void *data);
+
+struct ofono_call_forwarding_driver {
+       const char *name;
+       int (*probe)(struct ofono_call_forwarding *cf, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_call_forwarding *cf);
+       void (*activation)(struct ofono_call_forwarding *cf,
+                               int type, int cls,
+                               ofono_call_forwarding_set_cb_t cb, void *data);
+       void (*registration)(struct ofono_call_forwarding *cf,
+                               int type, int cls,
+                               const struct ofono_phone_number *number,
+                               int time,
+                               ofono_call_forwarding_set_cb_t cb, void *data);
+       void (*deactivation)(struct ofono_call_forwarding *cf,
+                               int type, int cls,
+                               ofono_call_forwarding_set_cb_t cb, void *data);
+       void (*erasure)(struct ofono_call_forwarding *cf, int type, int cls,
+                               ofono_call_forwarding_set_cb_t cb, void *data);
+       void (*query)(struct ofono_call_forwarding *cf, int type, int cls,
+                               ofono_call_forwarding_query_cb_t cb,
+                               void *data);
+};
+
+int ofono_call_forwarding_driver_register(const struct ofono_call_forwarding_driver *d);
+void ofono_call_forwarding_driver_unregister(const struct ofono_call_forwarding_driver *d);
+
+struct ofono_call_forwarding *ofono_call_forwarding_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+
+void ofono_call_forwarding_register(struct ofono_call_forwarding *cf);
+void ofono_call_forwarding_remove(struct ofono_call_forwarding *cf);
+
+void ofono_call_forwarding_set_data(struct ofono_call_forwarding *cf,
+                                       void *data);
+void *ofono_call_forwarding_get_data(struct ofono_call_forwarding *cf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CALL_FORWARDING_H */
diff --git a/include/call-meter.h b/include/call-meter.h
new file mode 100644 (file)
index 0000000..01c24af
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CALL_METER_H
+#define __OFONO_CALL_METER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_call_meter;
+
+typedef void (*ofono_call_meter_query_cb_t)(const struct ofono_error *error,
+                                       int value, void *data);
+
+typedef void (*ofono_call_meter_puct_query_cb_t)(const struct ofono_error *error,
+                                       const char *currency, double ppu,
+                                       void *data);
+
+typedef void(*ofono_call_meter_set_cb_t)(const struct ofono_error *error,
+                                               void *data);
+
+struct ofono_call_meter_driver {
+       const char *name;
+       int (*probe)(struct ofono_call_meter *cm, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_call_meter *cm);
+       void (*call_meter_query)(struct ofono_call_meter *cm,
+                       ofono_call_meter_query_cb_t cb, void *data);
+       void (*acm_query)(struct ofono_call_meter *cm,
+                       ofono_call_meter_query_cb_t cb, void *data);
+       void (*acm_reset)(struct ofono_call_meter *cm, const char *sim_pin2,
+                       ofono_call_meter_set_cb_t cb, void *data);
+       void (*acm_max_query)(struct ofono_call_meter *cm,
+                       ofono_call_meter_query_cb_t cb, void *data);
+       void (*acm_max_set)(struct ofono_call_meter *cm, int new_value,
+                               const char *sim_pin2,
+                               ofono_call_meter_set_cb_t cb, void *data);
+       void (*puct_query)(struct ofono_call_meter *cm,
+                       ofono_call_meter_puct_query_cb_t cb, void *data);
+       void (*puct_set)(struct ofono_call_meter *cm, const char *currency,
+                       double ppu, const char *sim_pin2,
+                       ofono_call_meter_set_cb_t cb, void *data);
+};
+
+int ofono_call_meter_driver_register(const struct ofono_call_meter_driver *d);
+void ofono_call_meter_driver_unregister(const struct ofono_call_meter_driver *d);
+
+struct ofono_call_meter *ofono_call_meter_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+
+void ofono_call_meter_register(struct ofono_call_meter *cm);
+void ofono_call_meter_remove(struct ofono_call_meter *cm);
+
+void ofono_call_meter_maximum_notify(struct ofono_call_meter *cm);
+void ofono_call_meter_changed_notify(struct ofono_call_meter *cm,
+                                       int new_value);
+
+void ofono_call_meter_set_data(struct ofono_call_meter *cm, void *data);
+void *ofono_call_meter_get_data(struct ofono_call_meter *cm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CALL_METER_H */
diff --git a/include/call-settings.h b/include/call-settings.h
new file mode 100644 (file)
index 0000000..8c88eb6
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CALL_SETTINGS_H
+#define __OFONO_CALL_SETTINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_call_settings;
+
+typedef void (*ofono_call_settings_status_cb_t)(const struct ofono_error *error,
+                                               int status, void *data);
+
+typedef void (*ofono_call_settings_set_cb_t)(const struct ofono_error *error,
+                                               void *data);
+
+typedef void (*ofono_call_settings_clir_cb_t)(const struct ofono_error *error,
+                                       int override, int network, void *data);
+
+struct ofono_call_settings_driver {
+       const char *name;
+       int (*probe)(struct ofono_call_settings *cs, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_call_settings *cs);
+       void (*clip_query)(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data);
+       void (*cnap_query)(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data);
+       void (*cdip_query)(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data);
+       void (*colp_query)(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data);
+       void (*clir_query)(struct ofono_call_settings *cs,
+                               ofono_call_settings_clir_cb_t cb, void *data);
+       void (*colr_query)(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data);
+       void (*clir_set)(struct ofono_call_settings *cs, int mode,
+                               ofono_call_settings_set_cb_t cb, void *data);
+       void (*cw_query)(struct ofono_call_settings *cs, int cls,
+                       ofono_call_settings_status_cb_t cb, void *data);
+       void (*cw_set)(struct ofono_call_settings *cs, int mode, int cls,
+                       ofono_call_settings_set_cb_t cb, void *data);
+};
+
+int ofono_call_settings_driver_register(const struct ofono_call_settings_driver *d);
+void ofono_call_settings_driver_unregister(const struct ofono_call_settings_driver *d);
+
+struct ofono_call_settings *ofono_call_settings_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+
+void ofono_call_settings_register(struct ofono_call_settings *cs);
+void ofono_call_settings_remove(struct ofono_call_settings *cs);
+
+void ofono_call_settings_set_data(struct ofono_call_settings *cs, void *data);
+void *ofono_call_settings_get_data(struct ofono_call_settings *cs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CALL_SETTINGS_H */
diff --git a/include/call-volume.h b/include/call-volume.h
new file mode 100644 (file)
index 0000000..cbfcebc
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CALL_VOLUME_H
+#define __OFONO_CALL_VOLUME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+#include <ofono/dbus.h>
+
+struct ofono_call_volume;
+
+typedef void (*ofono_call_volume_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+struct ofono_call_volume_driver {
+       const char *name;
+       int (*probe)(struct ofono_call_volume *cv, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_call_volume *cv);
+       void (*speaker_volume)(struct ofono_call_volume *cv,
+                               unsigned char percent,
+                               ofono_call_volume_cb_t cb, void *data);
+       void (*microphone_volume)(struct ofono_call_volume *cv,
+                                       unsigned char percent,
+                                       ofono_call_volume_cb_t cb, void *data);
+       void (*mute)(struct ofono_call_volume *cv, int muted,
+                       ofono_call_volume_cb_t cb, void *data);
+};
+
+void ofono_call_volume_set_speaker_volume(struct ofono_call_volume *cv,
+                                       unsigned char percent);
+void ofono_call_volume_set_microphone_volume(struct ofono_call_volume *cv,
+                                               unsigned char percent);
+void ofono_call_volume_set_muted(struct ofono_call_volume *cv, int muted);
+
+int ofono_call_volume_driver_register(const struct ofono_call_volume_driver *d);
+void ofono_call_volume_driver_unregister(
+                       const struct ofono_call_volume_driver *d);
+
+struct ofono_call_volume *ofono_call_volume_create(struct ofono_modem *modem,
+                       unsigned int vendor, const char *driver, void *data);
+
+void ofono_call_volume_register(struct ofono_call_volume *cv);
+void ofono_call_volume_remove(struct ofono_call_volume *cv);
+
+void ofono_call_volume_set_data(struct ofono_call_volume *cv, void *data);
+void *ofono_call_volume_get_data(struct ofono_call_volume *cv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CALL_VOLUME_H */
diff --git a/include/cbs.h b/include/cbs.h
new file mode 100644 (file)
index 0000000..8dae3ba
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CBS_H
+#define __OFONO_CBS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_cbs;
+
+typedef void (*ofono_cbs_set_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+struct ofono_cbs_driver {
+       const char *name;
+       int (*probe)(struct ofono_cbs *cbs, unsigned int vendor, void *data);
+       void (*remove)(struct ofono_cbs *cbs);
+       void (*set_topics)(struct ofono_cbs *cbs, const char *topics,
+                               ofono_cbs_set_cb_t cb, void *data);
+       void (*clear_topics)(struct ofono_cbs *cbs,
+                               ofono_cbs_set_cb_t cb, void *data);
+};
+
+void ofono_cbs_notify(struct ofono_cbs *cbs, const unsigned char *pdu, int len);
+
+int ofono_cbs_driver_register(const struct ofono_cbs_driver *d);
+void ofono_cbs_driver_unregister(const struct ofono_cbs_driver *d);
+
+struct ofono_cbs *ofono_cbs_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_cbs_register(struct ofono_cbs *cbs);
+void ofono_cbs_remove(struct ofono_cbs *cbs);
+
+void ofono_cbs_set_data(struct ofono_cbs *cbs, void *data);
+void *ofono_cbs_get_data(struct ofono_cbs *cbs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CBS_H */
diff --git a/include/cdma-connman.h b/include/cdma-connman.h
new file mode 100644 (file)
index 0000000..3e6493e
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CDMA_CONNMAN_H
+#define __OFONO_CDMA_CONNMAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_cdma_connman;
+
+#define OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH 63
+#define OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH 255
+
+typedef void (*ofono_cdma_connman_cb_t)(const struct ofono_error *error,
+                                               void *data);
+typedef void (*ofono_cdma_connman_up_cb_t)(const struct ofono_error *error,
+                                               const char *interface,
+                                               ofono_bool_t static_ip,
+                                               const char *address,
+                                               const char *netmask,
+                                               const char *gw,
+                                               const char **dns,
+                                               void *data);
+
+struct ofono_cdma_connman_driver {
+       const char *name;
+       int (*probe)(struct ofono_cdma_connman *cm, unsigned int vendor,
+                                               void *data);
+       void (*remove)(struct ofono_cdma_connman *cm);
+       void (*activate)(struct ofono_cdma_connman *cm,
+                                               const char *username,
+                                               const char *password,
+                                               ofono_cdma_connman_up_cb_t cb,
+                                               void *data);
+       void (*deactivate)(struct ofono_cdma_connman *cm,
+                                               ofono_cdma_connman_cb_t cb,
+                                               void *data);
+};
+
+int ofono_cdma_connman_driver_register(
+                               const struct ofono_cdma_connman_driver *d);
+void ofono_cdma_connman_driver_unregister(
+                               const struct ofono_cdma_connman_driver *d);
+
+void ofono_cdma_connman_deactivated(struct ofono_cdma_connman *cm);
+
+void ofono_cdma_connman_dormant_notify(struct ofono_cdma_connman *cm,
+                                       ofono_bool_t dormant);
+
+struct ofono_cdma_connman *ofono_cdma_connman_create(
+                                               struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data);
+
+void ofono_cdma_connman_register(struct ofono_cdma_connman *cm);
+void ofono_cdma_connman_remove(struct ofono_cdma_connman *cm);
+
+void ofono_cdma_connman_set_data(struct ofono_cdma_connman *cm,
+                                               void *data);
+void *ofono_cdma_connman_get_data(struct ofono_cdma_connman *cm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CDMA_CONNMAN_H */
diff --git a/include/cdma-netreg.h b/include/cdma-netreg.h
new file mode 100644 (file)
index 0000000..529dab5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CDMA_NETREG_H
+#define __OFONO_CDMA_NETREG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+enum cdma_netreg_status {
+       CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED =       0,
+       CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED =           1,
+       CDMA_NETWORK_REGISTRATION_STATUS_ROAMING =              2,
+};
+
+struct ofono_cdma_netreg;
+
+typedef void (*ofono_cdma_netreg_serving_system_cb_t)(
+                               const struct ofono_error *error,
+                               const char *sid,
+                               void *data);
+
+struct ofono_cdma_netreg_driver {
+       const char *name;
+       int (*probe)(struct ofono_cdma_netreg *cdma_netreg,
+                               unsigned int vendor,
+                               void *data);
+       void (*remove)(struct ofono_cdma_netreg *cdma_netreg);
+       void (*serving_system)(struct ofono_cdma_netreg *cdma_netreg,
+                       ofono_cdma_netreg_serving_system_cb_t cb, void *data);
+};
+
+void ofono_cdma_netreg_status_notify(struct ofono_cdma_netreg *netreg,
+                                       enum cdma_netreg_status status);
+void ofono_cdma_netreg_strength_notify(struct ofono_cdma_netreg *netreg,
+                                       int strength);
+void ofono_cdma_netreg_data_strength_notify(struct ofono_cdma_netreg *netreg,
+                                               int data_strength);
+int ofono_cdma_netreg_get_status(struct ofono_cdma_netreg *netreg);
+
+int ofono_cdma_netreg_driver_register(
+                               const struct ofono_cdma_netreg_driver *d);
+void ofono_cdma_netreg_driver_unregister(
+                               const struct ofono_cdma_netreg_driver *d);
+
+struct ofono_cdma_netreg *ofono_cdma_netreg_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+
+void ofono_cdma_netreg_register(struct ofono_cdma_netreg *cdma_netreg);
+void ofono_cdma_netreg_remove(struct ofono_cdma_netreg *cdma_netreg);
+
+void ofono_cdma_netreg_set_data(struct ofono_cdma_netreg *cdma_netreg,
+                               void *data);
+void *ofono_cdma_netreg_get_data(struct ofono_cdma_netreg *cdma_netreg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CDMA_NETREG_H */
diff --git a/include/cdma-provision.h b/include/cdma-provision.h
new file mode 100644 (file)
index 0000000..a6d5765
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CDMA_PROVISION_H
+#define __OFONO_CDMA_PROVISION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ofono_cdma_provision_driver {
+       const char *name;
+       int priority;
+       int (*get_provider_name)(const char *sid, char **name);
+};
+
+int ofono_cdma_provision_driver_register(
+               const struct ofono_cdma_provision_driver *driver);
+void ofono_cdma_provision_driver_unregister(
+               const struct ofono_cdma_provision_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CDMA_PROVISION_H */
diff --git a/include/cdma-sms.h b/include/cdma-sms.h
new file mode 100644 (file)
index 0000000..7caa2c9
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CDMA_SMS_H
+#define __OFONO_CDMA_SMS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_cdma_sms;
+
+typedef void (*ofono_cdma_sms_submit_cb_t)(const struct ofono_error *error,
+                                               void *data);
+
+struct ofono_cdma_sms_driver {
+       const char *name;
+       int (*probe)(struct ofono_cdma_sms *cdma_sms, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_cdma_sms *cdma_sms);
+       void (*submit)(struct ofono_cdma_sms *cdma_sms, unsigned char *tpdu,
+                       int tpdu_len, ofono_cdma_sms_submit_cb_t cb,
+                       void *data);
+};
+
+void ofono_cdma_sms_deliver_notify(struct ofono_cdma_sms *cdma_sms,
+                                       unsigned char *pdu, int tpdu_len);
+
+int ofono_cdma_sms_driver_register(const struct ofono_cdma_sms_driver *d);
+void ofono_cdma_sms_driver_unregister(const struct ofono_cdma_sms_driver *d);
+
+struct ofono_cdma_sms *ofono_cdma_sms_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data);
+
+void ofono_cdma_sms_register(struct ofono_cdma_sms *cdma_sms);
+void ofono_cdma_sms_remove(struct ofono_cdma_sms *cdma_sms);
+
+void ofono_cdma_sms_set_data(struct ofono_cdma_sms *cdma_sms, void *data);
+void *ofono_cdma_sms_get_data(struct ofono_cdma_sms *cdma_sms);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CDMA_SMS_H */
diff --git a/include/cdma-voicecall.h b/include/cdma-voicecall.h
new file mode 100644 (file)
index 0000000..5a4d475
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CDMA_VOICECALL_H
+#define __OFONO_CDMA_VOICECALL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_cdma_voicecall;
+
+enum cdma_call_status {
+       CDMA_CALL_STATUS_ACTIVE,
+       CDMA_CALL_STATUS_DIALING,
+       CDMA_CALL_STATUS_ALERTING,
+       CDMA_CALL_STATUS_INCOMING,
+       CDMA_CALL_STATUS_DISCONNECTED
+};
+
+typedef void (*ofono_cdma_voicecall_cb_t)(const struct ofono_error *error,
+                                               void *data);
+
+/* Voice call related functionality, including AT+CDV, AT+CHV */
+struct ofono_cdma_voicecall_driver {
+       const char *name;
+       int (*probe)(struct ofono_cdma_voicecall *vc,
+                       unsigned int vendor, void *data);
+       void (*remove)(struct ofono_cdma_voicecall *vc);
+
+       void (*dial)(struct ofono_cdma_voicecall *vc,
+                       const struct ofono_cdma_phone_number *number,
+                       ofono_cdma_voicecall_cb_t cb, void *data);
+
+       /* Hangs up active, dialing, alerting or incoming calls */
+       void (*hangup)(struct ofono_cdma_voicecall *vc,
+                       ofono_cdma_voicecall_cb_t cb, void *data);
+
+       void (*answer)(struct ofono_cdma_voicecall *vc,
+                       ofono_cdma_voicecall_cb_t cb, void *data);
+
+       void (*send_flash)(struct ofono_cdma_voicecall *vc, const char *string,
+                       ofono_cdma_voicecall_cb_t cb, void *data);
+
+       void (*send_tones)(struct ofono_cdma_voicecall *vc, const char *tones,
+                       ofono_cdma_voicecall_cb_t cb, void *data);
+};
+
+void ofono_cdma_voicecall_disconnected(struct ofono_cdma_voicecall *vc,
+                                       enum ofono_disconnect_reason reason,
+                                       const struct ofono_error *error);
+
+int ofono_cdma_voicecall_driver_register(
+                       const struct ofono_cdma_voicecall_driver *d);
+void ofono_cdma_voicecall_driver_unregister(
+                       const struct ofono_cdma_voicecall_driver *d);
+
+struct ofono_cdma_voicecall *ofono_cdma_voicecall_create(
+                                       struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_cdma_voicecall_register(struct ofono_cdma_voicecall *vc);
+void ofono_cdma_voicecall_remove(struct ofono_cdma_voicecall *vc);
+
+void ofono_cdma_voicecall_set_data(struct ofono_cdma_voicecall *vc,
+                                       void *data);
+void *ofono_cdma_voicecall_get_data(struct ofono_cdma_voicecall *vc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CDMA_VOICECALL_H */
diff --git a/include/ctm.h b/include/ctm.h
new file mode 100644 (file)
index 0000000..76d1544
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_CTM_H
+#define __OFONO_CTM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_ctm;
+
+typedef void (*ofono_ctm_set_cb_t)(const struct ofono_error *error,
+                                       void *data);
+typedef void (*ofono_ctm_query_cb_t)(const struct ofono_error *error,
+                                       ofono_bool_t enable, void *data);
+
+struct ofono_ctm_driver {
+       const char *name;
+       int (*probe)(struct ofono_ctm *ctm, unsigned int vendor, void *data);
+       void (*remove)(struct ofono_ctm *ctm);
+       void (*query_tty)(struct ofono_ctm *ctm,
+                               ofono_ctm_query_cb_t cb, void *data);
+       void (*set_tty)(struct ofono_ctm *ctm, ofono_bool_t enable,
+                               ofono_ctm_set_cb_t cb, void *data);
+};
+
+int ofono_ctm_driver_register(const struct ofono_ctm_driver *d);
+void ofono_ctm_driver_unregister(const struct ofono_ctm_driver *d);
+
+struct ofono_ctm *ofono_ctm_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_ctm_register(struct ofono_ctm *ctm);
+void ofono_ctm_remove(struct ofono_ctm *ctm);
+
+void ofono_ctm_set_data(struct ofono_ctm *ctm, void *data);
+void *ofono_ctm_get_data(struct ofono_ctm *ctm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_CTM_H */
diff --git a/include/dbus.h b/include/dbus.h
new file mode 100644 (file)
index 0000000..44faa7f
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_DBUS_H
+#define __OFONO_DBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+
+#define OFONO_SERVICE  "org.ofono"
+#define OFONO_MANAGER_INTERFACE "org.ofono.Manager"
+#define OFONO_MANAGER_PATH "/"
+#define OFONO_MODEM_INTERFACE "org.ofono.Modem"
+#define OFONO_CALL_BARRING_INTERFACE "org.ofono.CallBarring"
+#define OFONO_CALL_FORWARDING_INTERFACE "org.ofono.CallForwarding"
+#define OFONO_CALL_METER_INTERFACE "org.ofono.CallMeter"
+#define OFONO_CALL_SETTINGS_INTERFACE "org.ofono.CallSettings"
+#define OFONO_CALL_VOLUME_INTERFACE OFONO_SERVICE ".CallVolume"
+#define OFONO_CELL_BROADCAST_INTERFACE "org.ofono.CellBroadcast"
+#define OFONO_CONNECTION_CONTEXT_INTERFACE "org.ofono.ConnectionContext"
+#define OFONO_CONNECTION_MANAGER_INTERFACE "org.ofono.ConnectionManager"
+#define OFONO_MESSAGE_MANAGER_INTERFACE "org.ofono.MessageManager"
+#define OFONO_MESSAGE_INTERFACE "org.ofono.Message"
+#define OFONO_MESSAGE_WAITING_INTERFACE "org.ofono.MessageWaiting"
+#define OFONO_SUPPLEMENTARY_SERVICES_INTERFACE "org.ofono.SupplementaryServices"
+#define OFONO_NETWORK_REGISTRATION_INTERFACE "org.ofono.NetworkRegistration"
+#define OFONO_NETWORK_OPERATOR_INTERFACE "org.ofono.NetworkOperator"
+#define OFONO_PHONEBOOK_INTERFACE "org.ofono.Phonebook"
+#define OFONO_RADIO_SETTINGS_INTERFACE "org.ofono.RadioSettings"
+#define OFONO_AUDIO_SETTINGS_INTERFACE "org.ofono.AudioSettings"
+#define OFONO_TEXT_TELEPHONY_INTERFACE "org.ofono.TextTelephony"
+#define OFONO_SIM_MANAGER_INTERFACE "org.ofono.SimManager"
+#define OFONO_VOICECALL_INTERFACE "org.ofono.VoiceCall"
+#define OFONO_VOICECALL_MANAGER_INTERFACE "org.ofono.VoiceCallManager"
+#define OFONO_STK_INTERFACE OFONO_SERVICE ".SimToolkit"
+#define OFONO_SIM_APP_INTERFACE OFONO_SERVICE ".SimToolkitAgent"
+#define OFONO_LOCATION_REPORTING_INTERFACE OFONO_SERVICE ".LocationReporting"
+#define OFONO_GNSS_INTERFACE "org.ofono.AssistedSatelliteNavigation"
+#define OFONO_GNSS_POSR_AGENT_INTERFACE "org.ofono.PositioningRequestAgent"
+#define OFONO_HANDSFREE_INTERFACE OFONO_SERVICE ".Handsfree"
+
+/* CDMA Interfaces */
+#define OFONO_CDMA_VOICECALL_MANAGER_INTERFACE "org.ofono.cdma.VoiceCallManager"
+#define OFONO_CDMA_MESSAGE_MANAGER_INTERFACE "org.ofono.cdma.MessageManager"
+#define OFONO_CDMA_CONNECTION_MANAGER_INTERFACE "org.ofono.cdma.ConnectionManager"
+#define OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE \
+                                       "org.ofono.cdma.NetworkRegistration"
+
+/* Essentially a{sv} */
+#define OFONO_PROPERTIES_ARRAY_SIGNATURE DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
+                                       DBUS_TYPE_STRING_AS_STRING \
+                                       DBUS_TYPE_VARIANT_AS_STRING \
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+
+DBusConnection *ofono_dbus_get_connection(void);
+
+void ofono_dbus_dict_append(DBusMessageIter *dict, const char *key, int type,
+                               void *value);
+
+void ofono_dbus_dict_append_array(DBusMessageIter *dict, const char *key,
+                                       int type, void *val);
+
+void ofono_dbus_dict_append_dict(DBusMessageIter *dict, const char *key,
+                                       int type, void *val);
+
+int ofono_dbus_signal_property_changed(DBusConnection *conn, const char *path,
+                                       const char *interface, const char *name,
+                                       int type, void *value);
+
+int ofono_dbus_signal_array_property_changed(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name, int type,
+                                               void *value);
+
+int ofono_dbus_signal_dict_property_changed(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name, int type,
+                                               void *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_DBUS_H */
diff --git a/include/devinfo.h b/include/devinfo.h
new file mode 100644 (file)
index 0000000..a9acce9
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_DEVINFO_H
+#define __OFONO_DEVINFO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_devinfo;
+
+typedef void (*ofono_devinfo_query_cb_t)(const struct ofono_error *error,
+                                       const char *attribute, void *data);
+
+struct ofono_devinfo_driver {
+       const char *name;
+       int (*probe)(struct ofono_devinfo *info, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_devinfo *info);
+       void (*query_manufacturer)(struct ofono_devinfo *info,
+                       ofono_devinfo_query_cb_t cb, void *data);
+       void (*query_serial)(struct ofono_devinfo *info,
+                       ofono_devinfo_query_cb_t cb, void *data);
+       void (*query_model)(struct ofono_devinfo *info,
+                       ofono_devinfo_query_cb_t cb, void *data);
+       void (*query_revision)(struct ofono_devinfo *info,
+                       ofono_devinfo_query_cb_t cb, void *data);
+};
+
+int ofono_devinfo_driver_register(const struct ofono_devinfo_driver *d);
+void ofono_devinfo_driver_unregister(const struct ofono_devinfo_driver *d);
+
+struct ofono_devinfo *ofono_devinfo_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+void ofono_devinfo_register(struct ofono_devinfo *info);
+void ofono_devinfo_remove(struct ofono_devinfo *info);
+
+void ofono_devinfo_set_data(struct ofono_devinfo *info, void *data);
+void *ofono_devinfo_get_data(struct ofono_devinfo *info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_MODEM_INFO_H */
diff --git a/include/emulator.h b/include/emulator.h
new file mode 100644 (file)
index 0000000..5cd894b
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_EMULATOR_H
+#define __OFONO_EMULATOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+#define OFONO_EMULATOR_IND_BATTERY "battchg"
+#define OFONO_EMULATOR_IND_CALL "call"
+#define OFONO_EMULATOR_IND_CALLHELD "callheld"
+#define OFONO_EMULATOR_IND_CALLSETUP "callsetup"
+#define OFONO_EMULATOR_IND_ROAMING "roam"
+#define OFONO_EMULATOR_IND_SERVICE "service"
+#define OFONO_EMULATOR_IND_SIGNAL "signal"
+
+#define OFONO_EMULATOR_CALL_INACTIVE 0
+#define OFONO_EMULATOR_CALL_ACTIVE 1
+
+#define OFONO_EMULATOR_CALLSETUP_INACTIVE 0
+#define OFONO_EMULATOR_CALLSETUP_INCOMING 1
+#define OFONO_EMULATOR_CALLSETUP_OUTGOING 2
+#define OFONO_EMULATOR_CALLSETUP_ALERTING 3
+
+#define OFONO_EMULATOR_CALLHELD_NONE 0
+#define OFONO_EMULATOR_CALLHELD_MULTIPLE 1
+#define OFONO_EMULATOR_CALLHELD_ON_HOLD 2
+
+struct ofono_emulator;
+struct ofono_emulator_request;
+
+enum ofono_emulator_type {
+       OFONO_EMULATOR_TYPE_DUN,
+       OFONO_EMULATOR_TYPE_HFP,
+};
+
+enum ofono_emulator_request_type {
+       OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY,
+       OFONO_EMULATOR_REQUEST_TYPE_QUERY,
+       OFONO_EMULATOR_REQUEST_TYPE_SUPPORT,
+       OFONO_EMULATOR_REQUEST_TYPE_SET,
+};
+
+typedef void (*ofono_emulator_request_cb_t)(struct ofono_emulator *em,
+                                       struct ofono_emulator_request *req,
+                                       void *data);
+
+struct ofono_emulator *ofono_emulator_create(struct ofono_modem *modem,
+                                               enum ofono_emulator_type type);
+
+void ofono_emulator_register(struct ofono_emulator *em, int fd);
+
+void ofono_emulator_remove(struct ofono_emulator *em);
+
+void ofono_emulator_send_final(struct ofono_emulator *em,
+                               const struct ofono_error *final);
+void ofono_emulator_send_unsolicited(struct ofono_emulator *em,
+                                       const char *result);
+void ofono_emulator_send_intermediate(struct ofono_emulator *em,
+                                       const char *result);
+void ofono_emulator_send_info(struct ofono_emulator *em, const char *line,
+                               ofono_bool_t last);
+
+ofono_bool_t ofono_emulator_add_handler(struct ofono_emulator *em,
+                                       const char *prefix,
+                                       ofono_emulator_request_cb_t cb,
+                                       void *data, ofono_destroy_func destroy);
+ofono_bool_t ofono_emulator_remove_handler(struct ofono_emulator *em,
+                                               const char *prefix);
+
+ofono_bool_t ofono_emulator_request_next_string(
+                                       struct ofono_emulator_request *req,
+                                       const char **str);
+ofono_bool_t ofono_emulator_request_next_number(
+                                       struct ofono_emulator_request *req,
+                                       int *number);
+
+const char *ofono_emulator_request_get_raw(struct ofono_emulator_request *req);
+
+enum ofono_emulator_request_type ofono_emulator_request_get_type(
+                                       struct ofono_emulator_request *req);
+
+void ofono_emulator_set_indicator(struct ofono_emulator *em,
+                                       const char *name, int value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_EMULATOR_H */
diff --git a/include/gnss.h b/include/gnss.h
new file mode 100644 (file)
index 0000000..e7fc660
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_GNSS_H
+#define __OFONO_GNSS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_gnss;
+
+typedef void (*ofono_gnss_cb_t)(const struct ofono_error *error, void *data);
+
+struct ofono_gnss_driver {
+       const char *name;
+       int (*probe)(struct ofono_gnss *gnss, unsigned int vendor, void *data);
+       void (*remove)(struct ofono_gnss *gnss);
+       void (*send_element)(struct ofono_gnss *gnss,
+                               const char *xml,
+                               ofono_gnss_cb_t cb, void *data);
+       void (*set_position_reporting)(struct ofono_gnss *gnss,
+                                       ofono_bool_t enable,
+                                       ofono_gnss_cb_t cb,
+                                       void *data);
+};
+
+void ofono_gnss_notify_posr_request(struct ofono_gnss *gnss, const char *xml);
+void ofono_gnss_notify_posr_reset(struct ofono_gnss *gnss);
+int ofono_gnss_driver_register(const struct ofono_gnss_driver *d);
+void ofono_gnss_driver_unregister(const struct ofono_gnss_driver *d);
+
+struct ofono_gnss *ofono_gnss_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_gnss_register(struct ofono_gnss *gnss);
+void ofono_gnss_remove(struct ofono_gnss *gnss);
+
+void ofono_gnss_set_data(struct ofono_gnss *gnss, void *data);
+void *ofono_gnss_get_data(struct ofono_gnss *gnss);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_GNSS_H */
diff --git a/include/gprs-context.h b/include/gprs-context.h
new file mode 100644 (file)
index 0000000..6cae9a2
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_GPRS_CONTEXT_H
+#define __OFONO_GPRS_CONTEXT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_gprs_context;
+
+#define OFONO_GPRS_MAX_APN_LENGTH 127
+#define OFONO_GPRS_MAX_USERNAME_LENGTH 63
+#define OFONO_GPRS_MAX_PASSWORD_LENGTH 255
+
+enum ofono_gprs_proto {
+       OFONO_GPRS_PROTO_IP = 0,
+       OFONO_GPRS_PROTO_IPV6,
+       OFONO_GPRS_PROTO_IPV4V6,
+};
+
+enum ofono_gprs_context_type {
+       OFONO_GPRS_CONTEXT_TYPE_ANY = 0,
+       OFONO_GPRS_CONTEXT_TYPE_INTERNET,
+       OFONO_GPRS_CONTEXT_TYPE_MMS,
+       OFONO_GPRS_CONTEXT_TYPE_WAP,
+       OFONO_GPRS_CONTEXT_TYPE_IMS,
+};
+
+struct ofono_gprs_primary_context {
+       unsigned int cid;
+       int direction;
+       char apn[OFONO_GPRS_MAX_APN_LENGTH + 1];
+       char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+       char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+       enum ofono_gprs_proto proto;
+};
+
+typedef void (*ofono_gprs_context_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+struct ofono_gprs_context_driver {
+       const char *name;
+       int (*probe)(struct ofono_gprs_context *gc, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_gprs_context *gc);
+       void (*activate_primary)(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data);
+       void (*deactivate_primary)(struct ofono_gprs_context *gc,
+                                       unsigned int id,
+                                       ofono_gprs_context_cb_t cb, void *data);
+};
+
+void ofono_gprs_context_deactivated(struct ofono_gprs_context *gc,
+                                       unsigned int id);
+
+int ofono_gprs_context_driver_register(const struct ofono_gprs_context_driver *d);
+void ofono_gprs_context_driver_unregister(const struct ofono_gprs_context_driver *d);
+
+struct ofono_gprs_context *ofono_gprs_context_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver, void *data);
+void ofono_gprs_context_remove(struct ofono_gprs_context *gc);
+
+void ofono_gprs_context_set_data(struct ofono_gprs_context *gc, void *data);
+void *ofono_gprs_context_get_data(struct ofono_gprs_context *gc);
+
+struct ofono_modem *ofono_gprs_context_get_modem(struct ofono_gprs_context *gc);
+
+void ofono_gprs_context_set_type(struct ofono_gprs_context *gc,
+                                       enum ofono_gprs_context_type type);
+
+void ofono_gprs_context_set_interface(struct ofono_gprs_context *gc,
+                                       const char *interface);
+
+void ofono_gprs_context_set_ipv4_address(struct ofono_gprs_context *gc,
+                                               const char *address,
+                                               gboolean static_ip);
+void ofono_gprs_context_set_ipv4_netmask(struct ofono_gprs_context *gc,
+                                               const char *netmask);
+void ofono_gprs_context_set_ipv4_gateway(struct ofono_gprs_context *gc,
+                                               const char *gateway);
+void ofono_gprs_context_set_ipv4_dns_servers(struct ofono_gprs_context *gc,
+                                               const char **dns);
+
+void ofono_gprs_context_set_ipv6_address(struct ofono_gprs_context *gc,
+                                               const char *address);
+void ofono_gprs_context_set_ipv6_prefix_length(struct ofono_gprs_context *gc,
+                                               unsigned char length);
+void ofono_gprs_context_set_ipv6_gateway(struct ofono_gprs_context *gc,
+                                               const char *gateway);
+void ofono_gprs_context_set_ipv6_dns_servers(struct ofono_gprs_context *gc,
+                                               const char **dns);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_GPRS_CONTEXT_H */
diff --git a/include/gprs-provision.h b/include/gprs-provision.h
new file mode 100644 (file)
index 0000000..e9eec61
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_GPRS_PROVISION_H
+#define __OFONO_GPRS_PROVISION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gprs-context.h"
+
+struct ofono_gprs_provision_data {
+       enum ofono_gprs_context_type type;
+       enum ofono_gprs_proto proto;
+       char *name;
+       char *apn;
+       char *username;
+       char *password;
+       char *message_proxy;
+       char *message_center;
+};
+
+struct ofono_gprs_provision_driver {
+       const char *name;
+       int priority;
+       int (*get_settings)(const char *mcc, const char *mnc, const char *spn,
+                               struct ofono_gprs_provision_data **settings,
+                               int *count);
+};
+
+int ofono_gprs_provision_driver_register(
+                       const struct ofono_gprs_provision_driver *driver);
+void ofono_gprs_provision_driver_unregister(
+                       const struct ofono_gprs_provision_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_GPRS_PROVISION_H */
diff --git a/include/gprs.h b/include/gprs.h
new file mode 100644 (file)
index 0000000..6c46d18
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_GPRS_H
+#define __OFONO_GPRS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_gprs;
+struct ofono_gprs_context;
+
+typedef void (*ofono_gprs_status_cb_t)(const struct ofono_error *error,
+                                               int status, void *data);
+
+typedef void (*ofono_gprs_cb_t)(const struct ofono_error *error, void *data);
+
+struct ofono_gprs_driver {
+       const char *name;
+       int (*probe)(struct ofono_gprs *gprs, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_gprs *gprs);
+       void (*set_attached)(struct ofono_gprs *gprs, int attached,
+                               ofono_gprs_cb_t cb, void *data);
+       void (*attached_status)(struct ofono_gprs *gprs,
+                                       ofono_gprs_status_cb_t cb, void *data);
+};
+
+enum gprs_suspend_cause {
+       GPRS_SUSPENDED_DETACHED,
+       GPRS_SUSPENDED_SIGNALLING,
+       GPRS_SUSPENDED_CALL,
+       GPRS_SUSPENDED_NO_COVERAGE,
+       GPRS_SUSPENDED_UNKNOWN_CAUSE,
+};
+
+void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status);
+void ofono_gprs_detached_notify(struct ofono_gprs *gprs);
+void ofono_gprs_suspend_notify(struct ofono_gprs *gprs, int cause);
+void ofono_gprs_resume_notify(struct ofono_gprs *gprs);
+void ofono_gprs_bearer_notify(struct ofono_gprs *gprs, int bearer);
+
+int ofono_gprs_driver_register(const struct ofono_gprs_driver *d);
+void ofono_gprs_driver_unregister(const struct ofono_gprs_driver *d);
+
+struct ofono_gprs *ofono_gprs_create(struct ofono_modem *modem,
+                                       unsigned int vendor, const char *driver,
+                                       void *data);
+void ofono_gprs_register(struct ofono_gprs *gprs);
+void ofono_gprs_remove(struct ofono_gprs *gprs);
+
+void ofono_gprs_set_data(struct ofono_gprs *gprs, void *data);
+void *ofono_gprs_get_data(struct ofono_gprs *gprs);
+
+void ofono_gprs_set_cid_range(struct ofono_gprs *gprs,
+                               unsigned int min, unsigned int max);
+void ofono_gprs_add_context(struct ofono_gprs *gprs,
+                               struct ofono_gprs_context *gc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_GPRS_H */
diff --git a/include/handsfree.h b/include/handsfree.h
new file mode 100644 (file)
index 0000000..1559ba1
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_HANDSFREE_H
+#define __OFONO_HANDSFREE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_handsfree;
+
+typedef void (*ofono_handsfree_cb_t)(const struct ofono_error *error,
+                                       void *data);
+typedef void (*ofono_handsfree_phone_cb_t)(const struct ofono_error *error,
+                                       const struct ofono_phone_number *number,
+                                       void *data);
+
+struct ofono_handsfree_driver {
+       const char *name;
+       int (*probe)(struct ofono_handsfree *hf, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_handsfree *hf);
+       void (*request_phone_number) (struct ofono_handsfree *hf,
+                                       ofono_handsfree_phone_cb_t cb,
+                                       void *data);
+       void (*voice_recognition)(struct ofono_handsfree *hf,
+                                       ofono_bool_t enabled,
+                                       ofono_handsfree_cb_t cb, void *data);
+};
+
+void ofono_handsfree_set_ag_features(struct ofono_handsfree *hf,
+                                       unsigned int ag_features);
+void ofono_handsfree_set_inband_ringing(struct ofono_handsfree *hf,
+                                               ofono_bool_t enabled);
+void ofono_handsfree_voice_recognition_notify(struct ofono_handsfree *hf,
+                                               ofono_bool_t enabled);
+
+int ofono_handsfree_driver_register(const struct ofono_handsfree_driver *d);
+void ofono_handsfree_driver_unregister(
+                       const struct ofono_handsfree_driver *d);
+
+struct ofono_handsfree *ofono_handsfree_create(struct ofono_modem *modem,
+                       unsigned int vendor, const char *driver, void *data);
+
+void ofono_handsfree_register(struct ofono_handsfree *hf);
+void ofono_handsfree_remove(struct ofono_handsfree *hf);
+
+void ofono_handsfree_set_data(struct ofono_handsfree *hf, void *data);
+void *ofono_handsfree_get_data(struct ofono_handsfree *hf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_HANDSFREE_H */
diff --git a/include/history.h b/include/history.h
new file mode 100644 (file)
index 0000000..cfa05fc
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_HISTORY_H
+#define __OFONO_HISTORY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+enum ofono_disconnect_reason;
+struct ofono_call;
+
+enum ofono_history_sms_status {
+       OFONO_HISTORY_SMS_STATUS_PENDING,
+       OFONO_HISTORY_SMS_STATUS_SUBMITTED,
+       OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED,
+       OFONO_HISTORY_SMS_STATUS_SUBMIT_CANCELLED,
+       OFONO_HISTORY_SMS_STATUS_DELIVERED,
+       OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED,
+};
+
+struct ofono_history_context {
+       struct ofono_history_driver *driver;
+       struct ofono_modem *modem;
+       void *data;
+};
+
+struct ofono_history_driver {
+       const char *name;
+       int (*probe)(struct ofono_history_context *context);
+       void (*remove)(struct ofono_history_context *context);
+       void (*call_ended)(struct ofono_history_context *context,
+                               const struct ofono_call *call,
+                               time_t start, time_t end);
+       void (*call_missed)(struct ofono_history_context *context,
+                               const struct ofono_call *call, time_t when);
+       void (*sms_received)(struct ofono_history_context *context,
+                               const struct ofono_uuid *uuid, const char *from,
+                               const struct tm *remote, const struct tm *local,
+                               const char *text);
+       void (*sms_send_pending)(struct ofono_history_context *context,
+                                       const struct ofono_uuid *uuid,
+                                       const char *to,
+                                       time_t when, const char *text);
+       void (*sms_send_status)(struct ofono_history_context *context,
+                                       const struct ofono_uuid *uuid,
+                                       time_t when,
+                                       enum ofono_history_sms_status status);
+};
+
+int ofono_history_driver_register(const struct ofono_history_driver *driver);
+void ofono_history_driver_unregister(const struct ofono_history_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_HISTORY_H */
diff --git a/include/location-reporting.h b/include/location-reporting.h
new file mode 100644 (file)
index 0000000..0717f71
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  Copyright (C) 2010  ProFUSION embedded systems.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_LOCATION_REPORTING_H
+#define __OFONO_LOCATION_REPORTING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_location_reporting;
+
+enum ofono_location_reporting_type {
+       OFONO_LOCATION_REPORTING_TYPE_NMEA = 0,
+};
+
+typedef void (*ofono_location_reporting_enable_cb_t)(
+                                               const struct ofono_error *error,
+                                               int fd, void *data);
+typedef void (*ofono_location_reporting_disable_cb_t)(
+                                               const struct ofono_error *error,
+                                               void *data);
+
+struct ofono_location_reporting_driver {
+       const char *name;
+       enum ofono_location_reporting_type type;
+       int (*probe)(struct ofono_location_reporting *lr, unsigned int vendor,
+                                                               void *data);
+       void (*remove)(struct ofono_location_reporting *lr);
+       void (*enable)(struct ofono_location_reporting *lr,
+                       ofono_location_reporting_enable_cb_t cb, void *data);
+       void (*disable)(struct ofono_location_reporting *lr,
+                       ofono_location_reporting_disable_cb_t cb, void *data);
+};
+
+int ofono_location_reporting_driver_register(
+                       const struct ofono_location_reporting_driver *d);
+void ofono_location_reporting_driver_unregister(
+                       const struct ofono_location_reporting_driver *d);
+
+struct ofono_location_reporting *ofono_location_reporting_create(
+                                               struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver, void *data);
+
+void ofono_location_reporting_register(struct ofono_location_reporting *lr);
+void ofono_location_reporting_remove(struct ofono_location_reporting *lr);
+
+void ofono_location_reporting_set_data(struct ofono_location_reporting *lr,
+                                                               void *data);
+void *ofono_location_reporting_get_data(struct ofono_location_reporting *lr);
+
+struct ofono_modem *ofono_location_reporting_get_modem(
+                                       struct ofono_location_reporting *lr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_LOCATION_REPORTING_H */
diff --git a/include/log.h b/include/log.h
new file mode 100644 (file)
index 0000000..ebfa540
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_LOG_H
+#define __OFONO_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:log
+ * @title: Logging premitives
+ * @short_description: Functions for logging error and debug information
+ */
+
+extern void ofono_info(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+extern void ofono_warn(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+extern void ofono_error(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+extern void ofono_debug(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+
+struct ofono_debug_desc {
+       const char *name;
+       const char *file;
+#define OFONO_DEBUG_FLAG_DEFAULT (0)
+#define OFONO_DEBUG_FLAG_PRINT   (1 << 0)
+       unsigned int flags;
+} __attribute__((aligned(8)));
+
+/**
+ * DBG:
+ * @fmt: format string
+ * @arg...: list of arguments
+ *
+ * Simple macro around ofono_debug() which also include the function
+ * name it is called in.
+ */
+#define DBG(fmt, arg...) do { \
+       static struct ofono_debug_desc __ofono_debug_desc \
+       __attribute__((used, section("__debug"), aligned(8))) = { \
+               .file = __FILE__, .flags = OFONO_DEBUG_FLAG_DEFAULT, \
+       }; \
+       if (__ofono_debug_desc.flags & OFONO_DEBUG_FLAG_PRINT) \
+               ofono_debug("%s:%s() " fmt, \
+                                       __FILE__, __FUNCTION__ , ## arg); \
+} while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_LOG_H */
diff --git a/include/message-waiting.h b/include/message-waiting.h
new file mode 100644 (file)
index 0000000..e7f337b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_MESSAGE_WAITING_H
+#define __OFONO_MESSAGE_WAITING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_message_waiting;
+
+struct ofono_message_waiting *ofono_message_waiting_create(struct ofono_modem *modem);
+void ofono_message_waiting_register(struct ofono_message_waiting *mw);
+void ofono_message_waiting_remove(struct ofono_message_waiting *mw);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_MESSAGE_WAITING_H */
diff --git a/include/modem.h b/include/modem.h
new file mode 100644 (file)
index 0000000..0ea1ec6
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_MODEM_H
+#define __OFONO_MODEM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_modem;
+
+enum ofono_modem_type {
+       OFONO_MODEM_TYPE_HARDWARE = 0,
+       OFONO_MODEM_TYPE_HFP,
+       OFONO_MODEM_TYPE_SAP,
+};
+
+void ofono_modem_add_interface(struct ofono_modem *modem,
+                               const char *interface);
+void ofono_modem_remove_interface(struct ofono_modem *modem,
+                                       const char *interface);
+
+const char *ofono_modem_get_path(struct ofono_modem *modem);
+
+void ofono_modem_set_data(struct ofono_modem *modem, void *data);
+void *ofono_modem_get_data(struct ofono_modem *modem);
+
+struct ofono_modem *ofono_modem_create(const char *name, const char *type);
+int ofono_modem_register(struct ofono_modem *modem);
+
+ofono_bool_t ofono_modem_is_registered(struct ofono_modem *modem);
+void ofono_modem_remove(struct ofono_modem *modem);
+
+void ofono_modem_reset(struct ofono_modem *modem);
+
+void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
+ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
+
+ofono_bool_t ofono_modem_get_online(struct ofono_modem *modem);
+
+ofono_bool_t ofono_modem_get_emergency_mode(struct ofono_modem *modem);
+
+void ofono_modem_set_name(struct ofono_modem *modem, const char *name);
+
+int ofono_modem_set_string(struct ofono_modem *modem,
+                               const char *key, const char *value);
+const char *ofono_modem_get_string(struct ofono_modem *modem, const char *key);
+
+int ofono_modem_set_integer(struct ofono_modem *modem,
+                               const char *key, int value);
+int ofono_modem_get_integer(struct ofono_modem *modem, const char *key);
+
+int ofono_modem_set_boolean(struct ofono_modem *modem,
+                               const char *key, ofono_bool_t value);
+ofono_bool_t ofono_modem_get_boolean(struct ofono_modem *modem,
+                                       const char *key);
+
+typedef void (*ofono_modem_online_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+struct ofono_modem_driver {
+       const char *name;
+       enum ofono_modem_type modem_type;
+
+       /* Detect existence of device and initialize any device-specific data
+        * structures */
+       int (*probe)(struct ofono_modem *modem);
+
+       /* Destroy data structures allocated during probe and cleanup */
+       void (*remove)(struct ofono_modem *modem);
+
+       /* Power up device */
+       int (*enable)(struct ofono_modem *modem);
+
+       /* Power down device */
+       int (*disable)(struct ofono_modem *modem);
+
+       /* Enable or disable cellular radio */
+       void (*set_online)(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t callback, void *data);
+
+       /* Populate the atoms available without SIM / Locked SIM */
+       void (*pre_sim)(struct ofono_modem *modem);
+
+       /* Populate the atoms that are available with SIM / Unlocked SIM*/
+       void (*post_sim)(struct ofono_modem *modem);
+
+       /* Populate the atoms available online */
+       void (*post_online)(struct ofono_modem *modem);
+};
+
+int ofono_modem_driver_register(const struct ofono_modem_driver *);
+void ofono_modem_driver_unregister(const struct ofono_modem_driver *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_MODEM_H */
diff --git a/include/netreg.h b/include/netreg.h
new file mode 100644 (file)
index 0000000..4338c14
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_NETREG_H
+#define __OFONO_NETREG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_netreg;
+
+/* Theoretical limit is 16, but each GSM char can be encoded into
+ *  * 3 UTF8 characters resulting in 16*3=48 chars
+ *   */
+#define OFONO_MAX_OPERATOR_NAME_LENGTH 63
+
+struct ofono_network_operator {
+       char name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1];
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       int status;
+       int tech;
+};
+
+typedef void (*ofono_netreg_operator_cb_t)(const struct ofono_error *error,
+                                       const struct ofono_network_operator *op,
+                                       void *data);
+
+typedef void (*ofono_netreg_register_cb_t)(const struct ofono_error *error,
+                                               void *data);
+
+typedef void (*ofono_netreg_operator_list_cb_t)(const struct ofono_error *error,
+                               int total,
+                               const struct ofono_network_operator *list,
+                               void *data);
+
+typedef void (*ofono_netreg_status_cb_t)(const struct ofono_error *error,
+                                       int status, int lac, int ci, int tech,
+                                       void *data);
+
+typedef void (*ofono_netreg_strength_cb_t)(const struct ofono_error *error,
+                                               int strength, void *data);
+
+/* Network related functions, including registration status, operator selection
+ * and signal strength indicators.
+ *
+ * It is up to the plugin to implement CSQ polling if the modem does not support
+ * vendor extensions for signal strength notification.
+ */
+struct ofono_netreg_driver {
+       const char *name;
+       int (*probe)(struct ofono_netreg *netreg, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_netreg *netreg);
+       void (*registration_status)(struct ofono_netreg *netreg,
+                       ofono_netreg_status_cb_t cb, void *data);
+       void (*current_operator)(struct ofono_netreg *netreg,
+                       ofono_netreg_operator_cb_t cb, void *data);
+       void (*list_operators)(struct ofono_netreg *netreg,
+                       ofono_netreg_operator_list_cb_t cb, void *data);
+       void (*register_auto)(struct ofono_netreg *netreg,
+                       ofono_netreg_register_cb_t cb, void *data);
+       void (*register_manual)(struct ofono_netreg *netreg,
+                               const char *mcc, const char *mnc,
+                               ofono_netreg_register_cb_t cb, void *data);
+       void (*strength)(struct ofono_netreg *netreg,
+                       ofono_netreg_strength_cb_t, void *data);
+};
+
+void ofono_netreg_strength_notify(struct ofono_netreg *netreg, int strength);
+void ofono_netreg_status_notify(struct ofono_netreg *netreg, int status,
+                                       int lac, int ci, int tech);
+void ofono_netreg_time_notify(struct ofono_netreg *netreg,
+                               struct ofono_network_time *info);
+
+int ofono_netreg_driver_register(const struct ofono_netreg_driver *d);
+void ofono_netreg_driver_unregister(const struct ofono_netreg_driver *d);
+
+struct ofono_netreg *ofono_netreg_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data);
+
+void ofono_netreg_register(struct ofono_netreg *netreg);
+void ofono_netreg_remove(struct ofono_netreg *netreg);
+
+void ofono_netreg_set_data(struct ofono_netreg *netreg, void *data);
+void *ofono_netreg_get_data(struct ofono_netreg *netreg);
+
+int ofono_netreg_get_location(struct ofono_netreg *netreg);
+int ofono_netreg_get_cellid(struct ofono_netreg *netreg);
+int ofono_netreg_get_status(struct ofono_netreg *netreg);
+int ofono_netreg_get_technology(struct ofono_netreg *netreg);
+const char *ofono_netreg_get_mcc(struct ofono_netreg *netreg);
+const char *ofono_netreg_get_mnc(struct ofono_netreg *netreg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_NETREG_H */
diff --git a/include/nettime.h b/include/nettime.h
new file mode 100644 (file)
index 0000000..0f23cc7
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_NETTIME_H
+#define __OFONO_NETTIME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ofono_network_time;
+
+struct ofono_nettime_context {
+       struct ofono_nettime_driver *driver;
+       struct ofono_modem *modem;
+       void *data;
+};
+
+struct ofono_nettime_driver {
+       const char *name;
+       int (*probe)(struct ofono_nettime_context *context);
+       void (*remove)(struct ofono_nettime_context *context);
+       void (*info_received)(struct ofono_nettime_context *context,
+                               struct ofono_network_time *info);
+};
+
+int ofono_nettime_driver_register(const struct ofono_nettime_driver *driver);
+void ofono_nettime_driver_unregister(const struct ofono_nettime_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_NETTIME_H */
diff --git a/include/phonebook.h b/include/phonebook.h
new file mode 100644 (file)
index 0000000..73780b2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_PHONEBOOK_H
+#define __OFONO_PHONEBOOK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_phonebook;
+
+typedef void (*ofono_phonebook_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+/* Export entries reports results through ofono_phonebook_entry, if an error
+ * occurs, ofono_phonebook_entry should not be called
+ */
+struct ofono_phonebook_driver {
+       const char *name;
+       int (*probe)(struct ofono_phonebook *pb, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_phonebook *pb);
+       void (*export_entries)(struct ofono_phonebook *pb, const char *storage,
+                               ofono_phonebook_cb_t cb, void *data);
+};
+
+void ofono_phonebook_entry(struct ofono_phonebook *pb, int index,
+                               const char *number, int type,
+                               const char *text, int hidden,
+                               const char *group,
+                               const char *adnumber, int adtype,
+                               const char *secondtext, const char *email,
+                               const char *sip_uri, const char *tel_uri);
+
+int ofono_phonebook_driver_register(const struct ofono_phonebook_driver *d);
+void ofono_phonebook_driver_unregister(const struct ofono_phonebook_driver *d);
+
+struct ofono_phonebook *ofono_phonebook_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+
+void ofono_phonebook_register(struct ofono_phonebook *pb);
+void ofono_phonebook_remove(struct ofono_phonebook *pb);
+
+void ofono_phonebook_set_data(struct ofono_phonebook *pb, void *data);
+void *ofono_phonebook_get_data(struct ofono_phonebook *pb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_PHONEBOOK_H */
diff --git a/include/plugin.h b/include/plugin.h
new file mode 100644 (file)
index 0000000..654fc98
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_PLUGIN_H
+#define __OFONO_PLUGIN_H
+
+#include <ofono/version.h>
+#include <ofono/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef OFONO_API_SUBJECT_TO_CHANGE
+#error "Please define OFONO_API_SUBJECT_TO_CHANGE to acknowledge your \
+understanding that oFono hasn't reached a stable API."
+#endif
+
+#define OFONO_PLUGIN_PRIORITY_LOW      -100
+#define OFONO_PLUGIN_PRIORITY_DEFAULT     0
+#define OFONO_PLUGIN_PRIORITY_HIGH      100
+
+/**
+ * SECTION:plugin
+ * @title: Plugin premitives
+ * @short_description: Functions for declaring plugins
+ */
+
+struct ofono_plugin_desc {
+       const char *name;
+       const char *description;
+       const char *version;
+       int priority;
+       int (*init) (void);
+       void (*exit) (void);
+       void *debug_start;
+       void *debug_stop;
+};
+
+/**
+ * OFONO_PLUGIN_DEFINE:
+ * @name: plugin name
+ * @description: plugin description
+ * @version: plugin version string
+ * @init: init function called on plugin loading
+ * @exit: exit function called on plugin removal
+ *
+ * Macro for defining a plugin descriptor
+ */
+#ifdef OFONO_PLUGIN_BUILTIN
+#define OFONO_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               struct ofono_plugin_desc __ofono_builtin_ ## name = { \
+                       #name, description, version, priority, init, exit \
+               };
+#else
+#define OFONO_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               extern struct ofono_debug_desc __start___debug[] \
+                               __attribute__ ((weak, visibility("hidden"))); \
+               extern struct ofono_debug_desc __stop___debug[] \
+                               __attribute__ ((weak, visibility("hidden"))); \
+               extern struct ofono_plugin_desc ofono_plugin_desc \
+                               __attribute__ ((visibility("default"))); \
+               struct ofono_plugin_desc ofono_plugin_desc = { \
+                       #name, description, version, priority, init, exit, \
+                       __start___debug, __stop___debug \
+               };
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_PLUGIN_H */
diff --git a/include/private-network.h b/include/private-network.h
new file mode 100644 (file)
index 0000000..860d40a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_PRIVATE_NETWORK_H
+#define __OFONO_PRIVATE_NETWORK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ofono_private_network_settings {
+       int fd;
+       char *server_ip;
+       char *peer_ip;
+       char *primary_dns;
+       char *secondary_dns;
+};
+
+typedef void (*ofono_private_network_cb_t)(
+                       const struct ofono_private_network_settings *settings,
+                       void *data);
+
+struct ofono_private_network_driver {
+       char *name;
+       int (*request)(ofono_private_network_cb_t cb, void *data);
+       void (*release)(int uid);
+};
+
+int ofono_private_network_driver_register(
+                       const struct ofono_private_network_driver *d);
+void ofono_private_network_driver_unregister(
+                       const struct ofono_private_network_driver *d);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_PRIVATE_NETWORK_H */
diff --git a/include/radio-settings.h b/include/radio-settings.h
new file mode 100644 (file)
index 0000000..9e74009
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_RADIO_SETTINGS_H
+#define __OFONO_RADIO_SETTINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+enum ofono_radio_access_mode {
+       OFONO_RADIO_ACCESS_MODE_ANY     = 0,
+       OFONO_RADIO_ACCESS_MODE_GSM     = 1,
+       OFONO_RADIO_ACCESS_MODE_UMTS    = 2,
+       OFONO_RADIO_ACCESS_MODE_LTE     = 3,
+};
+
+enum ofono_radio_band_gsm {
+       OFONO_RADIO_BAND_GSM_ANY,
+       OFONO_RADIO_BAND_GSM_850,
+       OFONO_RADIO_BAND_GSM_900P,
+       OFONO_RADIO_BAND_GSM_900E,
+       OFONO_RADIO_BAND_GSM_1800,
+       OFONO_RADIO_BAND_GSM_1900,
+};
+
+enum ofono_radio_band_umts {
+       OFONO_RADIO_BAND_UMTS_ANY,
+       OFONO_RADIO_BAND_UMTS_850,
+       OFONO_RADIO_BAND_UMTS_900,
+       OFONO_RADIO_BAND_UMTS_1700AWS,
+       OFONO_RADIO_BAND_UMTS_1900,
+       OFONO_RADIO_BAND_UMTS_2100,
+};
+
+struct ofono_radio_settings;
+
+typedef void (*ofono_radio_settings_rat_mode_set_cb_t)(const struct ofono_error *error,
+                                                       void *data);
+typedef void (*ofono_radio_settings_rat_mode_query_cb_t)(const struct ofono_error *error,
+                                       enum ofono_radio_access_mode mode,
+                                       void *data);
+
+typedef void (*ofono_radio_settings_band_set_cb_t)(const struct ofono_error *error,
+                                                       void *data);
+typedef void (*ofono_radio_settings_band_query_cb_t)(const struct ofono_error *error,
+                                       enum ofono_radio_band_gsm band_gsm,
+                                       enum ofono_radio_band_umts band_umts,
+                                       void *data);
+
+typedef void (*ofono_radio_settings_fast_dormancy_set_cb_t)(const struct ofono_error *error,
+                                                       void *data);
+typedef void (*ofono_radio_settings_fast_dormancy_query_cb_t)(const struct ofono_error *error,
+                                                       ofono_bool_t enable,
+                                                       void *data);
+
+struct ofono_radio_settings_driver {
+       const char *name;
+       int (*probe)(struct ofono_radio_settings *rs, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_radio_settings *rs);
+       void (*query_rat_mode)(struct ofono_radio_settings *rs,
+                               ofono_radio_settings_rat_mode_query_cb_t cb,
+                               void *data);
+       void (*set_rat_mode)(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode,
+                               ofono_radio_settings_rat_mode_set_cb_t cb,
+                               void *data);
+       void (*query_band)(struct ofono_radio_settings *rs,
+                               ofono_radio_settings_band_query_cb_t cb,
+                               void *data);
+       void (*set_band)(struct ofono_radio_settings *rs,
+                               enum ofono_radio_band_gsm band_gsm,
+                               enum ofono_radio_band_umts band_umts,
+                               ofono_radio_settings_band_set_cb_t cb,
+                               void *data);
+       void (*query_fast_dormancy)(struct ofono_radio_settings *rs,
+                       ofono_radio_settings_fast_dormancy_query_cb_t cb,
+                       void *data);
+       void (*set_fast_dormancy)(struct ofono_radio_settings *rs,
+                               ofono_bool_t enable,
+                               ofono_radio_settings_fast_dormancy_set_cb_t,
+                               void *data);
+};
+
+int ofono_radio_settings_driver_register(const struct ofono_radio_settings_driver *d);
+void ofono_radio_settings_driver_unregister(const struct ofono_radio_settings_driver *d);
+
+struct ofono_radio_settings *ofono_radio_settings_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data);
+
+void ofono_radio_settings_register(struct ofono_radio_settings *rs);
+void ofono_radio_settings_remove(struct ofono_radio_settings *rs);
+
+void ofono_radio_settings_set_data(struct ofono_radio_settings *rs, void *data);
+void *ofono_radio_settings_get_data(struct ofono_radio_settings *rs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_RADIO_SETTINGS_H */
diff --git a/include/sim-auth.h b/include/sim-auth.h
new file mode 100644 (file)
index 0000000..0a62adc
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_SIM_AUTH_H
+#define __OFONO_SIM_AUTH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_sim_auth;
+
+typedef void (*ofono_sim_list_apps_cb_t)(const struct ofono_error *error,
+                                       const unsigned char *dataobj,
+                                       int len, void *data);
+
+struct ofono_sim_auth_driver {
+       const char *name;
+       int (*probe)(struct ofono_sim_auth *sa, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_sim_auth *sa);
+
+       void (*list_apps)(struct ofono_sim_auth *sa,
+                               ofono_sim_list_apps_cb_t cb, void *data);
+};
+
+int ofono_sim_auth_driver_register(const struct ofono_sim_auth_driver *d);
+void ofono_sim_auth_driver_unregister(const struct ofono_sim_auth_driver *d);
+
+struct ofono_sim_auth *ofono_sim_auth_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver, void *data);
+
+void ofono_sim_auth_register(struct ofono_sim_auth *sa);
+void ofono_sim_auth_remove(struct ofono_sim_auth *sa);
+
+void ofono_sim_auth_set_data(struct ofono_sim_auth *sa, void *data);
+void *ofono_sim_auth_get_data(struct ofono_sim_auth *sa);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_SIM_AUTH_H */
diff --git a/include/sim.h b/include/sim.h
new file mode 100644 (file)
index 0000000..605164e
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_SIM_H
+#define __OFONO_SIM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_sim;
+struct ofono_sim_context;
+
+/* 51.011 Section 9.3 */
+enum ofono_sim_file_structure {
+       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT = 0,
+       OFONO_SIM_FILE_STRUCTURE_FIXED = 1,
+       OFONO_SIM_FILE_STRUCTURE_CYCLIC = 3
+};
+
+enum ofono_sim_password_type {
+       OFONO_SIM_PASSWORD_NONE = 0,
+       OFONO_SIM_PASSWORD_SIM_PIN,
+       OFONO_SIM_PASSWORD_PHSIM_PIN,
+       OFONO_SIM_PASSWORD_PHFSIM_PIN,
+       OFONO_SIM_PASSWORD_SIM_PIN2,
+       OFONO_SIM_PASSWORD_PHNET_PIN,
+       OFONO_SIM_PASSWORD_PHNETSUB_PIN,
+       OFONO_SIM_PASSWORD_PHSP_PIN,
+       OFONO_SIM_PASSWORD_PHCORP_PIN,
+       OFONO_SIM_PASSWORD_SIM_PUK,
+       OFONO_SIM_PASSWORD_PHFSIM_PUK,
+       OFONO_SIM_PASSWORD_SIM_PUK2,
+       OFONO_SIM_PASSWORD_PHNET_PUK,
+       OFONO_SIM_PASSWORD_PHNETSUB_PUK,
+       OFONO_SIM_PASSWORD_PHSP_PUK,
+       OFONO_SIM_PASSWORD_PHCORP_PUK,
+       OFONO_SIM_PASSWORD_INVALID,
+};
+
+enum ofono_sim_phase {
+       OFONO_SIM_PHASE_1G,
+       OFONO_SIM_PHASE_2G,
+       OFONO_SIM_PHASE_2G_PLUS,
+       OFONO_SIM_PHASE_3G,
+       OFONO_SIM_PHASE_UNKNOWN,
+};
+
+enum ofono_sim_cphs_phase {
+       OFONO_SIM_CPHS_PHASE_NONE,
+       OFONO_SIM_CPHS_PHASE_1G,
+       OFONO_SIM_CPHS_PHASE_2G,
+};
+
+enum ofono_sim_state {
+       OFONO_SIM_STATE_NOT_PRESENT,
+       OFONO_SIM_STATE_INSERTED,
+       OFONO_SIM_STATE_LOCKED_OUT,
+       OFONO_SIM_STATE_READY,
+};
+
+typedef void (*ofono_sim_file_info_cb_t)(const struct ofono_error *error,
+                                       int filelength,
+                                       enum ofono_sim_file_structure structure,
+                                       int recordlength,
+                                       const unsigned char access[3],
+                                       unsigned char file_status,
+                                       void *data);
+
+typedef void (*ofono_sim_read_cb_t)(const struct ofono_error *error,
+                                       const unsigned char *sdata, int length,
+                                       void *data);
+
+typedef void (*ofono_sim_write_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+typedef void (*ofono_sim_imsi_cb_t)(const struct ofono_error *error,
+                                       const char *imsi, void *data);
+
+typedef void (*ofono_sim_state_event_cb_t)(enum ofono_sim_state new_state,
+                                       void *data);
+
+typedef void (*ofono_sim_file_read_cb_t)(int ok, int total_length, int record,
+                                       const unsigned char *data,
+                                       int record_length, void *userdata);
+typedef void (*ofono_sim_file_changed_cb_t)(int id, void *userdata);
+
+typedef void (*ofono_sim_file_write_cb_t)(int ok, void *userdata);
+
+typedef void (*ofono_sim_passwd_cb_t)(const struct ofono_error *error,
+                                       enum ofono_sim_password_type type,
+                                       void *data);
+
+typedef void (*ofono_sim_pin_retries_cb_t)(const struct ofono_error *error,
+                       int retries[OFONO_SIM_PASSWORD_INVALID], void *data);
+
+typedef void (*ofono_sim_lock_unlock_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+typedef void (*ofono_sim_locked_cb_t)(const struct ofono_error *error,
+                                       int locked, void *data);
+
+struct ofono_sim_driver {
+       const char *name;
+       int (*probe)(struct ofono_sim *sim, unsigned int vendor, void *data);
+       void (*remove)(struct ofono_sim *sim);
+       void (*read_file_info)(struct ofono_sim *sim, int fileid,
+                       ofono_sim_file_info_cb_t cb, void *data);
+       void (*read_file_transparent)(struct ofono_sim *sim, int fileid,
+                       int start, int length,
+                       ofono_sim_read_cb_t cb, void *data);
+       void (*read_file_linear)(struct ofono_sim *sim, int fileid,
+                       int record, int length,
+                       ofono_sim_read_cb_t cb, void *data);
+       void (*read_file_cyclic)(struct ofono_sim *sim, int fileid,
+                       int record, int length,
+                       ofono_sim_read_cb_t cb, void *data);
+       void (*write_file_transparent)(struct ofono_sim *sim, int fileid,
+                       int start, int length, const unsigned char *value,
+                       ofono_sim_write_cb_t cb, void *data);
+       void (*write_file_linear)(struct ofono_sim *sim, int fileid,
+                       int record, int length, const unsigned char *value,
+                       ofono_sim_write_cb_t cb, void *data);
+       void (*write_file_cyclic)(struct ofono_sim *sim, int fileid,
+                       int length, const unsigned char *value,
+                       ofono_sim_write_cb_t cb, void *data);
+       void (*read_imsi)(struct ofono_sim *sim,
+                       ofono_sim_imsi_cb_t cb, void *data);
+       void (*query_passwd_state)(struct ofono_sim *sim,
+                       ofono_sim_passwd_cb_t cb, void *data);
+       void (*send_passwd)(struct ofono_sim *sim, const char *passwd,
+                       ofono_sim_lock_unlock_cb_t cb, void *data);
+       void (*query_pin_retries)(struct ofono_sim *sim,
+                               ofono_sim_pin_retries_cb_t cb, void *data);
+       void (*reset_passwd)(struct ofono_sim *sim, const char *puk,
+                       const char *passwd,
+                       ofono_sim_lock_unlock_cb_t cb, void *data);
+       void (*change_passwd)(struct ofono_sim *sim,
+                       enum ofono_sim_password_type type,
+                       const char *old_passwd, const char *new_passwd,
+                       ofono_sim_lock_unlock_cb_t cb, void *data);
+       void (*lock)(struct ofono_sim *sim, enum ofono_sim_password_type type,
+                       int enable, const char *passwd,
+                       ofono_sim_lock_unlock_cb_t cb, void *data);
+       void (*query_locked)(struct ofono_sim *sim,
+                       enum ofono_sim_password_type type,
+                       ofono_sim_locked_cb_t cb, void *data);
+};
+
+int ofono_sim_driver_register(const struct ofono_sim_driver *d);
+void ofono_sim_driver_unregister(const struct ofono_sim_driver *d);
+
+struct ofono_sim *ofono_sim_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_sim_register(struct ofono_sim *sim);
+void ofono_sim_remove(struct ofono_sim *sim);
+
+void ofono_sim_set_data(struct ofono_sim *sim, void *data);
+void *ofono_sim_get_data(struct ofono_sim *sim);
+
+const char *ofono_sim_get_imsi(struct ofono_sim *sim);
+const char *ofono_sim_get_mcc(struct ofono_sim *sim);
+const char *ofono_sim_get_mnc(struct ofono_sim *sim);
+const char *ofono_sim_get_spn(struct ofono_sim *sim);
+enum ofono_sim_phase ofono_sim_get_phase(struct ofono_sim *sim);
+
+enum ofono_sim_cphs_phase ofono_sim_get_cphs_phase(struct ofono_sim *sim);
+const unsigned char *ofono_sim_get_cphs_service_table(struct ofono_sim *sim);
+
+unsigned int ofono_sim_add_state_watch(struct ofono_sim *sim,
+                                       ofono_sim_state_event_cb_t cb,
+                                       void *data, ofono_destroy_func destroy);
+
+void ofono_sim_remove_state_watch(struct ofono_sim *sim, unsigned int id);
+
+enum ofono_sim_state ofono_sim_get_state(struct ofono_sim *sim);
+
+typedef void (*ofono_sim_spn_cb_t)(const char *spn, const char *dc, void *data);
+
+gboolean ofono_sim_add_spn_watch(struct ofono_sim *sim, unsigned int *id,
+                                       ofono_sim_spn_cb_t cb, void *data,
+                                       ofono_destroy_func destroy);
+
+gboolean ofono_sim_remove_spn_watch(struct ofono_sim *sim, unsigned int *id);
+
+void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted);
+
+struct ofono_sim_context *ofono_sim_context_create(struct ofono_sim *sim);
+void ofono_sim_context_free(struct ofono_sim_context *context);
+
+/* This will queue an operation to read all available records with id from the
+ * SIM.  Callback cb will be called every time a record has been read, or once
+ * if an error has occurred.  For transparent files, the callback will only
+ * be called once.
+ *
+ * Returns 0 if the request could be queued, -1 otherwise.
+ */
+int ofono_sim_read(struct ofono_sim_context *context, int id,
+                       enum ofono_sim_file_structure expected,
+                       ofono_sim_file_read_cb_t cb, void *data);
+
+int ofono_sim_write(struct ofono_sim_context *context, int id,
+                       ofono_sim_file_write_cb_t cb,
+                       enum ofono_sim_file_structure structure, int record,
+                       const unsigned char *data, int length, void *userdata);
+
+int ofono_sim_read_bytes(struct ofono_sim_context *context, int id,
+                       unsigned short offset, unsigned short num_bytes,
+                       ofono_sim_file_read_cb_t cb, void *data);
+
+unsigned int ofono_sim_add_file_watch(struct ofono_sim_context *context,
+                                       int id, ofono_sim_file_changed_cb_t cb,
+                                       void *userdata,
+                                       ofono_destroy_func destroy);
+void ofono_sim_remove_file_watch(struct ofono_sim_context *context,
+                                       unsigned int id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_SIM_H */
diff --git a/include/sms.h b/include/sms.h
new file mode 100644 (file)
index 0000000..9ecf866
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_SMS_H
+#define __OFONO_SMS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_sms;
+
+typedef void (*ofono_sms_sca_query_cb_t)(const struct ofono_error *error,
+                                       const struct ofono_phone_number *ph,
+                                       void *data);
+typedef void (*ofono_sms_submit_cb_t)(const struct ofono_error *error, int mr,
+                                       void *data);
+typedef void (*ofono_sms_sca_set_cb_t)(const struct ofono_error *error,
+                                       void *data);
+typedef void (*ofono_sms_bearer_set_cb_t)(const struct ofono_error *error,
+                                               void *data);
+typedef void (*ofono_sms_bearer_query_cb_t)(const struct ofono_error *error,
+                                               int bearer, void *data);
+
+struct ofono_sms_driver {
+       const char *name;
+       int (*probe)(struct ofono_sms *sms, unsigned int vendor, void *data);
+       void (*remove)(struct ofono_sms *sms);
+       void (*sca_query)(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb,
+                               void *data);
+       void (*sca_set)(struct ofono_sms *sms,
+                       const struct ofono_phone_number *sca,
+                       ofono_sms_sca_set_cb_t cb, void *data);
+       void (*submit)(struct ofono_sms *sms, unsigned char *pdu,
+                       int pdu_len, int tpdu_len, int mms,
+                       ofono_sms_submit_cb_t cb, void *data);
+       void (*bearer_query)(struct ofono_sms *sms,
+                               ofono_sms_bearer_query_cb_t, void *data);
+       void (*bearer_set)(struct ofono_sms *sms, int bearer,
+                               ofono_sms_bearer_set_cb_t, void *data);
+};
+
+void ofono_sms_deliver_notify(struct ofono_sms *sms, unsigned char *pdu,
+                               int len, int tpdu_len);
+void ofono_sms_status_notify(struct ofono_sms *sms, unsigned char *pdu,
+                               int len, int tpdu_len);
+
+int ofono_sms_driver_register(const struct ofono_sms_driver *d);
+void ofono_sms_driver_unregister(const struct ofono_sms_driver *d);
+
+struct ofono_sms *ofono_sms_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_sms_register(struct ofono_sms *sms);
+void ofono_sms_remove(struct ofono_sms *sms);
+
+void ofono_sms_set_data(struct ofono_sms *sms, void *data);
+void *ofono_sms_get_data(struct ofono_sms *sms);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_SMS_H */
diff --git a/include/stk.h b/include/stk.h
new file mode 100644 (file)
index 0000000..6182804
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_STK_H
+#define __OFONO_STK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_stk;
+
+typedef void (*ofono_stk_envelope_cb_t)(const struct ofono_error *error,
+                                       const unsigned char *rdata,
+                                       int length, void *data);
+
+typedef void (*ofono_stk_generic_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+struct ofono_stk_driver {
+       const char *name;
+       int (*probe)(struct ofono_stk *stk, unsigned int vendor, void *data);
+       void (*remove)(struct ofono_stk *stk);
+       void (*envelope)(struct ofono_stk *stk,
+                               int length, const unsigned char *command,
+                               ofono_stk_envelope_cb_t cb, void *data);
+       void (*terminal_response)(struct ofono_stk *stk,
+                                       int length, const unsigned char *resp,
+                                       ofono_stk_generic_cb_t cb, void *data);
+       void (*user_confirmation)(struct ofono_stk *stk, ofono_bool_t confirm);
+};
+
+int ofono_stk_driver_register(const struct ofono_stk_driver *d);
+void ofono_stk_driver_unregister(const struct ofono_stk_driver *d);
+
+struct ofono_stk *ofono_stk_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_stk_register(struct ofono_stk *stk);
+void ofono_stk_remove(struct ofono_stk *stk);
+
+void ofono_stk_set_data(struct ofono_stk *stk, void *data);
+void *ofono_stk_get_data(struct ofono_stk *stk);
+
+void ofono_stk_proactive_command_notify(struct ofono_stk *stk,
+                                       int length, const unsigned char *pdu);
+
+void ofono_stk_proactive_session_end_notify(struct ofono_stk *stk);
+
+void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk,
+                                               int length,
+                                               const unsigned char *pdu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_STK_H */
diff --git a/include/types.h b/include/types.h
new file mode 100644 (file)
index 0000000..8c01bf4
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_TYPES_H
+#define __OFONO_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef int            ofono_bool_t;
+
+/* MCC is always three digits. MNC is either two or three digits */
+#define OFONO_MAX_MCC_LENGTH 3
+#define OFONO_MAX_MNC_LENGTH 3
+
+typedef void (*ofono_destroy_func)(void *data);
+
+/* 27.007 Section 6.2 */
+enum ofono_clir_option {
+       OFONO_CLIR_OPTION_DEFAULT = 0,
+       OFONO_CLIR_OPTION_INVOCATION,
+       OFONO_CLIR_OPTION_SUPPRESSION,
+};
+
+enum ofono_error_type {
+       OFONO_ERROR_TYPE_NO_ERROR = 0,
+       OFONO_ERROR_TYPE_CME,
+       OFONO_ERROR_TYPE_CMS,
+       OFONO_ERROR_TYPE_CEER,
+       OFONO_ERROR_TYPE_SIM,
+       OFONO_ERROR_TYPE_FAILURE,
+};
+
+enum ofono_disconnect_reason {
+       OFONO_DISCONNECT_REASON_UNKNOWN = 0,
+       OFONO_DISCONNECT_REASON_LOCAL_HANGUP,
+       OFONO_DISCONNECT_REASON_REMOTE_HANGUP,
+       OFONO_DISCONNECT_REASON_ERROR,
+};
+
+struct ofono_error {
+       enum ofono_error_type type;
+       int error;
+};
+
+#define OFONO_MAX_PHONE_NUMBER_LENGTH 80
+#define OFONO_MAX_CALLER_NAME_LENGTH 80
+
+struct ofono_phone_number {
+       char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1];
+       int type;
+};
+
+/* Length of NUM_FIELDS in 3GPP2 C.S0005-E v2.0 */
+#define OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH 256
+
+struct ofono_cdma_phone_number {
+       /* char maps to max size of CHARi (8 bit) in 3GPP2 C.S0005-E v2.0 */
+       char number[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH];
+};
+
+struct ofono_call {
+       unsigned int id;
+       int type;
+       int direction;
+       int status;
+       struct ofono_phone_number phone_number;
+       struct ofono_phone_number called_number;
+       char name[OFONO_MAX_CALLER_NAME_LENGTH + 1];
+       int clip_validity;
+       int cnap_validity;
+};
+
+struct ofono_network_time {
+       int sec;        /* Seconds [0..59], -1 if unavailable */
+       int min;        /* Minutes [0..59], -1 if unavailable */
+       int hour;       /* Hours [0..23], -1 if unavailable */
+       int mday;       /* Day of month [1..31], -1 if unavailable */
+       int mon;        /* Month [1..12], -1 if unavailable */
+       int year;       /* Current year, -1 if unavailable */
+       int dst;        /* Current adjustment, in hours */
+       int utcoff;     /* Offset from UTC in seconds */
+};
+
+#define OFONO_SHA1_UUID_LEN 20
+
+struct ofono_uuid {
+       unsigned char uuid[OFONO_SHA1_UUID_LEN];
+};
+
+/* HFP AG supported features bitmap. Bluetooth HFP 1.6 spec page 88 */
+enum hfp_ag_feature {
+       HFP_AG_FEATURE_3WAY =                   0x1,
+       HFP_AG_FEATURE_ECNR =                   0x2,
+       HFP_AG_FEATURE_VOICE_RECOG =            0x4,
+       HFP_AG_FEATURE_IN_BAND_RING_TONE =      0x8,
+       HFP_AG_FEATURE_ATTACH_VOICE_TAG =       0x10,
+       HFP_AG_FEATURE_REJECT_CALL =            0x20,
+       HFP_AG_FEATURE_ENHANCED_CALL_STATUS =   0x40,
+       HFP_AG_FEATURE_ENHANCED_CALL_CONTROL =  0x80,
+       HFP_AG_FEATURE_EXTENDED_RES_CODE =      0x100,
+       HFP_AG_FEATURE_CODEC_NEGOTIATION =      0x200,
+};
+
+/* HFP HF supported features bitmap. Bluetooth HFP 1.6 spec page 88 */
+enum hfp_hf_feature {
+       HFP_HF_FEATURE_ECNR =                   0x1,
+       HFP_HF_FEATURE_3WAY =                   0x2,
+       HFP_HF_FEATURE_CLIP =                   0x4,
+       HFP_HF_FEATURE_VOICE_RECOGNITION =      0x8,
+       HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL =  0x10,
+       HFP_HF_FEATURE_ENHANCED_CALL_STATUS =   0x20,
+       HFP_HF_FEATURE_ENHANCED_CALL_CONTROL =  0x40,
+       HFP_HF_FEATURE_CODEC_NEGOTIATION =      0x80,
+};
+
+const char *ofono_uuid_to_str(const struct ofono_uuid *uuid);
+void ofono_call_init(struct ofono_call *call);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_TYPES_H */
diff --git a/include/ussd.h b/include/ussd.h
new file mode 100644 (file)
index 0000000..ac463e7
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_USSD_H
+#define __OFONO_USSD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+/* 3GPP TS 27.007 section 7.15, values for <m> */
+enum ofono_ussd_status {
+       OFONO_USSD_STATUS_NOTIFY = 0,
+       OFONO_USSD_STATUS_ACTION_REQUIRED = 1,
+       OFONO_USSD_STATUS_TERMINATED = 2,
+       OFONO_USSD_STATUS_LOCAL_CLIENT_RESPONDED = 3,
+       OFONO_USSD_STATUS_NOT_SUPPORTED = 4,
+       OFONO_USSD_STATUS_TIMED_OUT = 5,
+};
+
+struct ofono_ussd;
+
+typedef void (*ofono_ussd_cb_t)(const struct ofono_error *error, void *data);
+
+struct ofono_ussd_driver {
+       const char *name;
+       int (*probe)(struct ofono_ussd *ussd, unsigned int vendor, void *data);
+       void (*remove)(struct ofono_ussd *ussd);
+       void (*request)(struct ofono_ussd *ussd, int dcs,
+                       const unsigned char *pdu, int len,
+                       ofono_ussd_cb_t, void *data);
+       void (*cancel)(struct ofono_ussd *ussd,
+                               ofono_ussd_cb_t cb, void *data);
+};
+
+void ofono_ussd_notify(struct ofono_ussd *ussd, int status, int dcs,
+                       const unsigned char *data, int data_len);
+
+int ofono_ussd_driver_register(const struct ofono_ussd_driver *d);
+void ofono_ussd_driver_unregister(const struct ofono_ussd_driver *d);
+
+struct ofono_ussd *ofono_ussd_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_ussd_register(struct ofono_ussd *ussd);
+void ofono_ussd_remove(struct ofono_ussd *ussd);
+
+void ofono_ussd_set_data(struct ofono_ussd *ussd, void *data);
+void *ofono_ussd_get_data(struct ofono_ussd *ussd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_USSD_H */
diff --git a/include/version.h.in b/include/version.h.in
new file mode 100644 (file)
index 0000000..396b98a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  oFono - Open Telephony stack for Linux
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_VERSION_H
+#define __OFONO_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OFONO_VERSION  "@VERSION@"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_VERSION_H */
diff --git a/include/voicecall.h b/include/voicecall.h
new file mode 100644 (file)
index 0000000..221366e
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OFONO_VOICECALL_H
+#define __OFONO_VOICECALL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_voicecall;
+
+typedef void (*ofono_voicecall_cb_t)(const struct ofono_error *error,
+                                       void *data);
+
+/* Voice call related functionality, including ATD, ATA, +CHLD, CTFR, CLCC
+ * and VTS.
+ *
+ * It is up to the plugin to implement polling of CLCC if the modem does
+ * not support vendor extensions for call progress indication.
+ */
+struct ofono_voicecall_driver {
+       const char *name;
+       int (*probe)(struct ofono_voicecall *vc, unsigned int vendor,
+                       void *data);
+       void (*remove)(struct ofono_voicecall *vc);
+
+       /* According to 22.030 the dial is expected to do the following:
+        * - If an there is an existing active call(s), and the dial is
+        *   successful, the active calls are automatically put on hold.
+        *   Driver must take special care to put the call on hold before
+        *   returning from atd call.
+        *
+        * - The dial has no affect on the state of the waiting call,
+        *   if the hardware does not support this, then it is better
+        *   to return an error here.  No special handling of the
+        *   waiting call is performed by the core
+        */
+       void (*dial)(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *number,
+                       enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+                       void *data);
+       /* Answers an incoming call, this usually corresponds to ATA */
+       void (*answer)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+
+       /* Hangs up active, dialing, alerting or incoming calls */
+       void (*hangup_active)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /* Hangs up all calls except waiting calls */
+       void (*hangup_all)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Holds all active calls and answers waiting call.  If there is
+        * no waiting calls, retrieves held call.  This usually
+        * corresponds to +CHLD=2
+        */
+       void (*hold_all_active)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /* Releases all held calls, this usually corresponds to +CHLD=0*/
+       void (*release_all_held)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Sets the UDUB condition on a waiting call.  This usually
+        * corresponds to +CHLD=0
+        */
+       void (*set_udub)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Releases all active calls and accepts a possible waiting call.
+        * This usually corresponds to +CHLD=1
+        */
+       void (*release_all_active)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Releases a specific call given by id.  This usually corresponds to
+        * +CHLD=1X.  In 3GPP this command is only guaranteed to affect active
+        * calls.  Plugins are encouraged to implement this using vendor
+        * commands that can also affect held calls
+        */
+       void (*release_specific)(struct ofono_voicecall *vc, int id,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Breaks out a party given by id from a multiparty call.  This
+        * usually corresponds to +CHLD=2X
+        */
+       void (*private_chat)(struct ofono_voicecall *vc, int id,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Joins held and active calls together into a multiparty call.  This
+        * usually corresponds to +CHLD=3
+        */
+       void (*create_multiparty)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Connects two calls together and disconnects from both calls.  This
+        * usually corresponds to +CHLD=4
+        */
+       void (*transfer)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * Deflects an incoming or waiting call to a given number.  This
+        * usually corresponds to +CTFR
+        */
+       void (*deflect)(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       ofono_voicecall_cb_t cb, void *data);
+       /*
+        * This is equivalent to +CHLD=2 but does not affect a possible
+        * waiting call.
+        */
+       void (*swap_without_accept)(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data);
+       void (*send_tones)(struct ofono_voicecall *vc, const char *tones,
+                       ofono_voicecall_cb_t cb, void *data);
+};
+
+void ofono_voicecall_en_list_notify(struct ofono_voicecall *vc,
+                                       char **nw_en_list);
+
+void ofono_voicecall_notify(struct ofono_voicecall *vc,
+                               const struct ofono_call *call);
+void ofono_voicecall_disconnected(struct ofono_voicecall *vc, int id,
+                               enum ofono_disconnect_reason reason,
+                               const struct ofono_error *error);
+
+int ofono_voicecall_driver_register(const struct ofono_voicecall_driver *d);
+void ofono_voicecall_driver_unregister(const struct ofono_voicecall_driver *d);
+
+struct ofono_voicecall *ofono_voicecall_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data);
+
+void ofono_voicecall_register(struct ofono_voicecall *vc);
+void ofono_voicecall_remove(struct ofono_voicecall *vc);
+
+void ofono_voicecall_set_data(struct ofono_voicecall *vc, void *data);
+void *ofono_voicecall_get_data(struct ofono_voicecall *vc);
+int ofono_voicecall_get_next_callid(struct ofono_voicecall *vc);
+
+void ofono_voicecall_ssn_mo_notify(struct ofono_voicecall *vc, unsigned int id,
+                                       int code, int index);
+void ofono_voicecall_ssn_mt_notify(struct ofono_voicecall *vc, unsigned int id,
+                                       int code, int index,
+                                       const struct ofono_phone_number *ph);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_VOICECALL_H */
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..6781b98
--- /dev/null
@@ -0,0 +1,520 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2009-04-28.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" ""       $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+  doit_exec=exec
+else
+  doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+  test "$posix_glob" != "?" || {
+    if (set -f) 2>/dev/null; then
+      posix_glob=
+    else
+      posix_glob=:
+    fi
+  }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+       shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+       case $mode in
+         *' '* | *'    '* | *'
+'*       | *'*'* | *'?'* | *'['*)
+           echo "$0: invalid mode: $mode" >&2
+           exit 1;;
+       esac
+       shift;;
+
+    -o) chowncmd="$chownprog $2"
+       shift;;
+
+    -s) stripcmd=$stripprog;;
+
+    -t) dst_arg=$2
+       shift;;
+
+    -T) no_target_directory=true;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --)        shift
+       break;;
+
+    -*)        echo "$0: invalid option: $1" >&2
+       exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call `install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  trap '(exit $?); exit' 1 2 13 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+       u_plus_rw=
+      else
+       u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+       u_plus_rw=
+      else
+       u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names starting with `-'.
+  case $src in
+    -*) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+
+    dst=$dst_arg
+    # Protect names starting with `-'.
+    case $dst in
+      -*) dst=./$dst;;
+    esac
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test -n "$no_target_directory"; then
+       echo "$0: $dst_arg: Is a directory" >&2
+       exit 1
+      fi
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      # Prefer dirname, but fall back on a substitute if dirname fails.
+      dstdir=`
+       (dirname "$dst") 2>/dev/null ||
+       expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+            X"$dst" : 'X\(//\)[^/]' \| \
+            X"$dst" : 'X\(//\)$' \| \
+            X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+       echo X"$dst" |
+           sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\/\)[^/].*/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\/\)$/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\).*/{
+                  s//\1/
+                  q
+                }
+                s/.*/./; q'
+      `
+
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+       # Create intermediate dirs using mode 755 as modified by the umask.
+       # This is like FreeBSD 'install' as of 1997-10-28.
+       umask=`umask`
+       case $stripcmd.$umask in
+         # Optimize common cases.
+         *[2367][2367]) mkdir_umask=$umask;;
+         .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+         *[0-7])
+           mkdir_umask=`expr $umask + 22 \
+             - $umask % 100 % 40 + $umask % 20 \
+             - $umask % 10 % 4 + $umask % 2
+           `;;
+         *) mkdir_umask=$umask,go-w;;
+       esac
+
+       # With -d, create the new directory with the user-specified mode.
+       # Otherwise, rely on $mkdir_umask.
+       if test -n "$dir_arg"; then
+         mkdir_mode=-m$mode
+       else
+         mkdir_mode=
+       fi
+
+       posix_mkdir=false
+       case $umask in
+         *[123567][0-7][0-7])
+           # POSIX mkdir -p sets u+wx bits regardless of umask, which
+           # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+           ;;
+         *)
+           tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+           trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+           if (umask $mkdir_umask &&
+               exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+           then
+             if test -z "$dir_arg" || {
+                  # Check for POSIX incompatibilities with -m.
+                  # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                  # other-writeable bit of parent directory when it shouldn't.
+                  # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                  ls_ld_tmpdir=`ls -ld "$tmpdir"`
+                  case $ls_ld_tmpdir in
+                    d????-?r-*) different_mode=700;;
+                    d????-?--*) different_mode=755;;
+                    *) false;;
+                  esac &&
+                  $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+                    ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+                    test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                  }
+                }
+             then posix_mkdir=:
+             fi
+             rmdir "$tmpdir/d" "$tmpdir"
+           else
+             # Remove any dirs left behind by ancient mkdir implementations.
+             rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+           fi
+           trap '' 0;;
+       esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+       umask $mkdir_umask &&
+       $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+       /*) prefix='/';;
+       -*) prefix='./';;
+       *)  prefix='';;
+      esac
+
+      eval "$initialize_posix_glob"
+
+      oIFS=$IFS
+      IFS=/
+      $posix_glob set -f
+      set fnord $dstdir
+      shift
+      $posix_glob set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+       test -z "$d" && continue
+
+       prefix=$prefix$d
+       if test -d "$prefix"; then
+         prefixes=
+       else
+         if $posix_mkdir; then
+           (umask=$mkdir_umask &&
+            $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+           # Don't fail if two instances are running concurrently.
+           test -d "$prefix" || exit 1
+         else
+           case $prefix in
+             *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+             *) qprefix=$prefix;;
+           esac
+           prefixes="$prefixes '$qprefix'"
+         fi
+       fi
+       prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+       # Don't fail if two instances are running concurrently.
+       (umask $mkdir_umask &&
+        eval "\$doit_exec \$mkdirprog $prefixes") ||
+         test -d "$dstdir" || exit 1
+       obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"    2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+       eval "$initialize_posix_glob" &&
+       $posix_glob set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       $posix_glob set +f &&
+
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+       # Now remove or move aside any old file at destination location.
+       # We try this two ways since rm can't unlink itself on some
+       # systems and the destination file might be busy for other
+       # reasons.  In this case, the final cleanup might fail but the new
+       # file should still install successfully.
+       {
+         test ! -f "$dst" ||
+         $doit $rmcmd -f "$dst" 2>/dev/null ||
+         { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+           { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+         } ||
+         { echo "$0: cannot unlink or rename $dst" >&2
+           (exit 1); exit 1
+         }
+       } &&
+
+       # Now rename the file to the real destination.
+       $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/ltmain.sh b/ltmain.sh
new file mode 100755 (executable)
index 0000000..a72f2fd
--- /dev/null
+++ b/ltmain.sh
@@ -0,0 +1,8406 @@
+# Generated from ltmain.m4sh.
+
+# ltmain.sh (GNU libtool) 2.2.6b
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions.  There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html,
+# or obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Usage: $progname [OPTION]... [MODE-ARG]...
+#
+# Provide generalized library-building support services.
+#
+#     --config             show all configuration variables
+#     --debug              enable verbose shell tracing
+# -n, --dry-run            display commands without modifying any files
+#     --features           display basic configuration information and exit
+#     --mode=MODE          use operation mode MODE
+#     --preserve-dup-deps  don't remove duplicate dependency libraries
+#     --quiet, --silent    don't print informational messages
+#     --tag=TAG            use configuration variables from tag TAG
+# -v, --verbose            print informational messages (default)
+#     --version            print version information
+# -h, --help               print short or long help message
+#
+# MODE must be one of the following:
+#
+#       clean              remove files from the build directory
+#       compile            compile a source file into a libtool object
+#       execute            automatically set library path, then run a program
+#       finish             complete the installation of libtool libraries
+#       install            install libraries or executables
+#       link               create a library or an executable
+#       uninstall          remove libraries from an installed directory
+#
+# MODE-ARGS vary depending on the MODE.
+# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
+#
+# When reporting a bug, please describe a test case to reproduce it and
+# include the following information:
+#
+#       host-triplet:  $host
+#       shell:         $SHELL
+#       compiler:              $LTCC
+#       compiler flags:                $LTCFLAGS
+#       linker:                $LD (gnu? $with_gnu_ld)
+#       $progname:             (GNU libtool) 2.2.6b
+#       automake:              $automake_version
+#       autoconf:              $autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=2.2.6b
+TIMESTAMP=""
+package_revision=1.3017
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# NLS nuisances: We save the old values to restore during execute mode.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+lt_user_locale=
+lt_safe_locale=
+for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+  eval "if test \"\${$lt_var+set}\" = set; then
+          save_$lt_var=\$$lt_var
+          $lt_var=C
+         export $lt_var
+         lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
+         lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+       fi"
+done
+
+$lt_unset CDPATH
+
+
+
+
+
+: ${CP="cp -f"}
+: ${ECHO="echo"}
+: ${EGREP="/bin/grep -E"}
+: ${FGREP="/bin/grep -F"}
+: ${GREP="/bin/grep"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SED="/bin/sed"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+: ${Xsed="$SED -e 1s/^X//"}
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63  # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77     # $? = 77 is used to indicate a skipped test to automake.
+
+exit_status=$EXIT_SUCCESS
+
+# Make sure IFS has a sensible default
+lt_nl='
+'
+IFS="  $lt_nl"
+
+dirname="s,/[^/]*$,,"
+basename="s,^.*/,,"
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+#   dirname:  Compute the dirname of FILE.  If nonempty,
+#             add APPEND to the result, otherwise set result
+#             to NONDIR_REPLACEMENT.
+#             value returned in "$func_dirname_result"
+#   basename: Compute filename of FILE.
+#             value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+  # Extract subdirectory from the argument.
+  func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+  if test "X$func_dirname_result" = "X${1}"; then
+    func_dirname_result="${3}"
+  else
+    func_dirname_result="$func_dirname_result${2}"
+  fi
+  func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+# Generated shell functions inserted here.
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+# The name of this program:
+# In the unlikely event $progname began with a '-', it would play havoc with
+# func_echo (imagine progname=-n), so we prepend ./ in that case:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+case $progname in
+  -*) progname=./$progname ;;
+esac
+
+# Make sure we have an absolute path for reexecution:
+case $progpath in
+  [\\/]*|[A-Za-z]:\\*) ;;
+  *[\\/]*)
+     progdir=$func_dirname_result
+     progdir=`cd "$progdir" && pwd`
+     progpath="$progdir/$progname"
+     ;;
+  *)
+     save_IFS="$IFS"
+     IFS=:
+     for progdir in $PATH; do
+       IFS="$save_IFS"
+       test -x "$progdir/$progname" && break
+     done
+     IFS="$save_IFS"
+     test -n "$progdir" || progdir=`pwd`
+     progpath="$progdir/$progname"
+     ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Re-`\' parameter expansions in output of double_quote_subst that were
+# `\'-ed in input to the same.  If an odd number of `\' preceded a '$'
+# in input to double_quote_subst, that '$' was protected from expansion.
+# Since each input `\' is now two `\'s, look for any number of runs of
+# four `\'s followed by two `\'s and then a '$'.  `\' that '$'.
+bs='\\'
+bs2='\\\\'
+bs4='\\\\\\\\'
+dollar='\$'
+sed_double_backslash="\
+  s/$bs4/&\\
+/g
+  s/^$bs2$dollar/$bs&/
+  s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
+  s/\n//g"
+
+# Standard options:
+opt_dry_run=false
+opt_help=false
+opt_quiet=false
+opt_verbose=false
+opt_warning=:
+
+# func_echo arg...
+# Echo program name prefixed message, along with the current mode
+# name if it has been set yet.
+func_echo ()
+{
+    $ECHO "$progname${mode+: }$mode: $*"
+}
+
+# func_verbose arg...
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+    $opt_verbose && func_echo ${1+"$@"}
+
+    # A bug in bash halts the script if the last line of a function
+    # fails when set -e is in force, so we need another command to
+    # work around that:
+    :
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+    $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+    $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2
+
+    # bash bug again:
+    :
+}
+
+# func_fatal_error arg...
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+    func_error ${1+"$@"}
+    exit $EXIT_FAILURE
+}
+
+# func_fatal_help arg...
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+    func_error ${1+"$@"}
+    func_fatal_error "$help"
+}
+help="Try \`$progname --help' for more information."  ## default
+
+
+# func_grep expression filename
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+    $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_mkdir_p directory-path
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+    my_directory_path="$1"
+    my_dir_list=
+
+    if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+
+      # Protect directory names starting with `-'
+      case $my_directory_path in
+        -*) my_directory_path="./$my_directory_path" ;;
+      esac
+
+      # While some portion of DIR does not yet exist...
+      while test ! -d "$my_directory_path"; do
+        # ...make a list in topmost first order.  Use a colon delimited
+       # list incase some portion of path contains whitespace.
+        my_dir_list="$my_directory_path:$my_dir_list"
+
+        # If the last portion added has no slash in it, the list is done
+        case $my_directory_path in */*) ;; *) break ;; esac
+
+        # ...otherwise throw away the child directory and loop
+        my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"`
+      done
+      my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'`
+
+      save_mkdir_p_IFS="$IFS"; IFS=':'
+      for my_dir in $my_dir_list; do
+       IFS="$save_mkdir_p_IFS"
+        # mkdir can fail with a `File exist' error if two processes
+        # try to create one of the directories concurrently.  Don't
+        # stop in that case!
+        $MKDIR "$my_dir" 2>/dev/null || :
+      done
+      IFS="$save_mkdir_p_IFS"
+
+      # Bail out if we (or some other process) failed to create a directory.
+      test -d "$my_directory_path" || \
+        func_fatal_error "Failed to create \`$1'"
+    fi
+}
+
+
+# func_mktempdir [string]
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible.  If
+# given, STRING is the basename for that directory.
+func_mktempdir ()
+{
+    my_template="${TMPDIR-/tmp}/${1-$progname}"
+
+    if test "$opt_dry_run" = ":"; then
+      # Return a directory name, but don't create it in dry-run mode
+      my_tmpdir="${my_template}-$$"
+    else
+
+      # If mktemp works, use that first and foremost
+      my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+
+      if test ! -d "$my_tmpdir"; then
+        # Failing that, at least try and use $RANDOM to avoid a race
+        my_tmpdir="${my_template}-${RANDOM-0}$$"
+
+        save_mktempdir_umask=`umask`
+        umask 0077
+        $MKDIR "$my_tmpdir"
+        umask $save_mktempdir_umask
+      fi
+
+      # If we're not in dry-run mode, bomb out on failure
+      test -d "$my_tmpdir" || \
+        func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+    fi
+
+    $ECHO "X$my_tmpdir" | $Xsed
+}
+
+
+# func_quote_for_eval arg
+# Aesthetically quote ARG to be evaled later.
+# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
+# is double-quoted, suitable for a subsequent eval, whereas
+# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
+# which are still active within double quotes backslashified.
+func_quote_for_eval ()
+{
+    case $1 in
+      *[\\\`\"\$]*)
+       func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;;
+      *)
+        func_quote_for_eval_unquoted_result="$1" ;;
+    esac
+
+    case $func_quote_for_eval_unquoted_result in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting, command substitution and and variable
+      # expansion for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+        func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+        ;;
+      *)
+        func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+    esac
+}
+
+
+# func_quote_for_expand arg
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+    case $1 in
+      *[\\\`\"]*)
+       my_arg=`$ECHO "X$1" | $Xsed \
+           -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+      *)
+        my_arg="$1" ;;
+    esac
+
+    case $my_arg in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting and command substitution for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+        my_arg="\"$my_arg\""
+        ;;
+    esac
+
+    func_quote_for_expand_result="$my_arg"
+}
+
+
+# func_show_eval cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$my_cmd"
+      my_status=$?
+      if test "$my_status" -eq 0; then :; else
+       eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+
+# func_show_eval_locale cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.  Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$lt_user_locale
+           $my_cmd"
+      my_status=$?
+      eval "$lt_safe_locale"
+      if test "$my_status" -eq 0; then :; else
+       eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+
+
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+    $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / {
+        s/^# //
+       s/^# *$//
+        s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
+        p
+     }' < "$progpath"
+     exit $?
+}
+
+# func_usage
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+    $SED -n '/^# Usage:/,/# -h/ {
+        s/^# //
+       s/^# *$//
+       s/\$progname/'$progname'/
+       p
+    }' < "$progpath"
+    $ECHO
+    $ECHO "run \`$progname --help | more' for full usage"
+    exit $?
+}
+
+# func_help
+# Echo long help message to standard output and exit.
+func_help ()
+{
+    $SED -n '/^# Usage:/,/# Report bugs to/ {
+        s/^# //
+       s/^# *$//
+       s*\$progname*'$progname'*
+       s*\$host*'"$host"'*
+       s*\$SHELL*'"$SHELL"'*
+       s*\$LTCC*'"$LTCC"'*
+       s*\$LTCFLAGS*'"$LTCFLAGS"'*
+       s*\$LD*'"$LD"'*
+       s/\$with_gnu_ld/'"$with_gnu_ld"'/
+       s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/
+       s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/
+       p
+     }' < "$progpath"
+    exit $?
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+    func_error "missing argument for $1"
+    exit_cmd=exit
+}
+
+exit_cmd=:
+
+
+
+
+
+# Check that we have a working $ECHO.
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then
+  # Yippee, $ECHO works!
+  :
+else
+  # Restart under the correct shell, and then maybe $ECHO will work.
+  exec $SHELL "$progpath" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+$*
+EOF
+  exit $EXIT_SUCCESS
+fi
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+# $mode is unset
+nonopt=
+execute_dlfiles=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+opt_dry_run=false
+opt_duplicate_deps=false
+opt_silent=false
+opt_debug=:
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end.  This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+# func_fatal_configuration arg...
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+    func_error ${1+"$@"}
+    func_error "See the $PACKAGE documentation for more information."
+    func_fatal_error "Fatal configuration error."
+}
+
+
+# func_config
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+    re_begincf='^# ### BEGIN LIBTOOL'
+    re_endcf='^# ### END LIBTOOL'
+
+    # Default configuration.
+    $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+    # Now print the configurations for the tags.
+    for tagname in $taglist; do
+      $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+    done
+
+    exit $?
+}
+
+# func_features
+# Display the features supported by this script.
+func_features ()
+{
+    $ECHO "host: $host"
+    if test "$build_libtool_libs" = yes; then
+      $ECHO "enable shared libraries"
+    else
+      $ECHO "disable shared libraries"
+    fi
+    if test "$build_old_libs" = yes; then
+      $ECHO "enable static libraries"
+    else
+      $ECHO "disable static libraries"
+    fi
+
+    exit $?
+}
+
+# func_enable_tag tagname
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag.  We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+  # Global variable:
+  tagname="$1"
+
+  re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+  re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+  sed_extractcf="/$re_begincf/,/$re_endcf/p"
+
+  # Validate tagname.
+  case $tagname in
+    *[!-_A-Za-z0-9,/]*)
+      func_fatal_error "invalid tag name: $tagname"
+      ;;
+  esac
+
+  # Don't test for the "default" C tag, as we know it's
+  # there but not specially marked.
+  case $tagname in
+    CC) ;;
+    *)
+      if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+       taglist="$taglist $tagname"
+
+       # Evaluate the configuration.  Be careful to quote the path
+       # and the sed script, to avoid splitting on whitespace, but
+       # also don't use non-portable quotes within backquotes within
+       # quotes we have to do it in 2 steps:
+       extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+       eval "$extractedcf"
+      else
+       func_error "ignoring unknown tag $tagname"
+      fi
+      ;;
+  esac
+}
+
+# Parse options once, thoroughly.  This comes as soon as possible in
+# the script to make things like `libtool --version' happen quickly.
+{
+
+  # Shorthand for --mode=foo, only valid as the first argument
+  case $1 in
+  clean|clea|cle|cl)
+    shift; set dummy --mode clean ${1+"$@"}; shift
+    ;;
+  compile|compil|compi|comp|com|co|c)
+    shift; set dummy --mode compile ${1+"$@"}; shift
+    ;;
+  execute|execut|execu|exec|exe|ex|e)
+    shift; set dummy --mode execute ${1+"$@"}; shift
+    ;;
+  finish|finis|fini|fin|fi|f)
+    shift; set dummy --mode finish ${1+"$@"}; shift
+    ;;
+  install|instal|insta|inst|ins|in|i)
+    shift; set dummy --mode install ${1+"$@"}; shift
+    ;;
+  link|lin|li|l)
+    shift; set dummy --mode link ${1+"$@"}; shift
+    ;;
+  uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+    shift; set dummy --mode uninstall ${1+"$@"}; shift
+    ;;
+  esac
+
+  # Parse non-mode specific arguments:
+  while test "$#" -gt 0; do
+    opt="$1"
+    shift
+
+    case $opt in
+      --config)                func_config                                     ;;
+
+      --debug)         preserve_args="$preserve_args $opt"
+                       func_echo "enabling shell trace mode"
+                       opt_debug='set -x'
+                       $opt_debug
+                       ;;
+
+      -dlopen)         test "$#" -eq 0 && func_missing_arg "$opt" && break
+                       execute_dlfiles="$execute_dlfiles $1"
+                       shift
+                       ;;
+
+      --dry-run | -n)  opt_dry_run=:                                   ;;
+      --features)       func_features                                  ;;
+      --finish)                mode="finish"                                   ;;
+
+      --mode)          test "$#" -eq 0 && func_missing_arg "$opt" && break
+                       case $1 in
+                         # Valid mode arguments:
+                         clean)        ;;
+                         compile)      ;;
+                         execute)      ;;
+                         finish)       ;;
+                         install)      ;;
+                         link)         ;;
+                         relink)       ;;
+                         uninstall)    ;;
+
+                         # Catch anything else as an error
+                         *) func_error "invalid argument for $opt"
+                            exit_cmd=exit
+                            break
+                            ;;
+                       esac
+
+                       mode="$1"
+                       shift
+                       ;;
+
+      --preserve-dup-deps)
+                       opt_duplicate_deps=:                            ;;
+
+      --quiet|--silent)        preserve_args="$preserve_args $opt"
+                       opt_silent=:
+                       ;;
+
+      --verbose| -v)   preserve_args="$preserve_args $opt"
+                       opt_silent=false
+                       ;;
+
+      --tag)           test "$#" -eq 0 && func_missing_arg "$opt" && break
+                       preserve_args="$preserve_args $opt $1"
+                       func_enable_tag "$1"    # tagname is set here
+                       shift
+                       ;;
+
+      # Separate optargs to long options:
+      -dlopen=*|--mode=*|--tag=*)
+                       func_opt_split "$opt"
+                       set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"}
+                       shift
+                       ;;
+
+      -\?|-h)          func_usage                                      ;;
+      --help)          opt_help=:                                      ;;
+      --version)       func_version                                    ;;
+
+      -*)              func_fatal_help "unrecognized option \`$opt'"   ;;
+
+      *)               nonopt="$opt"
+                       break
+                       ;;
+    esac
+  done
+
+
+  case $host in
+    *cygwin* | *mingw* | *pw32* | *cegcc*)
+      # don't eliminate duplications in $postdeps and $predeps
+      opt_duplicate_compiler_generated_deps=:
+      ;;
+    *)
+      opt_duplicate_compiler_generated_deps=$opt_duplicate_deps
+      ;;
+  esac
+
+  # Having warned about all mis-specified options, bail out if
+  # anything was wrong.
+  $exit_cmd $EXIT_FAILURE
+}
+
+# func_check_version_match
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+  if test "$package_revision" != "$macro_revision"; then
+    if test "$VERSION" != "$macro_version"; then
+      if test -z "$macro_version"; then
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      else
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      fi
+    else
+      cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+    fi
+
+    exit $EXIT_MISMATCH
+  fi
+}
+
+
+## ----------- ##
+##    Main.    ##
+## ----------- ##
+
+$opt_help || {
+  # Sanity checks first:
+  func_check_version_match
+
+  if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+    func_fatal_configuration "not configured to build any kind of library"
+  fi
+
+  test -z "$mode" && func_fatal_error "error: you must specify a MODE."
+
+
+  # Darwin sucks
+  eval std_shrext=\"$shrext_cmds\"
+
+
+  # Only execute mode is allowed to have -dlopen flags.
+  if test -n "$execute_dlfiles" && test "$mode" != execute; then
+    func_error "unrecognized option \`-dlopen'"
+    $ECHO "$help" 1>&2
+    exit $EXIT_FAILURE
+  fi
+
+  # Change the help message to a mode-specific one.
+  generic_help="$help"
+  help="Try \`$progname --help --mode=$mode' for more information."
+}
+
+
+# func_lalib_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+    test -f "$1" &&
+      $SED -e 4q "$1" 2>/dev/null \
+        | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs.  To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway.  Works if `file' does not exist.
+func_lalib_unsafe_p ()
+{
+    lalib_p=no
+    if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+       for lalib_p_l in 1 2 3 4
+       do
+           read lalib_p_line
+           case "$lalib_p_line" in
+               \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+           esac
+       done
+       exec 0<&5 5<&-
+    fi
+    test "$lalib_p" = yes
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+    func_lalib_p "$1"
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+    func_ltwrapper_exec_suffix=
+    case $1 in
+    *.exe) ;;
+    *) func_ltwrapper_exec_suffix=.exe ;;
+    esac
+    $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+    func_ltwrapper_scriptname_result=""
+    if func_ltwrapper_executable_p "$1"; then
+       func_dirname_and_basename "$1" "" "."
+       func_stripname '' '.exe' "$func_basename_result"
+       func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+    fi
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+    func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+    $opt_debug
+    save_ifs=$IFS; IFS='~'
+    for cmd in $1; do
+      IFS=$save_ifs
+      eval cmd=\"$cmd\"
+      func_show_eval "$cmd" "${2-:}"
+    done
+    IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)!  Also, sourcing
+# `FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+    $opt_debug
+    case $1 in
+    */* | *\\*)        . "$1" ;;
+    *)         . "./$1" ;;
+    esac
+}
+
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+    $opt_debug
+    if test -n "$available_tags" && test -z "$tagname"; then
+      CC_quoted=
+      for arg in $CC; do
+        func_quote_for_eval "$arg"
+       CC_quoted="$CC_quoted $func_quote_for_eval_result"
+      done
+      case $@ in
+      # Blanks in the command may have been stripped by the calling shell,
+      # but not from the CC environment variable when configure was run.
+      " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;;
+      # Blanks at the start of $base_compile will cause this to fail
+      # if we don't check for them as well.
+      *)
+       for z in $available_tags; do
+         if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+           # Evaluate the configuration.
+           eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+           CC_quoted=
+           for arg in $CC; do
+             # Double-quote args containing other shell metacharacters.
+             func_quote_for_eval "$arg"
+             CC_quoted="$CC_quoted $func_quote_for_eval_result"
+           done
+           case "$@ " in
+             " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*)
+             # The compiler in the base compile command matches
+             # the one in the tagged configuration.
+             # Assume this is the tagged configuration we want.
+             tagname=$z
+             break
+             ;;
+           esac
+         fi
+       done
+       # If $tagname still isn't set, then no tagged configuration
+       # was found and let the user know that the "--tag" command
+       # line option must be used.
+       if test -z "$tagname"; then
+         func_echo "unable to infer tagged configuration"
+         func_fatal_error "specify a tag with \`--tag'"
+#      else
+#        func_verbose "using $tagname tagged configuration"
+       fi
+       ;;
+      esac
+    fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+    write_libobj=${1}
+    if test "$build_libtool_libs" = yes; then
+      write_lobj=\'${2}\'
+    else
+      write_lobj=none
+    fi
+
+    if test "$build_old_libs" = yes; then
+      write_oldobj=\'${3}\'
+    else
+      write_oldobj=none
+    fi
+
+    $opt_dry_run || {
+      cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+      $MV "${write_libobj}T" "${write_libobj}"
+    }
+}
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+    $opt_debug
+    # Get the compilation command and the source file.
+    base_compile=
+    srcfile="$nonopt"  #  always keep a non-empty value in "srcfile"
+    suppress_opt=yes
+    suppress_output=
+    arg_mode=normal
+    libobj=
+    later=
+    pie_flag=
+
+    for arg
+    do
+      case $arg_mode in
+      arg  )
+       # do not "continue".  Instead, add this to base_compile
+       lastarg="$arg"
+       arg_mode=normal
+       ;;
+
+      target )
+       libobj="$arg"
+       arg_mode=normal
+       continue
+       ;;
+
+      normal )
+       # Accept any command-line options.
+       case $arg in
+       -o)
+         test -n "$libobj" && \
+           func_fatal_error "you cannot specify \`-o' more than once"
+         arg_mode=target
+         continue
+         ;;
+
+       -pie | -fpie | -fPIE)
+          pie_flag="$pie_flag $arg"
+         continue
+         ;;
+
+       -shared | -static | -prefer-pic | -prefer-non-pic)
+         later="$later $arg"
+         continue
+         ;;
+
+       -no-suppress)
+         suppress_opt=no
+         continue
+         ;;
+
+       -Xcompiler)
+         arg_mode=arg  #  the next one goes into the "base_compile" arg list
+         continue      #  The current "srcfile" will either be retained or
+         ;;            #  replaced later.  I would guess that would be a bug.
+
+       -Wc,*)
+         func_stripname '-Wc,' '' "$arg"
+         args=$func_stripname_result
+         lastarg=
+         save_ifs="$IFS"; IFS=','
+         for arg in $args; do
+           IFS="$save_ifs"
+           func_quote_for_eval "$arg"
+           lastarg="$lastarg $func_quote_for_eval_result"
+         done
+         IFS="$save_ifs"
+         func_stripname ' ' '' "$lastarg"
+         lastarg=$func_stripname_result
+
+         # Add the arguments to base_compile.
+         base_compile="$base_compile $lastarg"
+         continue
+         ;;
+
+       *)
+         # Accept the current argument as the source file.
+         # The previous "srcfile" becomes the current argument.
+         #
+         lastarg="$srcfile"
+         srcfile="$arg"
+         ;;
+       esac  #  case $arg
+       ;;
+      esac    #  case $arg_mode
+
+      # Aesthetically quote the previous argument.
+      func_quote_for_eval "$lastarg"
+      base_compile="$base_compile $func_quote_for_eval_result"
+    done # for arg
+
+    case $arg_mode in
+    arg)
+      func_fatal_error "you must specify an argument for -Xcompile"
+      ;;
+    target)
+      func_fatal_error "you must specify a target with \`-o'"
+      ;;
+    *)
+      # Get the name of the library object.
+      test -z "$libobj" && {
+       func_basename "$srcfile"
+       libobj="$func_basename_result"
+      }
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    case $libobj in
+    *.[cCFSifmso] | \
+    *.ada | *.adb | *.ads | *.asm | \
+    *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+    *.[fF][09]? | *.for | *.java | *.obj | *.sx)
+      func_xform "$libobj"
+      libobj=$func_xform_result
+      ;;
+    esac
+
+    case $libobj in
+    *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+    *)
+      func_fatal_error "cannot determine name of library object from \`$libobj'"
+      ;;
+    esac
+
+    func_infer_tag $base_compile
+
+    for arg in $later; do
+      case $arg in
+      -shared)
+       test "$build_libtool_libs" != yes && \
+         func_fatal_configuration "can not build a shared library"
+       build_old_libs=no
+       continue
+       ;;
+
+      -static)
+       build_libtool_libs=no
+       build_old_libs=yes
+       continue
+       ;;
+
+      -prefer-pic)
+       pic_mode=yes
+       continue
+       ;;
+
+      -prefer-non-pic)
+       pic_mode=no
+       continue
+       ;;
+      esac
+    done
+
+    func_quote_for_eval "$libobj"
+    test "X$libobj" != "X$func_quote_for_eval_result" \
+      && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'   &()|`$[]' \
+      && func_warning "libobj name \`$libobj' may not contain shell special characters."
+    func_dirname_and_basename "$obj" "/" ""
+    objname="$func_basename_result"
+    xdir="$func_dirname_result"
+    lobj=${xdir}$objdir/$objname
+
+    test -z "$base_compile" && \
+      func_fatal_help "you must specify a compilation command"
+
+    # Delete any leftover library objects.
+    if test "$build_old_libs" = yes; then
+      removelist="$obj $lobj $libobj ${libobj}T"
+    else
+      removelist="$lobj $libobj ${libobj}T"
+    fi
+
+    # On Cygwin there's no "real" PIC flag so we must build both object types
+    case $host_os in
+    cygwin* | mingw* | pw32* | os2* | cegcc*)
+      pic_mode=default
+      ;;
+    esac
+    if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+      # non-PIC code in shared libraries is not supported
+      pic_mode=default
+    fi
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test "$compiler_c_o" = no; then
+      output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext}
+      lockfile="$output_obj.lock"
+    else
+      output_obj=
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test "$need_locks" = yes; then
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+       func_echo "Waiting for $lockfile to be removed"
+       sleep 2
+      done
+    elif test "$need_locks" = warn; then
+      if test -f "$lockfile"; then
+       $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $opt_dry_run || $RM $removelist
+       exit $EXIT_FAILURE
+      fi
+      removelist="$removelist $output_obj"
+      $ECHO "$srcfile" > "$lockfile"
+    fi
+
+    $opt_dry_run || $RM $removelist
+    removelist="$removelist $lockfile"
+    trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+    if test -n "$fix_srcfile_path"; then
+      eval srcfile=\"$fix_srcfile_path\"
+    fi
+    func_quote_for_eval "$srcfile"
+    qsrcfile=$func_quote_for_eval_result
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test "$build_libtool_libs" = yes; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      if test "$pic_mode" != no; then
+       command="$base_compile $qsrcfile $pic_flag"
+      else
+       # Don't build PIC code
+       command="$base_compile $qsrcfile"
+      fi
+
+      func_mkdir_p "$xdir$objdir"
+
+      if test -z "$output_obj"; then
+       # Place PIC objects in $objdir
+       command="$command -o $lobj"
+      fi
+
+      func_show_eval_locale "$command" \
+          'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+        test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+       $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $opt_dry_run || $RM $removelist
+       exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+       func_show_eval '$MV "$output_obj" "$lobj"' \
+         'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+
+      # Allow error messages only from the first compilation.
+      if test "$suppress_opt" = yes; then
+       suppress_output=' >/dev/null 2>&1'
+      fi
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test "$build_old_libs" = yes; then
+      if test "$pic_mode" != yes; then
+       # Don't build PIC code
+       command="$base_compile $qsrcfile$pie_flag"
+      else
+       command="$base_compile $qsrcfile $pic_flag"
+      fi
+      if test "$compiler_c_o" = yes; then
+       command="$command -o $obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      command="$command$suppress_output"
+      func_show_eval_locale "$command" \
+        '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+        test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+       $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $opt_dry_run || $RM $removelist
+       exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed
+      if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+       func_show_eval '$MV "$output_obj" "$obj"' \
+         'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+    fi
+
+    $opt_dry_run || {
+      func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+      # Unlock the critical section if it was locked
+      if test "$need_locks" != no; then
+       removelist=$lockfile
+        $RM "$lockfile"
+      fi
+    }
+
+    exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+test "$mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+    # We need to display help for each of the modes.
+    case $mode in
+      "")
+        # Generic help is extracted from the usage comments
+        # at the start of this file.
+        func_help
+        ;;
+
+      clean)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      compile)
+      $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -no-suppress      do not suppress compiler output for multiple passes
+  -prefer-pic       try to building PIC objects only
+  -prefer-non-pic   try to building non-PIC objects only
+  -shared           do not build a \`.o' file suitable for static linking
+  -static           only build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+        ;;
+
+      execute)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+        ;;
+
+      finish)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the \`--dry-run' option if you just want to see what would be executed."
+        ;;
+
+      install)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the \`install' or \`cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+  -inst-prefix PREFIX-DIR  Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+        ;;
+
+      link)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -dlopen FILE      \`-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+                    try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+                    try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-fast-install  disable the fast-install mode
+  -no-install       link a not-installable executable
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -objectlist FILE  Use a list of object files found in FILE to specify objects
+  -precious-files-regex REGEX
+                    don't remove output files matching REGEX
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -shared           only do dynamic linking of libtool libraries
+  -shrext SUFFIX    override the standard shared library file extension
+  -static           do not do any dynamic linking of uninstalled libtool libraries
+  -static-libtool-libs
+                    do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+                    specify library version info [each variable defaults to 0]
+  -weak LIBNAME     declare that the target provides the LIBNAME interface
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+        ;;
+
+      uninstall)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      *)
+        func_fatal_help "invalid operation mode \`$mode'"
+        ;;
+    esac
+
+    $ECHO
+    $ECHO "Try \`$progname --help' for more information about other modes."
+
+    exit $?
+}
+
+  # Now that we've collected a possible --mode arg, show help if necessary
+  $opt_help && func_mode_help
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+    $opt_debug
+    # The first argument is the command name.
+    cmd="$nonopt"
+    test -z "$cmd" && \
+      func_fatal_help "you must specify a COMMAND"
+
+    # Handle -dlopen flags immediately.
+    for file in $execute_dlfiles; do
+      test -f "$file" \
+       || func_fatal_help "\`$file' is not a file"
+
+      dir=
+      case $file in
+      *.la)
+       # Check to see that this really is a libtool archive.
+       func_lalib_unsafe_p "$file" \
+         || func_fatal_help "\`$lib' is not a valid libtool archive"
+
+       # Read the libtool library.
+       dlname=
+       library_names=
+       func_source "$file"
+
+       # Skip this library if it cannot be dlopened.
+       if test -z "$dlname"; then
+         # Warn if it was a shared library.
+         test -n "$library_names" && \
+           func_warning "\`$file' was not linked with \`-export-dynamic'"
+         continue
+       fi
+
+       func_dirname "$file" "" "."
+       dir="$func_dirname_result"
+
+       if test -f "$dir/$objdir/$dlname"; then
+         dir="$dir/$objdir"
+       else
+         if test ! -f "$dir/$dlname"; then
+           func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+         fi
+       fi
+       ;;
+
+      *.lo)
+       # Just add the directory containing the .lo file.
+       func_dirname "$file" "" "."
+       dir="$func_dirname_result"
+       ;;
+
+      *)
+       func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+       continue
+       ;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir="$absdir"
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+       eval "$shlibpath_var=\"\$dir\""
+      else
+       eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic="$magic"
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case $file in
+      -*) ;;
+      *)
+       # Do a test to see if this is really a libtool program.
+       if func_ltwrapper_script_p "$file"; then
+         func_source "$file"
+         # Transform arg to wrapped name.
+         file="$progdir/$program"
+       elif func_ltwrapper_executable_p "$file"; then
+         func_ltwrapper_scriptname "$file"
+         func_source "$func_ltwrapper_scriptname_result"
+         # Transform arg to wrapped name.
+         file="$progdir/$program"
+       fi
+       ;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      func_quote_for_eval "$file"
+      args="$args $func_quote_for_eval_result"
+    done
+
+    if test "X$opt_dry_run" = Xfalse; then
+      if test -n "$shlibpath_var"; then
+       # Export the shlibpath_var.
+       eval "export $shlibpath_var"
+      fi
+
+      # Restore saved environment variables
+      for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+      do
+       eval "if test \"\${save_$lt_var+set}\" = set; then
+                $lt_var=\$save_$lt_var; export $lt_var
+             else
+               $lt_unset $lt_var
+             fi"
+      done
+
+      # Now prepare to actually exec the command.
+      exec_cmd="\$cmd$args"
+    else
+      # Display what would be done.
+      if test -n "$shlibpath_var"; then
+       eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+       $ECHO "export $shlibpath_var"
+      fi
+      $ECHO "$cmd$args"
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+    $opt_debug
+    libdirs="$nonopt"
+    admincmds=
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for dir
+      do
+       libdirs="$libdirs $dir"
+      done
+
+      for libdir in $libdirs; do
+       if test -n "$finish_cmds"; then
+         # Do each command in the finish commands.
+         func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+       fi
+       if test -n "$finish_eval"; then
+         # Do the single finish_eval.
+         eval cmds=\"$finish_eval\"
+         $opt_dry_run || eval "$cmds" || admincmds="$admincmds
+       $cmds"
+       fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    $opt_silent && exit $EXIT_SUCCESS
+
+    $ECHO "X----------------------------------------------------------------------" | $Xsed
+    $ECHO "Libraries have been installed in:"
+    for libdir in $libdirs; do
+      $ECHO "   $libdir"
+    done
+    $ECHO
+    $ECHO "If you ever happen to want to link against installed libraries"
+    $ECHO "in a given directory, LIBDIR, you must either use libtool, and"
+    $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'"
+    $ECHO "flag during linking and do at least one of the following:"
+    if test -n "$shlibpath_var"; then
+      $ECHO "   - add LIBDIR to the \`$shlibpath_var' environment variable"
+      $ECHO "     during execution"
+    fi
+    if test -n "$runpath_var"; then
+      $ECHO "   - add LIBDIR to the \`$runpath_var' environment variable"
+      $ECHO "     during linking"
+    fi
+    if test -n "$hardcode_libdir_flag_spec"; then
+      libdir=LIBDIR
+      eval flag=\"$hardcode_libdir_flag_spec\"
+
+      $ECHO "   - use the \`$flag' linker flag"
+    fi
+    if test -n "$admincmds"; then
+      $ECHO "   - have your system administrator run these commands:$admincmds"
+    fi
+    if test -f /etc/ld.so.conf; then
+      $ECHO "   - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+    fi
+    $ECHO
+
+    $ECHO "See any operating system documentation about shared libraries for"
+    case $host in
+      solaris2.[6789]|solaris2.1[0-9])
+        $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+       $ECHO "pages."
+       ;;
+      *)
+        $ECHO "more information, such as the ld(1) and ld.so(8) manual pages."
+        ;;
+    esac
+    $ECHO "X----------------------------------------------------------------------" | $Xsed
+    exit $EXIT_SUCCESS
+}
+
+test "$mode" = finish && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+    $opt_debug
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+       # Allow the use of GNU shtool's install command.
+       $ECHO "X$nonopt" | $GREP shtool >/dev/null; then
+      # Aesthetically quote it.
+      func_quote_for_eval "$nonopt"
+      install_prog="$func_quote_for_eval_result "
+      arg=$1
+      shift
+    else
+      install_prog=
+      arg=$nonopt
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    func_quote_for_eval "$arg"
+    install_prog="$install_prog$func_quote_for_eval_result"
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=no
+    stripme=
+    for arg
+    do
+      if test -n "$dest"; then
+       files="$files $dest"
+       dest=$arg
+       continue
+      fi
+
+      case $arg in
+      -d) isdir=yes ;;
+      -f)
+       case " $install_prog " in
+       *[\\\ /]cp\ *) ;;
+       *) prev=$arg ;;
+       esac
+       ;;
+      -g | -m | -o)
+       prev=$arg
+       ;;
+      -s)
+       stripme=" -s"
+       continue
+       ;;
+      -*)
+       ;;
+      *)
+       # If the previous option needed an argument, then skip it.
+       if test -n "$prev"; then
+         prev=
+       else
+         dest=$arg
+         continue
+       fi
+       ;;
+      esac
+
+      # Aesthetically quote the argument.
+      func_quote_for_eval "$arg"
+      install_prog="$install_prog $func_quote_for_eval_result"
+    done
+
+    test -z "$install_prog" && \
+      func_fatal_help "you must specify an install program"
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prev' option requires an argument"
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+       func_fatal_help "no file or destination specified"
+      else
+       func_fatal_help "you must specify a destination"
+      fi
+    fi
+
+    # Strip any trailing slash from the destination.
+    func_stripname '' '/' "$dest"
+    dest=$func_stripname_result
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=yes
+    if test "$isdir" = yes; then
+      destdir="$dest"
+      destname=
+    else
+      func_dirname_and_basename "$dest" "" "."
+      destdir="$func_dirname_result"
+      destname="$func_basename_result"
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files; shift
+      test "$#" -gt 1 && \
+       func_fatal_help "\`$dest' is not a directory"
+    fi
+    case $destdir in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+       case $file in
+       *.lo) ;;
+       *)
+         func_fatal_help "\`$destdir' must be an absolute directory name"
+         ;;
+       esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case $file in
+      *.$libext)
+       # Do the static libraries later.
+       staticlibs="$staticlibs $file"
+       ;;
+
+      *.la)
+       # Check to see that this really is a libtool archive.
+       func_lalib_unsafe_p "$file" \
+         || func_fatal_help "\`$file' is not a valid libtool archive"
+
+       library_names=
+       old_library=
+       relink_command=
+       func_source "$file"
+
+       # Add the libdir to current_libdirs if it is the destination.
+       if test "X$destdir" = "X$libdir"; then
+         case "$current_libdirs " in
+         *" $libdir "*) ;;
+         *) current_libdirs="$current_libdirs $libdir" ;;
+         esac
+       else
+         # Note the libdir as a future libdir.
+         case "$future_libdirs " in
+         *" $libdir "*) ;;
+         *) future_libdirs="$future_libdirs $libdir" ;;
+         esac
+       fi
+
+       func_dirname "$file" "/" ""
+       dir="$func_dirname_result"
+       dir="$dir$objdir"
+
+       if test -n "$relink_command"; then
+         # Determine the prefix the user has applied to our future dir.
+         inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"`
+
+         # Don't allow the user to place us outside of our expected
+         # location b/c this prevents finding dependent libraries that
+         # are installed to the same prefix.
+         # At present, this check doesn't affect windows .dll's that
+         # are installed into $libdir/../bin (currently, that works fine)
+         # but it's something to keep an eye on.
+         test "$inst_prefix_dir" = "$destdir" && \
+           func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+
+         if test -n "$inst_prefix_dir"; then
+           # Stick the inst_prefix_dir data into the link command.
+           relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+         else
+           relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"`
+         fi
+
+         func_warning "relinking \`$file'"
+         func_show_eval "$relink_command" \
+           'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+       fi
+
+       # See the names of the shared library.
+       set dummy $library_names; shift
+       if test -n "$1"; then
+         realname="$1"
+         shift
+
+         srcname="$realname"
+         test -n "$relink_command" && srcname="$realname"T
+
+         # Install the shared library and build the symlinks.
+         func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \
+             'exit $?'
+         tstripme="$stripme"
+         case $host_os in
+         cygwin* | mingw* | pw32* | cegcc*)
+           case $realname in
+           *.dll.a)
+             tstripme=""
+             ;;
+           esac
+           ;;
+         esac
+         if test -n "$tstripme" && test -n "$striplib"; then
+           func_show_eval "$striplib $destdir/$realname" 'exit $?'
+         fi
+
+         if test "$#" -gt 0; then
+           # Delete the old symlinks, and create new ones.
+           # Try `ln -sf' first, because the `ln' binary might depend on
+           # the symlink we replace!  Solaris /bin/ln does not understand -f,
+           # so we also need to try rm && ln -s.
+           for linkname
+           do
+             test "$linkname" != "$realname" \
+               && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+           done
+         fi
+
+         # Do each command in the postinstall commands.
+         lib="$destdir/$realname"
+         func_execute_cmds "$postinstall_cmds" 'exit $?'
+       fi
+
+       # Install the pseudo-library for information purposes.
+       func_basename "$file"
+       name="$func_basename_result"
+       instname="$dir/$name"i
+       func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+       # Maybe install the static library, too.
+       test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+       ;;
+
+      *.lo)
+       # Install (i.e. copy) a libtool object.
+
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         func_basename "$file"
+         destfile="$func_basename_result"
+         destfile="$destdir/$destfile"
+       fi
+
+       # Deduce the name of the destination old-style object file.
+       case $destfile in
+       *.lo)
+         func_lo2o "$destfile"
+         staticdest=$func_lo2o_result
+         ;;
+       *.$objext)
+         staticdest="$destfile"
+         destfile=
+         ;;
+       *)
+         func_fatal_help "cannot copy a libtool object to \`$destfile'"
+         ;;
+       esac
+
+       # Install the libtool object if requested.
+       test -n "$destfile" && \
+         func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+       # Install the old object if enabled.
+       if test "$build_old_libs" = yes; then
+         # Deduce the name of the old-style object file.
+         func_lo2o "$file"
+         staticobj=$func_lo2o_result
+         func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+       fi
+       exit $EXIT_SUCCESS
+       ;;
+
+      *)
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         func_basename "$file"
+         destfile="$func_basename_result"
+         destfile="$destdir/$destfile"
+       fi
+
+       # If the file is missing, and there is a .exe on the end, strip it
+       # because it is most likely a libtool script we actually want to
+       # install
+       stripped_ext=""
+       case $file in
+         *.exe)
+           if test ! -f "$file"; then
+             func_stripname '' '.exe' "$file"
+             file=$func_stripname_result
+             stripped_ext=".exe"
+           fi
+           ;;
+       esac
+
+       # Do a test to see if this is really a libtool program.
+       case $host in
+       *cygwin* | *mingw*)
+           if func_ltwrapper_executable_p "$file"; then
+             func_ltwrapper_scriptname "$file"
+             wrapper=$func_ltwrapper_scriptname_result
+           else
+             func_stripname '' '.exe' "$file"
+             wrapper=$func_stripname_result
+           fi
+           ;;
+       *)
+           wrapper=$file
+           ;;
+       esac
+       if func_ltwrapper_script_p "$wrapper"; then
+         notinst_deplibs=
+         relink_command=
+
+         func_source "$wrapper"
+
+         # Check the variables that should have been set.
+         test -z "$generated_by_libtool_version" && \
+           func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+
+         finalize=yes
+         for lib in $notinst_deplibs; do
+           # Check to see that each library is installed.
+           libdir=
+           if test -f "$lib"; then
+             func_source "$lib"
+           fi
+           libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test
+           if test -n "$libdir" && test ! -f "$libfile"; then
+             func_warning "\`$lib' has not been installed in \`$libdir'"
+             finalize=no
+           fi
+         done
+
+         relink_command=
+         func_source "$wrapper"
+
+         outputname=
+         if test "$fast_install" = no && test -n "$relink_command"; then
+           $opt_dry_run || {
+             if test "$finalize" = yes; then
+               tmpdir=`func_mktempdir`
+               func_basename "$file$stripped_ext"
+               file="$func_basename_result"
+               outputname="$tmpdir/$file"
+               # Replace the output file specification.
+               relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+               $opt_silent || {
+                 func_quote_for_expand "$relink_command"
+                 eval "func_echo $func_quote_for_expand_result"
+               }
+               if eval "$relink_command"; then :
+                 else
+                 func_error "error: relink \`$file' with the above command before installing it"
+                 $opt_dry_run || ${RM}r "$tmpdir"
+                 continue
+               fi
+               file="$outputname"
+             else
+               func_warning "cannot relink \`$file'"
+             fi
+           }
+         else
+           # Install the binary that we compiled earlier.
+           file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+         fi
+       fi
+
+       # remove .exe since cygwin /usr/bin/install will append another
+       # one anyway
+       case $install_prog,$host in
+       */usr/bin/install*,*cygwin*)
+         case $file:$destfile in
+         *.exe:*.exe)
+           # this is ok
+           ;;
+         *.exe:*)
+           destfile=$destfile.exe
+           ;;
+         *:*.exe)
+           func_stripname '' '.exe' "$destfile"
+           destfile=$func_stripname_result
+           ;;
+         esac
+         ;;
+       esac
+       func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+       $opt_dry_run || if test -n "$outputname"; then
+         ${RM}r "$tmpdir"
+       fi
+       ;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      func_basename "$file"
+      name="$func_basename_result"
+
+      # Set up the ranlib parameters.
+      oldlib="$destdir/$name"
+
+      func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+      if test -n "$stripme" && test -n "$old_striplib"; then
+       func_show_eval "$old_striplib $oldlib" 'exit $?'
+      fi
+
+      # Do each command in the postinstall commands.
+      func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+    done
+
+    test -n "$future_libdirs" && \
+      func_warning "remember to run \`$progname --finish$future_libdirs'"
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      $opt_dry_run && current_libdirs=" -n$current_libdirs"
+      exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+    else
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$mode" = install && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+    $opt_debug
+    my_outputname="$1"
+    my_originator="$2"
+    my_pic_p="${3-no}"
+    my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+    my_dlsyms=
+
+    if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+      if test -n "$NM" && test -n "$global_symbol_pipe"; then
+       my_dlsyms="${my_outputname}S.c"
+      else
+       func_error "not configured to extract global symbols from dlpreopened files"
+      fi
+    fi
+
+    if test -n "$my_dlsyms"; then
+      case $my_dlsyms in
+      "") ;;
+      *.c)
+       # Discover the nlist of each of the dlfiles.
+       nlist="$output_objdir/${my_outputname}.nm"
+
+       func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+       # Parse the name list into a source file.
+       func_verbose "creating $output_objdir/$my_dlsyms"
+
+       $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* External symbol declarations for the compiler. */\
+"
+
+       if test "$dlself" = yes; then
+         func_verbose "generating symbol list for \`$output'"
+
+         $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+         # Add our own program objects to the symbol list.
+         progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+         for progfile in $progfiles; do
+           func_verbose "extracting global C symbols from \`$progfile'"
+           $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'"
+         done
+
+         if test -n "$exclude_expsyms"; then
+           $opt_dry_run || {
+             eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+             eval '$MV "$nlist"T "$nlist"'
+           }
+         fi
+
+         if test -n "$export_symbols_regex"; then
+           $opt_dry_run || {
+             eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+             eval '$MV "$nlist"T "$nlist"'
+           }
+         fi
+
+         # Prepare the list of exported symbols
+         if test -z "$export_symbols"; then
+           export_symbols="$output_objdir/$outputname.exp"
+           $opt_dry_run || {
+             $RM $export_symbols
+             eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+             case $host in
+             *cygwin* | *mingw* | *cegcc* )
+                eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+                eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+               ;;
+             esac
+           }
+         else
+           $opt_dry_run || {
+             eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+             eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+             eval '$MV "$nlist"T "$nlist"'
+             case $host in
+               *cygwin | *mingw* | *cegcc* )
+                 eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+                 eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+                 ;;
+             esac
+           }
+         fi
+       fi
+
+       for dlprefile in $dlprefiles; do
+         func_verbose "extracting global C symbols from \`$dlprefile'"
+         func_basename "$dlprefile"
+         name="$func_basename_result"
+         $opt_dry_run || {
+           eval '$ECHO ": $name " >> "$nlist"'
+           eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+         }
+       done
+
+       $opt_dry_run || {
+         # Make sure we have at least an empty file.
+         test -f "$nlist" || : > "$nlist"
+
+         if test -n "$exclude_expsyms"; then
+           $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+           $MV "$nlist"T "$nlist"
+         fi
+
+         # Try sorting and uniquifying the output.
+         if $GREP -v "^: " < "$nlist" |
+             if sort -k 3 </dev/null >/dev/null 2>&1; then
+               sort -k 3
+             else
+               sort +2
+             fi |
+             uniq > "$nlist"S; then
+           :
+         else
+           $GREP -v "^: " < "$nlist" > "$nlist"S
+         fi
+
+         if test -f "$nlist"S; then
+           eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+         else
+           $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms"
+         fi
+
+         $ECHO >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols.  */
+typedef struct {
+  const char *name;
+  void *address;
+} lt_dlsymlist;
+"
+         case $host in
+         *cygwin* | *mingw* | *cegcc* )
+           $ECHO >> "$output_objdir/$my_dlsyms" "\
+/* DATA imports from DLLs on WIN32 con't be const, because
+   runtime relocations are performed -- see ld's documentation
+   on pseudo-relocs.  */"
+           lt_dlsym_const= ;;
+         *osf5*)
+           echo >> "$output_objdir/$my_dlsyms" "\
+/* This system does not cope well with relocations in const data */"
+           lt_dlsym_const= ;;
+         *)
+           lt_dlsym_const=const ;;
+         esac
+
+         $ECHO >> "$output_objdir/$my_dlsyms" "\
+extern $lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];
+$lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{\
+  { \"$my_originator\", (void *) 0 },"
+
+         case $need_lib_prefix in
+         no)
+           eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+           ;;
+         *)
+           eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+           ;;
+         esac
+         $ECHO >> "$output_objdir/$my_dlsyms" "\
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+       } # !$opt_dry_run
+
+       pic_flag_for_symtable=
+       case "$compile_command " in
+       *" -static "*) ;;
+       *)
+         case $host in
+         # compiling the symbol table file with pic_flag works around
+         # a FreeBSD bug that causes programs to crash when -lm is
+         # linked before any other PIC object.  But we must not use
+         # pic_flag when linking with -static.  The problem exists in
+         # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+         *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+           pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+         *-*-hpux*)
+           pic_flag_for_symtable=" $pic_flag"  ;;
+         *)
+           if test "X$my_pic_p" != Xno; then
+             pic_flag_for_symtable=" $pic_flag"
+           fi
+           ;;
+         esac
+         ;;
+       esac
+       symtab_cflags=
+       for arg in $LTCFLAGS; do
+         case $arg in
+         -pie | -fpie | -fPIE) ;;
+         *) symtab_cflags="$symtab_cflags $arg" ;;
+         esac
+       done
+
+       # Now compile the dynamic symbol file.
+       func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+       # Clean up the generated files.
+       func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+
+       # Transform the symbol file into the correct name.
+       symfileobj="$output_objdir/${my_outputname}S.$objext"
+       case $host in
+       *cygwin* | *mingw* | *cegcc* )
+         if test -f "$output_objdir/$my_outputname.def"; then
+           compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+           finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+         else
+           compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+           finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+         fi
+         ;;
+       *)
+         compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+         finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+         ;;
+       esac
+       ;;
+      *)
+       func_fatal_error "unknown suffix for \`$my_dlsyms'"
+       ;;
+      esac
+    else
+      # We keep going just in case the user didn't refer to
+      # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+      # really was required.
+
+      # Nullify the symbol file.
+      compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+      finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+    fi
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+func_win32_libid ()
+{
+  $opt_debug
+  win32_libid_type="unknown"
+  win32_fileres=`file -L $1 2>/dev/null`
+  case $win32_fileres in
+  *ar\ archive\ import\ library*) # definitely import
+    win32_libid_type="x86 archive import"
+    ;;
+  *ar\ archive*) # could be an import, or static
+    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+       $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then
+      win32_nmres=`eval $NM -f posix -A $1 |
+       $SED -n -e '
+           1,100{
+               / I /{
+                   s,.*,import,
+                   p
+                   q
+               }
+           }'`
+      case $win32_nmres in
+      import*)  win32_libid_type="x86 archive import";;
+      *)        win32_libid_type="x86 archive static";;
+      esac
+    fi
+    ;;
+  *DLL*)
+    win32_libid_type="x86 DLL"
+    ;;
+  *executable*) # but shell scripts are "executable" too...
+    case $win32_fileres in
+    *MS\ Windows\ PE\ Intel*)
+      win32_libid_type="x86 DLL"
+      ;;
+    esac
+    ;;
+  esac
+  $ECHO "$win32_libid_type"
+}
+
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+    $opt_debug
+    f_ex_an_ar_dir="$1"; shift
+    f_ex_an_ar_oldlib="$1"
+    func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?'
+    if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+     :
+    else
+      func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+    fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+    $opt_debug
+    my_gentop="$1"; shift
+    my_oldlibs=${1+"$@"}
+    my_oldobjs=""
+    my_xlib=""
+    my_xabs=""
+    my_xdir=""
+
+    for my_xlib in $my_oldlibs; do
+      # Extract the objects.
+      case $my_xlib in
+       [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+       *) my_xabs=`pwd`"/$my_xlib" ;;
+      esac
+      func_basename "$my_xlib"
+      my_xlib="$func_basename_result"
+      my_xlib_u=$my_xlib
+      while :; do
+        case " $extracted_archives " in
+       *" $my_xlib_u "*)
+         func_arith $extracted_serial + 1
+         extracted_serial=$func_arith_result
+         my_xlib_u=lt$extracted_serial-$my_xlib ;;
+       *) break ;;
+       esac
+      done
+      extracted_archives="$extracted_archives $my_xlib_u"
+      my_xdir="$my_gentop/$my_xlib_u"
+
+      func_mkdir_p "$my_xdir"
+
+      case $host in
+      *-darwin*)
+       func_verbose "Extracting $my_xabs"
+       # Do not bother doing anything if just a dry run
+       $opt_dry_run || {
+         darwin_orig_dir=`pwd`
+         cd $my_xdir || exit $?
+         darwin_archive=$my_xabs
+         darwin_curdir=`pwd`
+         darwin_base_archive=`basename "$darwin_archive"`
+         darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+         if test -n "$darwin_arches"; then
+           darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+           darwin_arch=
+           func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+           for darwin_arch in  $darwin_arches ; do
+             func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+             $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
+             cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+             func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+             cd "$darwin_curdir"
+             $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+           done # $darwin_arches
+            ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+           darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+           darwin_file=
+           darwin_files=
+           for darwin_file in $darwin_filelist; do
+             darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP`
+             $LIPO -create -output "$darwin_file" $darwin_files
+           done # $darwin_filelist
+           $RM -rf unfat-$$
+           cd "$darwin_orig_dir"
+         else
+           cd $darwin_orig_dir
+           func_extract_an_archive "$my_xdir" "$my_xabs"
+         fi # $darwin_arches
+       } # !$opt_dry_run
+       ;;
+      *)
+        func_extract_an_archive "$my_xdir" "$my_xabs"
+       ;;
+      esac
+      my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
+    done
+
+    func_extract_archives_result="$my_oldobjs"
+}
+
+
+
+# func_emit_wrapper_part1 [arg=no]
+#
+# Emit the first part of a libtool wrapper script on stdout.
+# For more information, see the description associated with
+# func_emit_wrapper(), below.
+func_emit_wrapper_part1 ()
+{
+       func_emit_wrapper_part1_arg1=no
+       if test -n "$1" ; then
+         func_emit_wrapper_part1_arg1=$1
+       fi
+
+       $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='${SED} -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variables:
+  generated_by_libtool_version='$macro_version'
+  notinst_deplibs='$notinst_deplibs'
+else
+  # When we are sourced in execute mode, \$file and \$ECHO are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    ECHO=\"$qecho\"
+    file=\"\$0\"
+    # Make sure echo works.
+    if test \"X\$1\" = X--no-reexec; then
+      # Discard the --no-reexec flag, and continue.
+      shift
+    elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then
+      # Yippee, \$ECHO works!
+      :
+    else
+      # Restart under the correct shell, and then maybe \$ECHO will work.
+      exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+    fi
+  fi\
+"
+       $ECHO "\
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\`
+  done
+"
+}
+# end: func_emit_wrapper_part1
+
+# func_emit_wrapper_part2 [arg=no]
+#
+# Emit the second part of a libtool wrapper script on stdout.
+# For more information, see the description associated with
+# func_emit_wrapper(), below.
+func_emit_wrapper_part2 ()
+{
+       func_emit_wrapper_part2_arg1=no
+       if test -n "$1" ; then
+         func_emit_wrapper_part2_arg1=$1
+       fi
+
+       $ECHO "\
+
+  # Usually 'no', except on cygwin/mingw when embedded into
+  # the cwrapper.
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1
+  if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+    # special case for '.'
+    if test \"\$thisdir\" = \".\"; then
+      thisdir=\`pwd\`
+    fi
+    # remove .libs from thisdir
+    case \"\$thisdir\" in
+    *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;;
+    $objdir )   thisdir=. ;;
+    esac
+  fi
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+       if test "$fast_install" = yes; then
+         $ECHO "\
+  program=lt-'$outputname'$exeext
+  progdir=\"\$thisdir/$objdir\"
+
+  if test ! -f \"\$progdir/\$program\" ||
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $MKDIR \"\$progdir\"
+    else
+      $RM \"\$progdir/\$file\"
+    fi"
+
+         $ECHO "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+      else
+       $ECHO \"\$relink_command_output\" >&2
+       $RM \"\$progdir/\$file\"
+       exit 1
+      fi
+    fi
+
+    $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $RM \"\$progdir/\$program\";
+      $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $RM \"\$progdir/\$file\"
+  fi"
+       else
+         $ECHO "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+       fi
+
+       $ECHO "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+       # Export our shlibpath_var if we have one.
+       if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+         $ECHO "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+       fi
+
+       # fixup the dll searchpath if we need to.
+       if test -n "$dllsearchpath"; then
+         $ECHO "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+       fi
+
+       $ECHO "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+"
+       case $host in
+       # Backslashes separate directories on plain windows
+       *-*-mingw | *-*-os2* | *-cegcc*)
+         $ECHO "\
+      exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+         ;;
+
+       *)
+         $ECHO "\
+      exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+         ;;
+       esac
+       $ECHO "\
+      \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+      exit 1
+    fi
+  else
+    # The program doesn't exist.
+    \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+    \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+    $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit 1
+  fi
+fi\
+"
+}
+# end: func_emit_wrapper_part2
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable.  Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take.  If 'yes', then the emitted script
+# will assume that the directory in which it is stored is
+# the $objdir directory.  This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+       func_emit_wrapper_arg1=no
+       if test -n "$1" ; then
+         func_emit_wrapper_arg1=$1
+       fi
+
+       # split this up so that func_emit_cwrapperexe_src
+       # can call each part independently.
+       func_emit_wrapper_part1 "${func_emit_wrapper_arg1}"
+       func_emit_wrapper_part2 "${func_emit_wrapper_arg1}"
+}
+
+
+# func_to_host_path arg
+#
+# Convert paths to host format when used with build tools.
+# Intended for use with "native" mingw (where libtool itself
+# is running under the msys shell), or in the following cross-
+# build environments:
+#    $build          $host
+#    mingw (msys)    mingw  [e.g. native]
+#    cygwin          mingw
+#    *nix + wine     mingw
+# where wine is equipped with the `winepath' executable.
+# In the native mingw case, the (msys) shell automatically
+# converts paths for any non-msys applications it launches,
+# but that facility isn't available from inside the cwrapper.
+# Similar accommodations are necessary for $host mingw and
+# $build cygwin.  Calling this function does no harm for other
+# $host/$build combinations not listed above.
+#
+# ARG is the path (on $build) that should be converted to
+# the proper representation for $host. The result is stored
+# in $func_to_host_path_result.
+func_to_host_path ()
+{
+  func_to_host_path_result="$1"
+  if test -n "$1" ; then
+    case $host in
+      *mingw* )
+        lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+        case $build in
+          *mingw* ) # actually, msys
+            # awkward: cmd appends spaces to result
+            lt_sed_strip_trailing_spaces="s/[ ]*\$//"
+            func_to_host_path_tmp1=`( cmd //c echo "$1" |\
+              $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""`
+            func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+              $SED -e "$lt_sed_naive_backslashify"`
+            ;;
+          *cygwin* )
+            func_to_host_path_tmp1=`cygpath -w "$1"`
+            func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+              $SED -e "$lt_sed_naive_backslashify"`
+            ;;
+          * )
+            # Unfortunately, winepath does not exit with a non-zero
+            # error code, so we are forced to check the contents of
+            # stdout. On the other hand, if the command is not
+            # found, the shell will set an exit code of 127 and print
+            # *an error message* to stdout. So we must check for both
+            # error code of zero AND non-empty stdout, which explains
+            # the odd construction:
+            func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null`
+            if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then
+              func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+                $SED -e "$lt_sed_naive_backslashify"`
+            else
+              # Allow warning below.
+              func_to_host_path_result=""
+            fi
+            ;;
+        esac
+        if test -z "$func_to_host_path_result" ; then
+          func_error "Could not determine host path corresponding to"
+          func_error "  '$1'"
+          func_error "Continuing, but uninstalled executables may not work."
+          # Fallback:
+          func_to_host_path_result="$1"
+        fi
+        ;;
+    esac
+  fi
+}
+# end: func_to_host_path
+
+# func_to_host_pathlist arg
+#
+# Convert pathlists to host format when used with build tools.
+# See func_to_host_path(), above. This function supports the
+# following $build/$host combinations (but does no harm for
+# combinations not listed here):
+#    $build          $host
+#    mingw (msys)    mingw  [e.g. native]
+#    cygwin          mingw
+#    *nix + wine     mingw
+#
+# Path separators are also converted from $build format to
+# $host format. If ARG begins or ends with a path separator
+# character, it is preserved (but converted to $host format)
+# on output.
+#
+# ARG is a pathlist (on $build) that should be converted to
+# the proper representation on $host. The result is stored
+# in $func_to_host_pathlist_result.
+func_to_host_pathlist ()
+{
+  func_to_host_pathlist_result="$1"
+  if test -n "$1" ; then
+    case $host in
+      *mingw* )
+        lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+        # Remove leading and trailing path separator characters from
+        # ARG. msys behavior is inconsistent here, cygpath turns them
+        # into '.;' and ';.', and winepath ignores them completely.
+        func_to_host_pathlist_tmp2="$1"
+        # Once set for this call, this variable should not be
+        # reassigned. It is used in tha fallback case.
+        func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\
+          $SED -e 's|^:*||' -e 's|:*$||'`
+        case $build in
+          *mingw* ) # Actually, msys.
+            # Awkward: cmd appends spaces to result.
+            lt_sed_strip_trailing_spaces="s/[ ]*\$//"
+            func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\
+              $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""`
+            func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\
+              $SED -e "$lt_sed_naive_backslashify"`
+            ;;
+          *cygwin* )
+            func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"`
+            func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\
+              $SED -e "$lt_sed_naive_backslashify"`
+            ;;
+          * )
+            # unfortunately, winepath doesn't convert pathlists
+            func_to_host_pathlist_result=""
+            func_to_host_pathlist_oldIFS=$IFS
+            IFS=:
+            for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do
+              IFS=$func_to_host_pathlist_oldIFS
+              if test -n "$func_to_host_pathlist_f" ; then
+                func_to_host_path "$func_to_host_pathlist_f"
+                if test -n "$func_to_host_path_result" ; then
+                  if test -z "$func_to_host_pathlist_result" ; then
+                    func_to_host_pathlist_result="$func_to_host_path_result"
+                  else
+                    func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result"
+                  fi
+                fi
+              fi
+              IFS=:
+            done
+            IFS=$func_to_host_pathlist_oldIFS
+            ;;
+        esac
+        if test -z "$func_to_host_pathlist_result" ; then
+          func_error "Could not determine the host path(s) corresponding to"
+          func_error "  '$1'"
+          func_error "Continuing, but uninstalled executables may not work."
+          # Fallback. This may break if $1 contains DOS-style drive
+          # specifications. The fix is not to complicate the expression
+          # below, but for the user to provide a working wine installation
+          # with winepath so that path translation in the cross-to-mingw
+          # case works properly.
+          lt_replace_pathsep_nix_to_dos="s|:|;|g"
+          func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\
+            $SED -e "$lt_replace_pathsep_nix_to_dos"`
+        fi
+        # Now, add the leading and trailing path separators back
+        case "$1" in
+          :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result"
+            ;;
+        esac
+        case "$1" in
+          *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;"
+            ;;
+        esac
+        ;;
+    esac
+  fi
+}
+# end: func_to_host_pathlist
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+       cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+   Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+
+   The $output program cannot be directly executed until all the libtool
+   libraries that it depends on are installed.
+
+   This wrapper executable should never be moved out of the build directory.
+   If it is, it will not operate correctly.
+
+   Currently, it simply execs the wrapper *script* "$SHELL $output",
+   but could eventually absorb all of the scripts functionality and
+   exec $objdir/$outputname directly.
+*/
+EOF
+           cat <<"EOF"
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+# define setmode _setmode
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+#  include <io.h>
+#  define HAVE_SETENV
+#  ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+#  endif
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+#ifdef _MSC_VER
+# define S_IXUSR _S_IEXEC
+# define stat _stat
+# ifndef _INTPTR_T_DEFINED
+#  define intptr_t int
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+  defined (__OS2__)
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+#  define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+#  define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+       (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifdef __CYGWIN__
+# define FOPEN_WB "wb"
+#endif
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+  if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+#undef LTWRAPPER_DEBUGPRINTF
+#if defined DEBUGWRAPPER
+# define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args
+static void
+ltwrapper_debugprintf (const char *fmt, ...)
+{
+    va_list args;
+    va_start (args, fmt);
+    (void) vfprintf (stderr, fmt, args);
+    va_end (args);
+}
+#else
+# define LTWRAPPER_DEBUGPRINTF(args)
+#endif
+
+const char *program_name = NULL;
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_fatal (const char *message, ...);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_opt_process_env_set (const char *arg);
+void lt_opt_process_env_prepend (const char *arg);
+void lt_opt_process_env_append (const char *arg);
+int lt_split_name_value (const char *arg, char** name, char** value);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+
+static const char *script_text_part1 =
+EOF
+
+           func_emit_wrapper_part1 yes |
+               $SED -e 's/\([\\"]\)/\\\1/g' \
+                    -e 's/^/  "/' -e 's/$/\\n"/'
+           echo ";"
+           cat <<EOF
+
+static const char *script_text_part2 =
+EOF
+           func_emit_wrapper_part2 yes |
+               $SED -e 's/\([\\"]\)/\\\1/g' \
+                    -e 's/^/  "/' -e 's/$/\\n"/'
+           echo ";"
+
+           cat <<EOF
+const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+           if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+              func_to_host_pathlist "$temp_rpath"
+             cat <<EOF
+const char * LIB_PATH_VALUE   = "$func_to_host_pathlist_result";
+EOF
+           else
+             cat <<"EOF"
+const char * LIB_PATH_VALUE   = "";
+EOF
+           fi
+
+           if test -n "$dllsearchpath"; then
+              func_to_host_pathlist "$dllsearchpath:"
+             cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE   = "$func_to_host_pathlist_result";
+EOF
+           else
+             cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE   = "";
+EOF
+           fi
+
+           if test "$fast_install" = yes; then
+             cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+           else
+             cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+           fi
+
+
+           cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX         "--lt-"
+#define LTWRAPPER_OPTION_PREFIX_LENGTH  5
+
+static const size_t opt_prefix_len         = LTWRAPPER_OPTION_PREFIX_LENGTH;
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+
+static const char *dumpscript_opt       = LTWRAPPER_OPTION_PREFIX "dump-script";
+
+static const size_t env_set_opt_len     = LTWRAPPER_OPTION_PREFIX_LENGTH + 7;
+static const char *env_set_opt          = LTWRAPPER_OPTION_PREFIX "env-set";
+  /* argument is putenv-style "foo=bar", value of foo is set to bar */
+
+static const size_t env_prepend_opt_len = LTWRAPPER_OPTION_PREFIX_LENGTH + 11;
+static const char *env_prepend_opt      = LTWRAPPER_OPTION_PREFIX "env-prepend";
+  /* argument is putenv-style "foo=bar", new value of foo is bar${foo} */
+
+static const size_t env_append_opt_len  = LTWRAPPER_OPTION_PREFIX_LENGTH + 10;
+static const char *env_append_opt       = LTWRAPPER_OPTION_PREFIX "env-append";
+  /* argument is putenv-style "foo=bar", new value of foo is ${foo}bar */
+
+int
+main (int argc, char *argv[])
+{
+  char **newargz;
+  int  newargc;
+  char *tmp_pathspec;
+  char *actual_cwrapper_path;
+  char *actual_cwrapper_name;
+  char *target_name;
+  char *lt_argv_zero;
+  intptr_t rval = 127;
+
+  int i;
+
+  program_name = (char *) xstrdup (base_name (argv[0]));
+  LTWRAPPER_DEBUGPRINTF (("(main) argv[0]      : %s\n", argv[0]));
+  LTWRAPPER_DEBUGPRINTF (("(main) program_name : %s\n", program_name));
+
+  /* very simple arg parsing; don't want to rely on getopt */
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp (argv[i], dumpscript_opt) == 0)
+       {
+EOF
+           case "$host" in
+             *mingw* | *cygwin* )
+               # make stdout use "unix" line endings
+               echo "          setmode(1,_O_BINARY);"
+               ;;
+             esac
+
+           cat <<"EOF"
+         printf ("%s", script_text_part1);
+         printf ("%s", script_text_part2);
+         return 0;
+       }
+    }
+
+  newargz = XMALLOC (char *, argc + 1);
+  tmp_pathspec = find_executable (argv[0]);
+  if (tmp_pathspec == NULL)
+    lt_fatal ("Couldn't find %s", argv[0]);
+  LTWRAPPER_DEBUGPRINTF (("(main) found exe (before symlink chase) at : %s\n",
+                         tmp_pathspec));
+
+  actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+  LTWRAPPER_DEBUGPRINTF (("(main) found exe (after symlink chase) at : %s\n",
+                         actual_cwrapper_path));
+  XFREE (tmp_pathspec);
+
+  actual_cwrapper_name = xstrdup( base_name (actual_cwrapper_path));
+  strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+  /* wrapper name transforms */
+  strendzap (actual_cwrapper_name, ".exe");
+  tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+  XFREE (actual_cwrapper_name);
+  actual_cwrapper_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  /* target_name transforms -- use actual target program name; might have lt- prefix */
+  target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+  strendzap (target_name, ".exe");
+  tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+  XFREE (target_name);
+  target_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  LTWRAPPER_DEBUGPRINTF (("(main) libtool target name: %s\n",
+                         target_name));
+EOF
+
+           cat <<EOF
+  newargz[0] =
+    XMALLOC (char, (strlen (actual_cwrapper_path) +
+                   strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+  strcpy (newargz[0], actual_cwrapper_path);
+  strcat (newargz[0], "$objdir");
+  strcat (newargz[0], "/");
+EOF
+
+           cat <<"EOF"
+  /* stop here, and copy so we don't have to do this twice */
+  tmp_pathspec = xstrdup (newargz[0]);
+
+  /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+  strcat (newargz[0], actual_cwrapper_name);
+
+  /* DO want the lt- prefix here if it exists, so use target_name */
+  lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+  XFREE (tmp_pathspec);
+  tmp_pathspec = NULL;
+EOF
+
+           case $host_os in
+             mingw*)
+           cat <<"EOF"
+  {
+    char* p;
+    while ((p = strchr (newargz[0], '\\')) != NULL)
+      {
+       *p = '/';
+      }
+    while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+      {
+       *p = '/';
+      }
+  }
+EOF
+           ;;
+           esac
+
+           cat <<"EOF"
+  XFREE (target_name);
+  XFREE (actual_cwrapper_path);
+  XFREE (actual_cwrapper_name);
+
+  lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+  lt_setenv ("DUALCASE", "1");  /* for MSK sh */
+  lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+  lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+
+  newargc=0;
+  for (i = 1; i < argc; i++)
+    {
+      if (strncmp (argv[i], env_set_opt, env_set_opt_len) == 0)
+        {
+          if (argv[i][env_set_opt_len] == '=')
+            {
+              const char *p = argv[i] + env_set_opt_len + 1;
+              lt_opt_process_env_set (p);
+            }
+          else if (argv[i][env_set_opt_len] == '\0' && i + 1 < argc)
+            {
+              lt_opt_process_env_set (argv[++i]); /* don't copy */
+            }
+          else
+            lt_fatal ("%s missing required argument", env_set_opt);
+          continue;
+        }
+      if (strncmp (argv[i], env_prepend_opt, env_prepend_opt_len) == 0)
+        {
+          if (argv[i][env_prepend_opt_len] == '=')
+            {
+              const char *p = argv[i] + env_prepend_opt_len + 1;
+              lt_opt_process_env_prepend (p);
+            }
+          else if (argv[i][env_prepend_opt_len] == '\0' && i + 1 < argc)
+            {
+              lt_opt_process_env_prepend (argv[++i]); /* don't copy */
+            }
+          else
+            lt_fatal ("%s missing required argument", env_prepend_opt);
+          continue;
+        }
+      if (strncmp (argv[i], env_append_opt, env_append_opt_len) == 0)
+        {
+          if (argv[i][env_append_opt_len] == '=')
+            {
+              const char *p = argv[i] + env_append_opt_len + 1;
+              lt_opt_process_env_append (p);
+            }
+          else if (argv[i][env_append_opt_len] == '\0' && i + 1 < argc)
+            {
+              lt_opt_process_env_append (argv[++i]); /* don't copy */
+            }
+          else
+            lt_fatal ("%s missing required argument", env_append_opt);
+          continue;
+        }
+      if (strncmp (argv[i], ltwrapper_option_prefix, opt_prefix_len) == 0)
+        {
+          /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+             namespace, but it is not one of the ones we know about and
+             have already dealt with, above (inluding dump-script), then
+             report an error. Otherwise, targets might begin to believe
+             they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+             namespace. The first time any user complains about this, we'll
+             need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+             or a configure.ac-settable value.
+           */
+          lt_fatal ("Unrecognized option in %s namespace: '%s'",
+                    ltwrapper_option_prefix, argv[i]);
+        }
+      /* otherwise ... */
+      newargz[++newargc] = xstrdup (argv[i]);
+    }
+  newargz[++newargc] = NULL;
+
+  LTWRAPPER_DEBUGPRINTF     (("(main) lt_argv_zero : %s\n", (lt_argv_zero ? lt_argv_zero : "<NULL>")));
+  for (i = 0; i < newargc; i++)
+    {
+      LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d]   : %s\n", i, (newargz[i] ? newargz[i] : "<NULL>")));
+    }
+
+EOF
+
+           case $host_os in
+             mingw*)
+               cat <<"EOF"
+  /* execv doesn't actually work on mingw as expected on unix */
+  rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+  if (rval == -1)
+    {
+      /* failed to start process */
+      LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno));
+      return 127;
+    }
+  return rval;
+EOF
+               ;;
+             *)
+               cat <<"EOF"
+  execv (lt_argv_zero, newargz);
+  return rval; /* =127, but avoids unused variable warning */
+EOF
+               ;;
+           esac
+
+           cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+  void *p = (void *) malloc (num);
+  if (!p)
+    lt_fatal ("Memory exhausted");
+
+  return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+  return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+                         string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+  const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  /* Skip over the disk name in MSDOS pathnames. */
+  if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+    name += 2;
+#endif
+
+  for (base = name; *name; name++)
+    if (IS_DIR_SEPARATOR (*name))
+      base = name + 1;
+  return base;
+}
+
+int
+check_executable (const char *path)
+{
+  struct stat st;
+
+  LTWRAPPER_DEBUGPRINTF (("(check_executable)  : %s\n",
+                         path ? (*path ? path : "EMPTY!") : "NULL!"));
+  if ((!path) || (!*path))
+    return 0;
+
+  if ((stat (path, &st) >= 0)
+      && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+    return 1;
+  else
+    return 0;
+}
+
+int
+make_executable (const char *path)
+{
+  int rval = 0;
+  struct stat st;
+
+  LTWRAPPER_DEBUGPRINTF (("(make_executable)   : %s\n",
+                         path ? (*path ? path : "EMPTY!") : "NULL!"));
+  if ((!path) || (!*path))
+    return 0;
+
+  if (stat (path, &st) >= 0)
+    {
+      rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+    }
+  return rval;
+}
+
+/* Searches for the full path of the wrapper.  Returns
+   newly allocated full path name if found, NULL otherwise
+   Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+  int has_slash = 0;
+  const char *p;
+  const char *p_next;
+  /* static buffer for getcwd */
+  char tmp[LT_PATHMAX + 1];
+  int tmp_len;
+  char *concat_name;
+
+  LTWRAPPER_DEBUGPRINTF (("(find_executable)   : %s\n",
+                         wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"));
+
+  if ((wrapper == NULL) || (*wrapper == '\0'))
+    return NULL;
+
+  /* Absolute path? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+    {
+      concat_name = xstrdup (wrapper);
+      if (check_executable (concat_name))
+       return concat_name;
+      XFREE (concat_name);
+    }
+  else
+    {
+#endif
+      if (IS_DIR_SEPARATOR (wrapper[0]))
+       {
+         concat_name = xstrdup (wrapper);
+         if (check_executable (concat_name))
+           return concat_name;
+         XFREE (concat_name);
+       }
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+    }
+#endif
+
+  for (p = wrapper; *p; p++)
+    if (*p == '/')
+      {
+       has_slash = 1;
+       break;
+      }
+  if (!has_slash)
+    {
+      /* no slashes; search PATH */
+      const char *path = getenv ("PATH");
+      if (path != NULL)
+       {
+         for (p = path; *p; p = p_next)
+           {
+             const char *q;
+             size_t p_len;
+             for (q = p; *q; q++)
+               if (IS_PATH_SEPARATOR (*q))
+                 break;
+             p_len = q - p;
+             p_next = (*q == '\0' ? q : q + 1);
+             if (p_len == 0)
+               {
+                 /* empty path: current directory */
+                 if (getcwd (tmp, LT_PATHMAX) == NULL)
+                   lt_fatal ("getcwd failed");
+                 tmp_len = strlen (tmp);
+                 concat_name =
+                   XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+                 memcpy (concat_name, tmp, tmp_len);
+                 concat_name[tmp_len] = '/';
+                 strcpy (concat_name + tmp_len + 1, wrapper);
+               }
+             else
+               {
+                 concat_name =
+                   XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+                 memcpy (concat_name, p, p_len);
+                 concat_name[p_len] = '/';
+                 strcpy (concat_name + p_len + 1, wrapper);
+               }
+             if (check_executable (concat_name))
+               return concat_name;
+             XFREE (concat_name);
+           }
+       }
+      /* not found in PATH; assume curdir */
+    }
+  /* Relative path | not found in path: prepend cwd */
+  if (getcwd (tmp, LT_PATHMAX) == NULL)
+    lt_fatal ("getcwd failed");
+  tmp_len = strlen (tmp);
+  concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+  memcpy (concat_name, tmp, tmp_len);
+  concat_name[tmp_len] = '/';
+  strcpy (concat_name + tmp_len + 1, wrapper);
+
+  if (check_executable (concat_name))
+    return concat_name;
+  XFREE (concat_name);
+  return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+  return xstrdup (pathspec);
+#else
+  char buf[LT_PATHMAX];
+  struct stat s;
+  char *tmp_pathspec = xstrdup (pathspec);
+  char *p;
+  int has_symlinks = 0;
+  while (strlen (tmp_pathspec) && !has_symlinks)
+    {
+      LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n",
+                             tmp_pathspec));
+      if (lstat (tmp_pathspec, &s) == 0)
+       {
+         if (S_ISLNK (s.st_mode) != 0)
+           {
+             has_symlinks = 1;
+             break;
+           }
+
+         /* search backwards for last DIR_SEPARATOR */
+         p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+         while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+           p--;
+         if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+           {
+             /* no more DIR_SEPARATORS left */
+             break;
+           }
+         *p = '\0';
+       }
+      else
+       {
+         char *errstr = strerror (errno);
+         lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr);
+       }
+    }
+  XFREE (tmp_pathspec);
+
+  if (!has_symlinks)
+    {
+      return xstrdup (pathspec);
+    }
+
+  tmp_pathspec = realpath (pathspec, buf);
+  if (tmp_pathspec == 0)
+    {
+      lt_fatal ("Could not follow symlinks for %s", pathspec);
+    }
+  return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+  size_t len, patlen;
+
+  assert (str != NULL);
+  assert (pat != NULL);
+
+  len = strlen (str);
+  patlen = strlen (pat);
+
+  if (patlen <= len)
+    {
+      str += len - patlen;
+      if (strcmp (str, pat) == 0)
+       *str = '\0';
+    }
+  return str;
+}
+
+static void
+lt_error_core (int exit_status, const char *mode,
+              const char *message, va_list ap)
+{
+  fprintf (stderr, "%s: %s: ", program_name, mode);
+  vfprintf (stderr, message, ap);
+  fprintf (stderr, ".\n");
+
+  if (exit_status >= 0)
+    exit (exit_status);
+}
+
+void
+lt_fatal (const char *message, ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  lt_error_core (EXIT_FAILURE, "FATAL", message, ap);
+  va_end (ap);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+  LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n",
+                          (name ? name : "<NULL>"),
+                          (value ? value : "<NULL>")));
+  {
+#ifdef HAVE_SETENV
+    /* always make a copy, for consistency with !HAVE_SETENV */
+    char *str = xstrdup (value);
+    setenv (name, str, 1);
+#else
+    int len = strlen (name) + 1 + strlen (value) + 1;
+    char *str = XMALLOC (char, len);
+    sprintf (str, "%s=%s", name, value);
+    if (putenv (str) != EXIT_SUCCESS)
+      {
+        XFREE (str);
+      }
+#endif
+  }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+  char *new_value;
+  if (orig_value && *orig_value)
+    {
+      int orig_value_len = strlen (orig_value);
+      int add_len = strlen (add);
+      new_value = XMALLOC (char, add_len + orig_value_len + 1);
+      if (to_end)
+        {
+          strcpy (new_value, orig_value);
+          strcpy (new_value + orig_value_len, add);
+        }
+      else
+        {
+          strcpy (new_value, add);
+          strcpy (new_value + add_len, orig_value);
+        }
+    }
+  else
+    {
+      new_value = xstrdup (add);
+    }
+  return new_value;
+}
+
+int
+lt_split_name_value (const char *arg, char** name, char** value)
+{
+  const char *p;
+  int len;
+  if (!arg || !*arg)
+    return 1;
+
+  p = strchr (arg, (int)'=');
+
+  if (!p)
+    return 1;
+
+  *value = xstrdup (++p);
+
+  len = strlen (arg) - strlen (*value);
+  *name = XMALLOC (char, len);
+  strncpy (*name, arg, len-1);
+  (*name)[len - 1] = '\0';
+
+  return 0;
+}
+
+void
+lt_opt_process_env_set (const char *arg)
+{
+  char *name = NULL;
+  char *value = NULL;
+
+  if (lt_split_name_value (arg, &name, &value) != 0)
+    {
+      XFREE (name);
+      XFREE (value);
+      lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg);
+    }
+
+  lt_setenv (name, value);
+  XFREE (name);
+  XFREE (value);
+}
+
+void
+lt_opt_process_env_prepend (const char *arg)
+{
+  char *name = NULL;
+  char *value = NULL;
+  char *new_value = NULL;
+
+  if (lt_split_name_value (arg, &name, &value) != 0)
+    {
+      XFREE (name);
+      XFREE (value);
+      lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg);
+    }
+
+  new_value = lt_extend_str (getenv (name), value, 0);
+  lt_setenv (name, new_value);
+  XFREE (new_value);
+  XFREE (name);
+  XFREE (value);
+}
+
+void
+lt_opt_process_env_append (const char *arg)
+{
+  char *name = NULL;
+  char *value = NULL;
+  char *new_value = NULL;
+
+  if (lt_split_name_value (arg, &name, &value) != 0)
+    {
+      XFREE (name);
+      XFREE (value);
+      lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg);
+    }
+
+  new_value = lt_extend_str (getenv (name), value, 1);
+  lt_setenv (name, new_value);
+  XFREE (new_value);
+  XFREE (name);
+  XFREE (value);
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+  LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+                          (name ? name : "<NULL>"),
+                          (value ? value : "<NULL>")));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      /* some systems can't cope with a ':'-terminated path #' */
+      int len = strlen (new_value);
+      while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+        {
+          new_value[len-1] = '\0';
+        }
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+  LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+                          (name ? name : "<NULL>"),
+                          (value ? value : "<NULL>")));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_mode_link arg...
+func_mode_link ()
+{
+    $opt_debug
+    case $host in
+    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # which system we are compiling for in order to pass an extra
+      # flag for every libtool invocation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll which has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    libtool_args=$nonopt
+    base_compile="$nonopt $@"
+    compile_command=$nonopt
+    finalize_command=$nonopt
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    old_deplibs=
+    compiler_flags=
+    linker_flags=
+    dllsearchpath=
+    lib_search_path=`pwd`
+    inst_prefix_dir=
+    new_inherited_linker_flags=
+
+    avoid_version=no
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    ltlibs=
+    module=no
+    no_install=no
+    objs=
+    non_pic_objects=
+    precious_files_regex=
+    prefer_static_libs=no
+    preload=no
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+    vinfo_number=no
+    weak_libs=
+    single_module="${wl}-single_module"
+    func_infer_tag $base_compile
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case $arg in
+      -shared)
+       test "$build_libtool_libs" != yes && \
+         func_fatal_configuration "can not build a shared library"
+       build_old_libs=no
+       break
+       ;;
+      -all-static | -static | -static-libtool-libs)
+       case $arg in
+       -all-static)
+         if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+           func_warning "complete static linking is impossible in this configuration"
+         fi
+         if test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+         prefer_static_libs=yes
+         ;;
+       -static)
+         if test -z "$pic_flag" && test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+         prefer_static_libs=built
+         ;;
+       -static-libtool-libs)
+         if test -z "$pic_flag" && test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+         prefer_static_libs=yes
+         ;;
+       esac
+       build_libtool_libs=no
+       build_old_libs=yes
+       break
+       ;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test "$#" -gt 0; do
+      arg="$1"
+      shift
+      func_quote_for_eval "$arg"
+      qarg=$func_quote_for_eval_unquoted_result
+      func_append libtool_args " $func_quote_for_eval_result"
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+       case $prev in
+       output)
+         func_append compile_command " @OUTPUT@"
+         func_append finalize_command " @OUTPUT@"
+         ;;
+       esac
+
+       case $prev in
+       dlfiles|dlprefiles)
+         if test "$preload" = no; then
+           # Add the symbol object into the linking commands.
+           func_append compile_command " @SYMFILE@"
+           func_append finalize_command " @SYMFILE@"
+           preload=yes
+         fi
+         case $arg in
+         *.la | *.lo) ;;  # We handle these cases below.
+         force)
+           if test "$dlself" = no; then
+             dlself=needless
+             export_dynamic=yes
+           fi
+           prev=
+           continue
+           ;;
+         self)
+           if test "$prev" = dlprefiles; then
+             dlself=yes
+           elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+             dlself=yes
+           else
+             dlself=needless
+             export_dynamic=yes
+           fi
+           prev=
+           continue
+           ;;
+         *)
+           if test "$prev" = dlfiles; then
+             dlfiles="$dlfiles $arg"
+           else
+             dlprefiles="$dlprefiles $arg"
+           fi
+           prev=
+           continue
+           ;;
+         esac
+         ;;
+       expsyms)
+         export_symbols="$arg"
+         test -f "$arg" \
+           || func_fatal_error "symbol file \`$arg' does not exist"
+         prev=
+         continue
+         ;;
+       expsyms_regex)
+         export_symbols_regex="$arg"
+         prev=
+         continue
+         ;;
+       framework)
+         case $host in
+           *-*-darwin*)
+             case "$deplibs " in
+               *" $qarg.ltframework "*) ;;
+               *) deplibs="$deplibs $qarg.ltframework" # this is fixed later
+                  ;;
+             esac
+             ;;
+         esac
+         prev=
+         continue
+         ;;
+       inst_prefix)
+         inst_prefix_dir="$arg"
+         prev=
+         continue
+         ;;
+       objectlist)
+         if test -f "$arg"; then
+           save_arg=$arg
+           moreargs=
+           for fil in `cat "$save_arg"`
+           do
+#            moreargs="$moreargs $fil"
+             arg=$fil
+             # A libtool-controlled object.
+
+             # Check to see that this really is a libtool object.
+             if func_lalib_unsafe_p "$arg"; then
+               pic_object=
+               non_pic_object=
+
+               # Read the .lo file
+               func_source "$arg"
+
+               if test -z "$pic_object" ||
+                  test -z "$non_pic_object" ||
+                  test "$pic_object" = none &&
+                  test "$non_pic_object" = none; then
+                 func_fatal_error "cannot find name of object for \`$arg'"
+               fi
+
+               # Extract subdirectory from the argument.
+               func_dirname "$arg" "/" ""
+               xdir="$func_dirname_result"
+
+               if test "$pic_object" != none; then
+                 # Prepend the subdirectory the object is found in.
+                 pic_object="$xdir$pic_object"
+
+                 if test "$prev" = dlfiles; then
+                   if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+                     dlfiles="$dlfiles $pic_object"
+                     prev=
+                     continue
+                   else
+                     # If libtool objects are unsupported, then we need to preload.
+                     prev=dlprefiles
+                   fi
+                 fi
+
+                 # CHECK ME:  I think I busted this.  -Ossama
+                 if test "$prev" = dlprefiles; then
+                   # Preload the old-style object.
+                   dlprefiles="$dlprefiles $pic_object"
+                   prev=
+                 fi
+
+                 # A PIC object.
+                 func_append libobjs " $pic_object"
+                 arg="$pic_object"
+               fi
+
+               # Non-PIC object.
+               if test "$non_pic_object" != none; then
+                 # Prepend the subdirectory the object is found in.
+                 non_pic_object="$xdir$non_pic_object"
+
+                 # A standard non-PIC object
+                 func_append non_pic_objects " $non_pic_object"
+                 if test -z "$pic_object" || test "$pic_object" = none ; then
+                   arg="$non_pic_object"
+                 fi
+               else
+                 # If the PIC object exists, use it instead.
+                 # $xdir was prepended to $pic_object above.
+                 non_pic_object="$pic_object"
+                 func_append non_pic_objects " $non_pic_object"
+               fi
+             else
+               # Only an error if not doing a dry-run.
+               if $opt_dry_run; then
+                 # Extract subdirectory from the argument.
+                 func_dirname "$arg" "/" ""
+                 xdir="$func_dirname_result"
+
+                 func_lo2o "$arg"
+                 pic_object=$xdir$objdir/$func_lo2o_result
+                 non_pic_object=$xdir$func_lo2o_result
+                 func_append libobjs " $pic_object"
+                 func_append non_pic_objects " $non_pic_object"
+               else
+                 func_fatal_error "\`$arg' is not a valid libtool object"
+               fi
+             fi
+           done
+         else
+           func_fatal_error "link input file \`$arg' does not exist"
+         fi
+         arg=$save_arg
+         prev=
+         continue
+         ;;
+       precious_regex)
+         precious_files_regex="$arg"
+         prev=
+         continue
+         ;;
+       release)
+         release="-$arg"
+         prev=
+         continue
+         ;;
+       rpath | xrpath)
+         # We need an absolute path.
+         case $arg in
+         [\\/]* | [A-Za-z]:[\\/]*) ;;
+         *)
+           func_fatal_error "only absolute run-paths are allowed"
+           ;;
+         esac
+         if test "$prev" = rpath; then
+           case "$rpath " in
+           *" $arg "*) ;;
+           *) rpath="$rpath $arg" ;;
+           esac
+         else
+           case "$xrpath " in
+           *" $arg "*) ;;
+           *) xrpath="$xrpath $arg" ;;
+           esac
+         fi
+         prev=
+         continue
+         ;;
+       shrext)
+         shrext_cmds="$arg"
+         prev=
+         continue
+         ;;
+       weak)
+         weak_libs="$weak_libs $arg"
+         prev=
+         continue
+         ;;
+       xcclinker)
+         linker_flags="$linker_flags $qarg"
+         compiler_flags="$compiler_flags $qarg"
+         prev=
+         func_append compile_command " $qarg"
+         func_append finalize_command " $qarg"
+         continue
+         ;;
+       xcompiler)
+         compiler_flags="$compiler_flags $qarg"
+         prev=
+         func_append compile_command " $qarg"
+         func_append finalize_command " $qarg"
+         continue
+         ;;
+       xlinker)
+         linker_flags="$linker_flags $qarg"
+         compiler_flags="$compiler_flags $wl$qarg"
+         prev=
+         func_append compile_command " $wl$qarg"
+         func_append finalize_command " $wl$qarg"
+         continue
+         ;;
+       *)
+         eval "$prev=\"\$arg\""
+         prev=
+         continue
+         ;;
+       esac
+      fi # test -n "$prev"
+
+      prevarg="$arg"
+
+      case $arg in
+      -all-static)
+       if test -n "$link_static_flag"; then
+         # See comment for -static flag below, for more details.
+         func_append compile_command " $link_static_flag"
+         func_append finalize_command " $link_static_flag"
+       fi
+       continue
+       ;;
+
+      -allow-undefined)
+       # FIXME: remove this flag sometime in the future.
+       func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+       ;;
+
+      -avoid-version)
+       avoid_version=yes
+       continue
+       ;;
+
+      -dlopen)
+       prev=dlfiles
+       continue
+       ;;
+
+      -dlpreopen)
+       prev=dlprefiles
+       continue
+       ;;
+
+      -export-dynamic)
+       export_dynamic=yes
+       continue
+       ;;
+
+      -export-symbols | -export-symbols-regex)
+       if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+         func_fatal_error "more than one -exported-symbols argument is not allowed"
+       fi
+       if test "X$arg" = "X-export-symbols"; then
+         prev=expsyms
+       else
+         prev=expsyms_regex
+       fi
+       continue
+       ;;
+
+      -framework)
+       prev=framework
+       continue
+       ;;
+
+      -inst-prefix-dir)
+       prev=inst_prefix
+       continue
+       ;;
+
+      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+      # so, if we see these flags be careful not to treat them like -L
+      -L[A-Z][A-Z]*:*)
+       case $with_gcc/$host in
+       no/*-*-irix* | /*-*-irix*)
+         func_append compile_command " $arg"
+         func_append finalize_command " $arg"
+         ;;
+       esac
+       continue
+       ;;
+
+      -L*)
+       func_stripname '-L' '' "$arg"
+       dir=$func_stripname_result
+       if test -z "$dir"; then
+         if test "$#" -gt 0; then
+           func_fatal_error "require no space between \`-L' and \`$1'"
+         else
+           func_fatal_error "need path for \`-L' option"
+         fi
+       fi
+       # We need an absolute path.
+       case $dir in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       *)
+         absdir=`cd "$dir" && pwd`
+         test -z "$absdir" && \
+           func_fatal_error "cannot determine absolute directory name of \`$dir'"
+         dir="$absdir"
+         ;;
+       esac
+       case "$deplibs " in
+       *" -L$dir "*) ;;
+       *)
+         deplibs="$deplibs -L$dir"
+         lib_search_path="$lib_search_path $dir"
+         ;;
+       esac
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+         testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'`
+         case :$dllsearchpath: in
+         *":$dir:"*) ;;
+         ::) dllsearchpath=$dir;;
+         *) dllsearchpath="$dllsearchpath:$dir";;
+         esac
+         case :$dllsearchpath: in
+         *":$testbindir:"*) ;;
+         ::) dllsearchpath=$testbindir;;
+         *) dllsearchpath="$dllsearchpath:$testbindir";;
+         esac
+         ;;
+       esac
+       continue
+       ;;
+
+      -l*)
+       if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+         case $host in
+         *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc*)
+           # These systems don't actually have a C or math library (as such)
+           continue
+           ;;
+         *-*-os2*)
+           # These systems don't actually have a C library (as such)
+           test "X$arg" = "X-lc" && continue
+           ;;
+         *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+           # Do not include libc due to us having libc/libc_r.
+           test "X$arg" = "X-lc" && continue
+           ;;
+         *-*-rhapsody* | *-*-darwin1.[012])
+           # Rhapsody C and math libraries are in the System framework
+           deplibs="$deplibs System.ltframework"
+           continue
+           ;;
+         *-*-sco3.2v5* | *-*-sco5v6*)
+           # Causes problems with __ctype
+           test "X$arg" = "X-lc" && continue
+           ;;
+         *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+           # Compiler inserts libc in the correct place for threads to work
+           test "X$arg" = "X-lc" && continue
+           ;;
+         esac
+       elif test "X$arg" = "X-lc_r"; then
+        case $host in
+        *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+          # Do not include libc_r directly, use -pthread flag.
+          continue
+          ;;
+        esac
+       fi
+       deplibs="$deplibs $arg"
+       continue
+       ;;
+
+      -module)
+       module=yes
+       continue
+       ;;
+
+      # Tru64 UNIX uses -model [arg] to determine the layout of C++
+      # classes, name mangling, and exception handling.
+      # Darwin uses the -arch flag to determine output architecture.
+      -model|-arch|-isysroot)
+       compiler_flags="$compiler_flags $arg"
+       func_append compile_command " $arg"
+       func_append finalize_command " $arg"
+       prev=xcompiler
+       continue
+       ;;
+
+      -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+       compiler_flags="$compiler_flags $arg"
+       func_append compile_command " $arg"
+       func_append finalize_command " $arg"
+       case "$new_inherited_linker_flags " in
+           *" $arg "*) ;;
+           * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;;
+       esac
+       continue
+       ;;
+
+      -multi_module)
+       single_module="${wl}-multi_module"
+       continue
+       ;;
+
+      -no-fast-install)
+       fast_install=no
+       continue
+       ;;
+
+      -no-install)
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+         # The PATH hackery in wrapper scripts is required on Windows
+         # and Darwin in order for the loader to find any dlls it needs.
+         func_warning "\`-no-install' is ignored for $host"
+         func_warning "assuming \`-no-fast-install' instead"
+         fast_install=no
+         ;;
+       *) no_install=yes ;;
+       esac
+       continue
+       ;;
+
+      -no-undefined)
+       allow_undefined=no
+       continue
+       ;;
+
+      -objectlist)
+       prev=objectlist
+       continue
+       ;;
+
+      -o) prev=output ;;
+
+      -precious-files-regex)
+       prev=precious_regex
+       continue
+       ;;
+
+      -release)
+       prev=release
+       continue
+       ;;
+
+      -rpath)
+       prev=rpath
+       continue
+       ;;
+
+      -R)
+       prev=xrpath
+       continue
+       ;;
+
+      -R*)
+       func_stripname '-R' '' "$arg"
+       dir=$func_stripname_result
+       # We need an absolute path.
+       case $dir in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       *)
+         func_fatal_error "only absolute run-paths are allowed"
+         ;;
+       esac
+       case "$xrpath " in
+       *" $dir "*) ;;
+       *) xrpath="$xrpath $dir" ;;
+       esac
+       continue
+       ;;
+
+      -shared)
+       # The effects of -shared are defined in a previous loop.
+       continue
+       ;;
+
+      -shrext)
+       prev=shrext
+       continue
+       ;;
+
+      -static | -static-libtool-libs)
+       # The effects of -static are defined in a previous loop.
+       # We used to do the same as -all-static on platforms that
+       # didn't have a PIC flag, but the assumption that the effects
+       # would be equivalent was wrong.  It would break on at least
+       # Digital Unix and AIX.
+       continue
+       ;;
+
+      -thread-safe)
+       thread_safe=yes
+       continue
+       ;;
+
+      -version-info)
+       prev=vinfo
+       continue
+       ;;
+
+      -version-number)
+       prev=vinfo
+       vinfo_number=yes
+       continue
+       ;;
+
+      -weak)
+        prev=weak
+       continue
+       ;;
+
+      -Wc,*)
+       func_stripname '-Wc,' '' "$arg"
+       args=$func_stripname_result
+       arg=
+       save_ifs="$IFS"; IFS=','
+       for flag in $args; do
+         IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+         arg="$arg $wl$func_quote_for_eval_result"
+         compiler_flags="$compiler_flags $func_quote_for_eval_result"
+       done
+       IFS="$save_ifs"
+       func_stripname ' ' '' "$arg"
+       arg=$func_stripname_result
+       ;;
+
+      -Wl,*)
+       func_stripname '-Wl,' '' "$arg"
+       args=$func_stripname_result
+       arg=
+       save_ifs="$IFS"; IFS=','
+       for flag in $args; do
+         IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+         arg="$arg $wl$func_quote_for_eval_result"
+         compiler_flags="$compiler_flags $wl$func_quote_for_eval_result"
+         linker_flags="$linker_flags $func_quote_for_eval_result"
+       done
+       IFS="$save_ifs"
+       func_stripname ' ' '' "$arg"
+       arg=$func_stripname_result
+       ;;
+
+      -Xcompiler)
+       prev=xcompiler
+       continue
+       ;;
+
+      -Xlinker)
+       prev=xlinker
+       continue
+       ;;
+
+      -XCClinker)
+       prev=xcclinker
+       continue
+       ;;
+
+      # -msg_* for osf cc
+      -msg_*)
+       func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+       ;;
+
+      # -64, -mips[0-9] enable 64-bit mode on the SGI compiler
+      # -r[0-9][0-9]* specifies the processor on the SGI compiler
+      # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler
+      # +DA*, +DD* enable 64-bit mode on the HP compiler
+      # -q* pass through compiler args for the IBM compiler
+      # -m*, -t[45]*, -txscale* pass through architecture-specific
+      # compiler args for GCC
+      # -F/path gives path to uninstalled frameworks, gcc on darwin
+      # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC
+      # @file GCC response files
+      -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+      -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*)
+        func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+        func_append compile_command " $arg"
+        func_append finalize_command " $arg"
+        compiler_flags="$compiler_flags $arg"
+        continue
+        ;;
+
+      # Some other compiler flag.
+      -* | +*)
+        func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+       ;;
+
+      *.$objext)
+       # A standard object.
+       objs="$objs $arg"
+       ;;
+
+      *.lo)
+       # A libtool-controlled object.
+
+       # Check to see that this really is a libtool object.
+       if func_lalib_unsafe_p "$arg"; then
+         pic_object=
+         non_pic_object=
+
+         # Read the .lo file
+         func_source "$arg"
+
+         if test -z "$pic_object" ||
+            test -z "$non_pic_object" ||
+            test "$pic_object" = none &&
+            test "$non_pic_object" = none; then
+           func_fatal_error "cannot find name of object for \`$arg'"
+         fi
+
+         # Extract subdirectory from the argument.
+         func_dirname "$arg" "/" ""
+         xdir="$func_dirname_result"
+
+         if test "$pic_object" != none; then
+           # Prepend the subdirectory the object is found in.
+           pic_object="$xdir$pic_object"
+
+           if test "$prev" = dlfiles; then
+             if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+               dlfiles="$dlfiles $pic_object"
+               prev=
+               continue
+             else
+               # If libtool objects are unsupported, then we need to preload.
+               prev=dlprefiles
+             fi
+           fi
+
+           # CHECK ME:  I think I busted this.  -Ossama
+           if test "$prev" = dlprefiles; then
+             # Preload the old-style object.
+             dlprefiles="$dlprefiles $pic_object"
+             prev=
+           fi
+
+           # A PIC object.
+           func_append libobjs " $pic_object"
+           arg="$pic_object"
+         fi
+
+         # Non-PIC object.
+         if test "$non_pic_object" != none; then
+           # Prepend the subdirectory the object is found in.
+           non_pic_object="$xdir$non_pic_object"
+
+           # A standard non-PIC object
+           func_append non_pic_objects " $non_pic_object"
+           if test -z "$pic_object" || test "$pic_object" = none ; then
+             arg="$non_pic_object"
+           fi
+         else
+           # If the PIC object exists, use it instead.
+           # $xdir was prepended to $pic_object above.
+           non_pic_object="$pic_object"
+           func_append non_pic_objects " $non_pic_object"
+         fi
+       else
+         # Only an error if not doing a dry-run.
+         if $opt_dry_run; then
+           # Extract subdirectory from the argument.
+           func_dirname "$arg" "/" ""
+           xdir="$func_dirname_result"
+
+           func_lo2o "$arg"
+           pic_object=$xdir$objdir/$func_lo2o_result
+           non_pic_object=$xdir$func_lo2o_result
+           func_append libobjs " $pic_object"
+           func_append non_pic_objects " $non_pic_object"
+         else
+           func_fatal_error "\`$arg' is not a valid libtool object"
+         fi
+       fi
+       ;;
+
+      *.$libext)
+       # An archive.
+       deplibs="$deplibs $arg"
+       old_deplibs="$old_deplibs $arg"
+       continue
+       ;;
+
+      *.la)
+       # A libtool-controlled library.
+
+       if test "$prev" = dlfiles; then
+         # This library was specified with -dlopen.
+         dlfiles="$dlfiles $arg"
+         prev=
+       elif test "$prev" = dlprefiles; then
+         # The library was specified with -dlpreopen.
+         dlprefiles="$dlprefiles $arg"
+         prev=
+       else
+         deplibs="$deplibs $arg"
+       fi
+       continue
+       ;;
+
+      # Some other compiler argument.
+      *)
+       # Unknown arguments in both finalize_command and compile_command need
+       # to be aesthetically quoted because they are evaled later.
+       func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+       ;;
+      esac # arg
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+       func_append compile_command " $arg"
+       func_append finalize_command " $arg"
+      fi
+    done # argument parsing loop
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prevarg' option requires an argument"
+
+    if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      func_append compile_command " $arg"
+      func_append finalize_command " $arg"
+    fi
+
+    oldlibs=
+    # calculate the name of the file, without its directory
+    func_basename "$output"
+    outputname="$func_basename_result"
+    libobjs_save="$libobjs"
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+    else
+      shlib_search_path=
+    fi
+    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+    func_dirname "$output" "/" ""
+    output_objdir="$func_dirname_result$objdir"
+    # Create the object directory.
+    func_mkdir_p "$output_objdir"
+
+    # Determine the type of output
+    case $output in
+    "")
+      func_fatal_help "you must specify an output file"
+      ;;
+    *.$libext) linkmode=oldlib ;;
+    *.lo | *.$objext) linkmode=obj ;;
+    *.la) linkmode=lib ;;
+    *) linkmode=prog ;; # Anything else should be a program.
+    esac
+
+    specialdeplibs=
+
+    libs=
+    # Find all interdependent deplibs by searching for libraries
+    # that are linked more than once (e.g. -la -lb -la)
+    for deplib in $deplibs; do
+      if $opt_duplicate_deps ; then
+       case "$libs " in
+       *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+       esac
+      fi
+      libs="$libs $deplib"
+    done
+
+    if test "$linkmode" = lib; then
+      libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+      # Compute libraries that are listed more than once in $predeps
+      # $postdeps and mark them as special (i.e., whose duplicates are
+      # not to be eliminated).
+      pre_post_deps=
+      if $opt_duplicate_compiler_generated_deps; then
+       for pre_post_dep in $predeps $postdeps; do
+         case "$pre_post_deps " in
+         *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;;
+         esac
+         pre_post_deps="$pre_post_deps $pre_post_dep"
+       done
+      fi
+      pre_post_deps=
+    fi
+
+    deplibs=
+    newdependency_libs=
+    newlib_search_path=
+    need_relink=no # whether we're linking any uninstalled libtool libraries
+    notinst_deplibs= # not-installed libtool libraries
+    notinst_path= # paths that contain not-installed libtool libraries
+
+    case $linkmode in
+    lib)
+       passes="conv dlpreopen link"
+       for file in $dlfiles $dlprefiles; do
+         case $file in
+         *.la) ;;
+         *)
+           func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+           ;;
+         esac
+       done
+       ;;
+    prog)
+       compile_deplibs=
+       finalize_deplibs=
+       alldeplibs=no
+       newdlfiles=
+       newdlprefiles=
+       passes="conv scan dlopen dlpreopen link"
+       ;;
+    *)  passes="conv"
+       ;;
+    esac
+
+    for pass in $passes; do
+      # The preopen pass in lib mode reverses $deplibs; put it back here
+      # so that -L comes before libs that need it for instance...
+      if test "$linkmode,$pass" = "lib,link"; then
+       ## FIXME: Find the place where the list is rebuilt in the wrong
+       ##        order, and fix it there properly
+        tmp_deplibs=
+       for deplib in $deplibs; do
+         tmp_deplibs="$deplib $tmp_deplibs"
+       done
+       deplibs="$tmp_deplibs"
+      fi
+
+      if test "$linkmode,$pass" = "lib,link" ||
+        test "$linkmode,$pass" = "prog,scan"; then
+       libs="$deplibs"
+       deplibs=
+      fi
+      if test "$linkmode" = prog; then
+       case $pass in
+       dlopen) libs="$dlfiles" ;;
+       dlpreopen) libs="$dlprefiles" ;;
+       link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+       esac
+      fi
+      if test "$linkmode,$pass" = "lib,dlpreopen"; then
+       # Collect and forward deplibs of preopened libtool libs
+       for lib in $dlprefiles; do
+         # Ignore non-libtool-libs
+         dependency_libs=
+         case $lib in
+         *.la) func_source "$lib" ;;
+         esac
+
+         # Collect preopened libtool deplibs, except any this library
+         # has declared as weak libs
+         for deplib in $dependency_libs; do
+            deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"`
+           case " $weak_libs " in
+           *" $deplib_base "*) ;;
+           *) deplibs="$deplibs $deplib" ;;
+           esac
+         done
+       done
+       libs="$dlprefiles"
+      fi
+      if test "$pass" = dlopen; then
+       # Collect dlpreopened libraries
+       save_deplibs="$deplibs"
+       deplibs=
+      fi
+
+      for deplib in $libs; do
+       lib=
+       found=no
+       case $deplib in
+       -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+         if test "$linkmode,$pass" = "prog,link"; then
+           compile_deplibs="$deplib $compile_deplibs"
+           finalize_deplibs="$deplib $finalize_deplibs"
+         else
+           compiler_flags="$compiler_flags $deplib"
+           if test "$linkmode" = lib ; then
+               case "$new_inherited_linker_flags " in
+                   *" $deplib "*) ;;
+                   * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+               esac
+           fi
+         fi
+         continue
+         ;;
+       -l*)
+         if test "$linkmode" != lib && test "$linkmode" != prog; then
+           func_warning "\`-l' is ignored for archives/objects"
+           continue
+         fi
+         func_stripname '-l' '' "$deplib"
+         name=$func_stripname_result
+         if test "$linkmode" = lib; then
+           searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+         else
+           searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+         fi
+         for searchdir in $searchdirs; do
+           for search_ext in .la $std_shrext .so .a; do
+             # Search the libtool library
+             lib="$searchdir/lib${name}${search_ext}"
+             if test -f "$lib"; then
+               if test "$search_ext" = ".la"; then
+                 found=yes
+               else
+                 found=no
+               fi
+               break 2
+             fi
+           done
+         done
+         if test "$found" != yes; then
+           # deplib doesn't seem to be a libtool library
+           if test "$linkmode,$pass" = "prog,link"; then
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           else
+             deplibs="$deplib $deplibs"
+             test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+           fi
+           continue
+         else # deplib is a libtool library
+           # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+           # We need to do some special things here, and not later.
+           if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+             case " $predeps $postdeps " in
+             *" $deplib "*)
+               if func_lalib_p "$lib"; then
+                 library_names=
+                 old_library=
+                 func_source "$lib"
+                 for l in $old_library $library_names; do
+                   ll="$l"
+                 done
+                 if test "X$ll" = "X$old_library" ; then # only static version available
+                   found=no
+                   func_dirname "$lib" "" "."
+                   ladir="$func_dirname_result"
+                   lib=$ladir/$old_library
+                   if test "$linkmode,$pass" = "prog,link"; then
+                     compile_deplibs="$deplib $compile_deplibs"
+                     finalize_deplibs="$deplib $finalize_deplibs"
+                   else
+                     deplibs="$deplib $deplibs"
+                     test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+                   fi
+                   continue
+                 fi
+               fi
+               ;;
+             *) ;;
+             esac
+           fi
+         fi
+         ;; # -l
+       *.ltframework)
+         if test "$linkmode,$pass" = "prog,link"; then
+           compile_deplibs="$deplib $compile_deplibs"
+           finalize_deplibs="$deplib $finalize_deplibs"
+         else
+           deplibs="$deplib $deplibs"
+           if test "$linkmode" = lib ; then
+               case "$new_inherited_linker_flags " in
+                   *" $deplib "*) ;;
+                   * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+               esac
+           fi
+         fi
+         continue
+         ;;
+       -L*)
+         case $linkmode in
+         lib)
+           deplibs="$deplib $deplibs"
+           test "$pass" = conv && continue
+           newdependency_libs="$deplib $newdependency_libs"
+           func_stripname '-L' '' "$deplib"
+           newlib_search_path="$newlib_search_path $func_stripname_result"
+           ;;
+         prog)
+           if test "$pass" = conv; then
+             deplibs="$deplib $deplibs"
+             continue
+           fi
+           if test "$pass" = scan; then
+             deplibs="$deplib $deplibs"
+           else
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           fi
+           func_stripname '-L' '' "$deplib"
+           newlib_search_path="$newlib_search_path $func_stripname_result"
+           ;;
+         *)
+           func_warning "\`-L' is ignored for archives/objects"
+           ;;
+         esac # linkmode
+         continue
+         ;; # -L
+       -R*)
+         if test "$pass" = link; then
+           func_stripname '-R' '' "$deplib"
+           dir=$func_stripname_result
+           # Make sure the xrpath contains only unique directories.
+           case "$xrpath " in
+           *" $dir "*) ;;
+           *) xrpath="$xrpath $dir" ;;
+           esac
+         fi
+         deplibs="$deplib $deplibs"
+         continue
+         ;;
+       *.la) lib="$deplib" ;;
+       *.$libext)
+         if test "$pass" = conv; then
+           deplibs="$deplib $deplibs"
+           continue
+         fi
+         case $linkmode in
+         lib)
+           # Linking convenience modules into shared libraries is allowed,
+           # but linking other static libraries is non-portable.
+           case " $dlpreconveniencelibs " in
+           *" $deplib "*) ;;
+           *)
+             valid_a_lib=no
+             case $deplibs_check_method in
+               match_pattern*)
+                 set dummy $deplibs_check_method; shift
+                 match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+                 if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \
+                   | $EGREP "$match_pattern_regex" > /dev/null; then
+                   valid_a_lib=yes
+                 fi
+               ;;
+               pass_all)
+                 valid_a_lib=yes
+               ;;
+             esac
+             if test "$valid_a_lib" != yes; then
+               $ECHO
+               $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+               $ECHO "*** I have the capability to make that library automatically link in when"
+               $ECHO "*** you link to this library.  But I can only do this if you have a"
+               $ECHO "*** shared version of the library, which you do not appear to have"
+               $ECHO "*** because the file extensions .$libext of this argument makes me believe"
+               $ECHO "*** that it is just a static archive that I should not use here."
+             else
+               $ECHO
+               $ECHO "*** Warning: Linking the shared library $output against the"
+               $ECHO "*** static library $deplib is not portable!"
+               deplibs="$deplib $deplibs"
+             fi
+             ;;
+           esac
+           continue
+           ;;
+         prog)
+           if test "$pass" != link; then
+             deplibs="$deplib $deplibs"
+           else
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           fi
+           continue
+           ;;
+         esac # linkmode
+         ;; # *.$libext
+       *.lo | *.$objext)
+         if test "$pass" = conv; then
+           deplibs="$deplib $deplibs"
+         elif test "$linkmode" = prog; then
+           if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+             # If there is no dlopen support or we're linking statically,
+             # we need to preload.
+             newdlprefiles="$newdlprefiles $deplib"
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           else
+             newdlfiles="$newdlfiles $deplib"
+           fi
+         fi
+         continue
+         ;;
+       %DEPLIBS%)
+         alldeplibs=yes
+         continue
+         ;;
+       esac # case $deplib
+
+       if test "$found" = yes || test -f "$lib"; then :
+       else
+         func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
+       fi
+
+       # Check to see that this really is a libtool archive.
+       func_lalib_unsafe_p "$lib" \
+         || func_fatal_error "\`$lib' is not a valid libtool archive"
+
+       func_dirname "$lib" "" "."
+       ladir="$func_dirname_result"
+
+       dlname=
+       dlopen=
+       dlpreopen=
+       libdir=
+       library_names=
+       old_library=
+       inherited_linker_flags=
+       # If the library was installed with an old release of libtool,
+       # it will not redefine variables installed, or shouldnotlink
+       installed=yes
+       shouldnotlink=no
+       avoidtemprpath=
+
+
+       # Read the .la file
+       func_source "$lib"
+
+       # Convert "-framework foo" to "foo.ltframework"
+       if test -n "$inherited_linker_flags"; then
+         tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'`
+         for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+           case " $new_inherited_linker_flags " in
+             *" $tmp_inherited_linker_flag "*) ;;
+             *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";;
+           esac
+         done
+       fi
+       dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+       if test "$linkmode,$pass" = "lib,link" ||
+          test "$linkmode,$pass" = "prog,scan" ||
+          { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+         test -n "$dlopen" && dlfiles="$dlfiles $dlopen"
+         test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen"
+       fi
+
+       if test "$pass" = conv; then
+         # Only check for convenience libraries
+         deplibs="$lib $deplibs"
+         if test -z "$libdir"; then
+           if test -z "$old_library"; then
+             func_fatal_error "cannot find name of link library for \`$lib'"
+           fi
+           # It is a libtool convenience library, so add in its objects.
+           convenience="$convenience $ladir/$objdir/$old_library"
+           old_convenience="$old_convenience $ladir/$objdir/$old_library"
+         elif test "$linkmode" != prog && test "$linkmode" != lib; then
+           func_fatal_error "\`$lib' is not a convenience library"
+         fi
+         tmp_libs=
+         for deplib in $dependency_libs; do
+           deplibs="$deplib $deplibs"
+           if $opt_duplicate_deps ; then
+             case "$tmp_libs " in
+             *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+             esac
+           fi
+           tmp_libs="$tmp_libs $deplib"
+         done
+         continue
+       fi # $pass = conv
+
+
+       # Get the name of the library we link against.
+       linklib=
+       for l in $old_library $library_names; do
+         linklib="$l"
+       done
+       if test -z "$linklib"; then
+         func_fatal_error "cannot find name of link library for \`$lib'"
+       fi
+
+       # This library was specified with -dlopen.
+       if test "$pass" = dlopen; then
+         if test -z "$libdir"; then
+           func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
+         fi
+         if test -z "$dlname" ||
+            test "$dlopen_support" != yes ||
+            test "$build_libtool_libs" = no; then
+           # If there is no dlname, no dlopen support or we're linking
+           # statically, we need to preload.  We also need to preload any
+           # dependent libraries so libltdl's deplib preloader doesn't
+           # bomb out in the load deplibs phase.
+           dlprefiles="$dlprefiles $lib $dependency_libs"
+         else
+           newdlfiles="$newdlfiles $lib"
+         fi
+         continue
+       fi # $pass = dlopen
+
+       # We need an absolute path.
+       case $ladir in
+       [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+       *)
+         abs_ladir=`cd "$ladir" && pwd`
+         if test -z "$abs_ladir"; then
+           func_warning "cannot determine absolute directory name of \`$ladir'"
+           func_warning "passing it literally to the linker, although it might fail"
+           abs_ladir="$ladir"
+         fi
+         ;;
+       esac
+       func_basename "$lib"
+       laname="$func_basename_result"
+
+       # Find the relevant object directory and library name.
+       if test "X$installed" = Xyes; then
+         if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+           func_warning "library \`$lib' was moved."
+           dir="$ladir"
+           absdir="$abs_ladir"
+           libdir="$abs_ladir"
+         else
+           dir="$libdir"
+           absdir="$libdir"
+         fi
+         test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+       else
+         if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+           dir="$ladir"
+           absdir="$abs_ladir"
+           # Remove this search path later
+           notinst_path="$notinst_path $abs_ladir"
+         else
+           dir="$ladir/$objdir"
+           absdir="$abs_ladir/$objdir"
+           # Remove this search path later
+           notinst_path="$notinst_path $abs_ladir"
+         fi
+       fi # $installed = yes
+       func_stripname 'lib' '.la' "$laname"
+       name=$func_stripname_result
+
+       # This library was specified with -dlpreopen.
+       if test "$pass" = dlpreopen; then
+         if test -z "$libdir" && test "$linkmode" = prog; then
+           func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+         fi
+         # Prefer using a static library (so that no silly _DYNAMIC symbols
+         # are required to link).
+         if test -n "$old_library"; then
+           newdlprefiles="$newdlprefiles $dir/$old_library"
+           # Keep a list of preopened convenience libraries to check
+           # that they are being used correctly in the link pass.
+           test -z "$libdir" && \
+               dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library"
+         # Otherwise, use the dlname, so that lt_dlopen finds it.
+         elif test -n "$dlname"; then
+           newdlprefiles="$newdlprefiles $dir/$dlname"
+         else
+           newdlprefiles="$newdlprefiles $dir/$linklib"
+         fi
+       fi # $pass = dlpreopen
+
+       if test -z "$libdir"; then
+         # Link the convenience library
+         if test "$linkmode" = lib; then
+           deplibs="$dir/$old_library $deplibs"
+         elif test "$linkmode,$pass" = "prog,link"; then
+           compile_deplibs="$dir/$old_library $compile_deplibs"
+           finalize_deplibs="$dir/$old_library $finalize_deplibs"
+         else
+           deplibs="$lib $deplibs" # used for prog,scan pass
+         fi
+         continue
+       fi
+
+
+       if test "$linkmode" = prog && test "$pass" != link; then
+         newlib_search_path="$newlib_search_path $ladir"
+         deplibs="$lib $deplibs"
+
+         linkalldeplibs=no
+         if test "$link_all_deplibs" != no || test -z "$library_names" ||
+            test "$build_libtool_libs" = no; then
+           linkalldeplibs=yes
+         fi
+
+         tmp_libs=
+         for deplib in $dependency_libs; do
+           case $deplib in
+           -L*) func_stripname '-L' '' "$deplib"
+                newlib_search_path="$newlib_search_path $func_stripname_result"
+                ;;
+           esac
+           # Need to link against all dependency_libs?
+           if test "$linkalldeplibs" = yes; then
+             deplibs="$deplib $deplibs"
+           else
+             # Need to hardcode shared library paths
+             # or/and link against static libraries
+             newdependency_libs="$deplib $newdependency_libs"
+           fi
+           if $opt_duplicate_deps ; then
+             case "$tmp_libs " in
+             *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+             esac
+           fi
+           tmp_libs="$tmp_libs $deplib"
+         done # for deplib
+         continue
+       fi # $linkmode = prog...
+
+       if test "$linkmode,$pass" = "prog,link"; then
+         if test -n "$library_names" &&
+            { { test "$prefer_static_libs" = no ||
+                test "$prefer_static_libs,$installed" = "built,yes"; } ||
+              test -z "$old_library"; }; then
+           # We need to hardcode the library path
+           if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+             # Make sure the rpath contains only unique directories.
+             case "$temp_rpath:" in
+             *"$absdir:"*) ;;
+             *) temp_rpath="$temp_rpath$absdir:" ;;
+             esac
+           fi
+
+           # Hardcode the library path.
+           # Skip directories that are in the system default run-time
+           # search path.
+           case " $sys_lib_dlsearch_path " in
+           *" $absdir "*) ;;
+           *)
+             case "$compile_rpath " in
+             *" $absdir "*) ;;
+             *) compile_rpath="$compile_rpath $absdir"
+             esac
+             ;;
+           esac
+           case " $sys_lib_dlsearch_path " in
+           *" $libdir "*) ;;
+           *)
+             case "$finalize_rpath " in
+             *" $libdir "*) ;;
+             *) finalize_rpath="$finalize_rpath $libdir"
+             esac
+             ;;
+           esac
+         fi # $linkmode,$pass = prog,link...
+
+         if test "$alldeplibs" = yes &&
+            { test "$deplibs_check_method" = pass_all ||
+              { test "$build_libtool_libs" = yes &&
+                test -n "$library_names"; }; }; then
+           # We only need to search for static libraries
+           continue
+         fi
+       fi
+
+       link_static=no # Whether the deplib will be linked statically
+       use_static_libs=$prefer_static_libs
+       if test "$use_static_libs" = built && test "$installed" = yes; then
+         use_static_libs=no
+       fi
+       if test -n "$library_names" &&
+          { test "$use_static_libs" = no || test -z "$old_library"; }; then
+         case $host in
+         *cygwin* | *mingw* | *cegcc*)
+             # No point in relinking DLLs because paths are not encoded
+             notinst_deplibs="$notinst_deplibs $lib"
+             need_relink=no
+           ;;
+         *)
+           if test "$installed" = no; then
+             notinst_deplibs="$notinst_deplibs $lib"
+             need_relink=yes
+           fi
+           ;;
+         esac
+         # This is a shared library
+
+         # Warn about portability, can't link against -module's on some
+         # systems (darwin).  Don't bleat about dlopened modules though!
+         dlopenmodule=""
+         for dlpremoduletest in $dlprefiles; do
+           if test "X$dlpremoduletest" = "X$lib"; then
+             dlopenmodule="$dlpremoduletest"
+             break
+           fi
+         done
+         if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+           $ECHO
+           if test "$linkmode" = prog; then
+             $ECHO "*** Warning: Linking the executable $output against the loadable module"
+           else
+             $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+           fi
+           $ECHO "*** $linklib is not portable!"
+         fi
+         if test "$linkmode" = lib &&
+            test "$hardcode_into_libs" = yes; then
+           # Hardcode the library path.
+           # Skip directories that are in the system default run-time
+           # search path.
+           case " $sys_lib_dlsearch_path " in
+           *" $absdir "*) ;;
+           *)
+             case "$compile_rpath " in
+             *" $absdir "*) ;;
+             *) compile_rpath="$compile_rpath $absdir"
+             esac
+             ;;
+           esac
+           case " $sys_lib_dlsearch_path " in
+           *" $libdir "*) ;;
+           *)
+             case "$finalize_rpath " in
+             *" $libdir "*) ;;
+             *) finalize_rpath="$finalize_rpath $libdir"
+             esac
+             ;;
+           esac
+         fi
+
+         if test -n "$old_archive_from_expsyms_cmds"; then
+           # figure out the soname
+           set dummy $library_names
+           shift
+           realname="$1"
+           shift
+           libname=`eval "\\$ECHO \"$libname_spec\""`
+           # use dlname if we got it. it's perfectly good, no?
+           if test -n "$dlname"; then
+             soname="$dlname"
+           elif test -n "$soname_spec"; then
+             # bleh windows
+             case $host in
+             *cygwin* | mingw* | *cegcc*)
+               func_arith $current - $age
+               major=$func_arith_result
+               versuffix="-$major"
+               ;;
+             esac
+             eval soname=\"$soname_spec\"
+           else
+             soname="$realname"
+           fi
+
+           # Make a new name for the extract_expsyms_cmds to use
+           soroot="$soname"
+           func_basename "$soroot"
+           soname="$func_basename_result"
+           func_stripname 'lib' '.dll' "$soname"
+           newlib=libimp-$func_stripname_result.a
+
+           # If the library has no export list, then create one now
+           if test -f "$output_objdir/$soname-def"; then :
+           else
+             func_verbose "extracting exported symbol list from \`$soname'"
+             func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+           fi
+
+           # Create $newlib
+           if test -f "$output_objdir/$newlib"; then :; else
+             func_verbose "generating import library for \`$soname'"
+             func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+           fi
+           # make sure the library variables are pointing to the new library
+           dir=$output_objdir
+           linklib=$newlib
+         fi # test -n "$old_archive_from_expsyms_cmds"
+
+         if test "$linkmode" = prog || test "$mode" != relink; then
+           add_shlibpath=
+           add_dir=
+           add=
+           lib_linked=yes
+           case $hardcode_action in
+           immediate | unsupported)
+             if test "$hardcode_direct" = no; then
+               add="$dir/$linklib"
+               case $host in
+                 *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
+                 *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+                 *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+                   *-*-unixware7*) add_dir="-L$dir" ;;
+                 *-*-darwin* )
+                   # if the lib is a (non-dlopened) module then we can not
+                   # link against it, someone is ignoring the earlier warnings
+                   if /usr/bin/file -L $add 2> /dev/null |
+                        $GREP ": [^:]* bundle" >/dev/null ; then
+                     if test "X$dlopenmodule" != "X$lib"; then
+                       $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+                       if test -z "$old_library" ; then
+                         $ECHO
+                         $ECHO "*** And there doesn't seem to be a static archive available"
+                         $ECHO "*** The link will probably fail, sorry"
+                       else
+                         add="$dir/$old_library"
+                       fi
+                     elif test -n "$old_library"; then
+                       add="$dir/$old_library"
+                     fi
+                   fi
+               esac
+             elif test "$hardcode_minus_L" = no; then
+               case $host in
+               *-*-sunos*) add_shlibpath="$dir" ;;
+               esac
+               add_dir="-L$dir"
+               add="-l$name"
+             elif test "$hardcode_shlibpath_var" = no; then
+               add_shlibpath="$dir"
+               add="-l$name"
+             else
+               lib_linked=no
+             fi
+             ;;
+           relink)
+             if test "$hardcode_direct" = yes &&
+                test "$hardcode_direct_absolute" = no; then
+               add="$dir/$linklib"
+             elif test "$hardcode_minus_L" = yes; then
+               add_dir="-L$dir"
+               # Try looking first in the location we're being installed to.
+               if test -n "$inst_prefix_dir"; then
+                 case $libdir in
+                   [\\/]*)
+                     add_dir="$add_dir -L$inst_prefix_dir$libdir"
+                     ;;
+                 esac
+               fi
+               add="-l$name"
+             elif test "$hardcode_shlibpath_var" = yes; then
+               add_shlibpath="$dir"
+               add="-l$name"
+             else
+               lib_linked=no
+             fi
+             ;;
+           *) lib_linked=no ;;
+           esac
+
+           if test "$lib_linked" != yes; then
+             func_fatal_configuration "unsupported hardcode properties"
+           fi
+
+           if test -n "$add_shlibpath"; then
+             case :$compile_shlibpath: in
+             *":$add_shlibpath:"*) ;;
+             *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;;
+             esac
+           fi
+           if test "$linkmode" = prog; then
+             test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+             test -n "$add" && compile_deplibs="$add $compile_deplibs"
+           else
+             test -n "$add_dir" && deplibs="$add_dir $deplibs"
+             test -n "$add" && deplibs="$add $deplibs"
+             if test "$hardcode_direct" != yes &&
+                test "$hardcode_minus_L" != yes &&
+                test "$hardcode_shlibpath_var" = yes; then
+               case :$finalize_shlibpath: in
+               *":$libdir:"*) ;;
+               *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+               esac
+             fi
+           fi
+         fi
+
+         if test "$linkmode" = prog || test "$mode" = relink; then
+           add_shlibpath=
+           add_dir=
+           add=
+           # Finalize command for both is simple: just hardcode it.
+           if test "$hardcode_direct" = yes &&
+              test "$hardcode_direct_absolute" = no; then
+             add="$libdir/$linklib"
+           elif test "$hardcode_minus_L" = yes; then
+             add_dir="-L$libdir"
+             add="-l$name"
+           elif test "$hardcode_shlibpath_var" = yes; then
+             case :$finalize_shlibpath: in
+             *":$libdir:"*) ;;
+             *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+             esac
+             add="-l$name"
+           elif test "$hardcode_automatic" = yes; then
+             if test -n "$inst_prefix_dir" &&
+                test -f "$inst_prefix_dir$libdir/$linklib" ; then
+               add="$inst_prefix_dir$libdir/$linklib"
+             else
+               add="$libdir/$linklib"
+             fi
+           else
+             # We cannot seem to hardcode it, guess we'll fake it.
+             add_dir="-L$libdir"
+             # Try looking first in the location we're being installed to.
+             if test -n "$inst_prefix_dir"; then
+               case $libdir in
+                 [\\/]*)
+                   add_dir="$add_dir -L$inst_prefix_dir$libdir"
+                   ;;
+               esac
+             fi
+             add="-l$name"
+           fi
+
+           if test "$linkmode" = prog; then
+             test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+             test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+           else
+             test -n "$add_dir" && deplibs="$add_dir $deplibs"
+             test -n "$add" && deplibs="$add $deplibs"
+           fi
+         fi
+       elif test "$linkmode" = prog; then
+         # Here we assume that one of hardcode_direct or hardcode_minus_L
+         # is not unsupported.  This is valid on all known static and
+         # shared platforms.
+         if test "$hardcode_direct" != unsupported; then
+           test -n "$old_library" && linklib="$old_library"
+           compile_deplibs="$dir/$linklib $compile_deplibs"
+           finalize_deplibs="$dir/$linklib $finalize_deplibs"
+         else
+           compile_deplibs="-l$name -L$dir $compile_deplibs"
+           finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+         fi
+       elif test "$build_libtool_libs" = yes; then
+         # Not a shared library
+         if test "$deplibs_check_method" != pass_all; then
+           # We're trying link a shared library against a static one
+           # but the system doesn't support it.
+
+           # Just print a warning and add the library to dependency_libs so
+           # that the program can be linked against the static library.
+           $ECHO
+           $ECHO "*** Warning: This system can not link to static lib archive $lib."
+           $ECHO "*** I have the capability to make that library automatically link in when"
+           $ECHO "*** you link to this library.  But I can only do this if you have a"
+           $ECHO "*** shared version of the library, which you do not appear to have."
+           if test "$module" = yes; then
+             $ECHO "*** But as you try to build a module library, libtool will still create "
+             $ECHO "*** a static module, that should work as long as the dlopening application"
+             $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime."
+             if test -z "$global_symbol_pipe"; then
+               $ECHO
+               $ECHO "*** However, this would only work if libtool was able to extract symbol"
+               $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+               $ECHO "*** not find such a program.  So, this module is probably useless."
+               $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+             fi
+             if test "$build_old_libs" = no; then
+               build_libtool_libs=module
+               build_old_libs=yes
+             else
+               build_libtool_libs=no
+             fi
+           fi
+         else
+           deplibs="$dir/$old_library $deplibs"
+           link_static=yes
+         fi
+       fi # link shared/static library?
+
+       if test "$linkmode" = lib; then
+         if test -n "$dependency_libs" &&
+            { test "$hardcode_into_libs" != yes ||
+              test "$build_old_libs" = yes ||
+              test "$link_static" = yes; }; then
+           # Extract -R from dependency_libs
+           temp_deplibs=
+           for libdir in $dependency_libs; do
+             case $libdir in
+             -R*) func_stripname '-R' '' "$libdir"
+                  temp_xrpath=$func_stripname_result
+                  case " $xrpath " in
+                  *" $temp_xrpath "*) ;;
+                  *) xrpath="$xrpath $temp_xrpath";;
+                  esac;;
+             *) temp_deplibs="$temp_deplibs $libdir";;
+             esac
+           done
+           dependency_libs="$temp_deplibs"
+         fi
+
+         newlib_search_path="$newlib_search_path $absdir"
+         # Link against this library
+         test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+         # ... and its dependency_libs
+         tmp_libs=
+         for deplib in $dependency_libs; do
+           newdependency_libs="$deplib $newdependency_libs"
+           if $opt_duplicate_deps ; then
+             case "$tmp_libs " in
+             *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+             esac
+           fi
+           tmp_libs="$tmp_libs $deplib"
+         done
+
+         if test "$link_all_deplibs" != no; then
+           # Add the search paths of all dependency libraries
+           for deplib in $dependency_libs; do
+             case $deplib in
+             -L*) path="$deplib" ;;
+             *.la)
+               func_dirname "$deplib" "" "."
+               dir="$func_dirname_result"
+               # We need an absolute path.
+               case $dir in
+               [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+               *)
+                 absdir=`cd "$dir" && pwd`
+                 if test -z "$absdir"; then
+                   func_warning "cannot determine absolute directory name of \`$dir'"
+                   absdir="$dir"
+                 fi
+                 ;;
+               esac
+               if $GREP "^installed=no" $deplib > /dev/null; then
+               case $host in
+               *-*-darwin*)
+                 depdepl=
+                 eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+                 if test -n "$deplibrary_names" ; then
+                   for tmp in $deplibrary_names ; do
+                     depdepl=$tmp
+                   done
+                   if test -f "$absdir/$objdir/$depdepl" ; then
+                     depdepl="$absdir/$objdir/$depdepl"
+                     darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+                      if test -z "$darwin_install_name"; then
+                          darwin_install_name=`${OTOOL64} -L $depdepl  | awk '{if (NR == 2) {print $1;exit}}'`
+                      fi
+                     compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+                     linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}"
+                     path=
+                   fi
+                 fi
+                 ;;
+               *)
+                 path="-L$absdir/$objdir"
+                 ;;
+               esac
+               else
+                 eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+                 test -z "$libdir" && \
+                   func_fatal_error "\`$deplib' is not a valid libtool archive"
+                 test "$absdir" != "$libdir" && \
+                   func_warning "\`$deplib' seems to be moved"
+
+                 path="-L$absdir"
+               fi
+               ;;
+             esac
+             case " $deplibs " in
+             *" $path "*) ;;
+             *) deplibs="$path $deplibs" ;;
+             esac
+           done
+         fi # link_all_deplibs != no
+       fi # linkmode = lib
+      done # for deplib in $libs
+      if test "$pass" = link; then
+       if test "$linkmode" = "prog"; then
+         compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+         finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+       else
+         compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+       fi
+      fi
+      dependency_libs="$newdependency_libs"
+      if test "$pass" = dlpreopen; then
+       # Link the dlpreopened libraries before other libraries
+       for deplib in $save_deplibs; do
+         deplibs="$deplib $deplibs"
+       done
+      fi
+      if test "$pass" != dlopen; then
+       if test "$pass" != conv; then
+         # Make sure lib_search_path contains only unique directories.
+         lib_search_path=
+         for dir in $newlib_search_path; do
+           case "$lib_search_path " in
+           *" $dir "*) ;;
+           *) lib_search_path="$lib_search_path $dir" ;;
+           esac
+         done
+         newlib_search_path=
+       fi
+
+       if test "$linkmode,$pass" != "prog,link"; then
+         vars="deplibs"
+       else
+         vars="compile_deplibs finalize_deplibs"
+       fi
+       for var in $vars dependency_libs; do
+         # Add libraries to $var in reverse order
+         eval tmp_libs=\"\$$var\"
+         new_libs=
+         for deplib in $tmp_libs; do
+           # FIXME: Pedantically, this is the right thing to do, so
+           #        that some nasty dependency loop isn't accidentally
+           #        broken:
+           #new_libs="$deplib $new_libs"
+           # Pragmatically, this seems to cause very few problems in
+           # practice:
+           case $deplib in
+           -L*) new_libs="$deplib $new_libs" ;;
+           -R*) ;;
+           *)
+             # And here is the reason: when a library appears more
+             # than once as an explicit dependence of a library, or
+             # is implicitly linked in more than once by the
+             # compiler, it is considered special, and multiple
+             # occurrences thereof are not removed.  Compare this
+             # with having the same library being listed as a
+             # dependency of multiple other libraries: in this case,
+             # we know (pedantically, we assume) the library does not
+             # need to be listed more than once, so we keep only the
+             # last copy.  This is not always right, but it is rare
+             # enough that we require users that really mean to play
+             # such unportable linking tricks to link the library
+             # using -Wl,-lname, so that libtool does not consider it
+             # for duplicate removal.
+             case " $specialdeplibs " in
+             *" $deplib "*) new_libs="$deplib $new_libs" ;;
+             *)
+               case " $new_libs " in
+               *" $deplib "*) ;;
+               *) new_libs="$deplib $new_libs" ;;
+               esac
+               ;;
+             esac
+             ;;
+           esac
+         done
+         tmp_libs=
+         for deplib in $new_libs; do
+           case $deplib in
+           -L*)
+             case " $tmp_libs " in
+             *" $deplib "*) ;;
+             *) tmp_libs="$tmp_libs $deplib" ;;
+             esac
+             ;;
+           *) tmp_libs="$tmp_libs $deplib" ;;
+           esac
+         done
+         eval $var=\"$tmp_libs\"
+       done # for var
+      fi
+      # Last step: remove runtime libs from dependency_libs
+      # (they stay in deplibs)
+      tmp_libs=
+      for i in $dependency_libs ; do
+       case " $predeps $postdeps $compiler_lib_search_path " in
+       *" $i "*)
+         i=""
+         ;;
+       esac
+       if test -n "$i" ; then
+         tmp_libs="$tmp_libs $i"
+       fi
+      done
+      dependency_libs=$tmp_libs
+    done # for pass
+    if test "$linkmode" = prog; then
+      dlfiles="$newdlfiles"
+    fi
+    if test "$linkmode" = prog || test "$linkmode" = lib; then
+      dlprefiles="$newdlprefiles"
+    fi
+
+    case $linkmode in
+    oldlib)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       func_warning "\`-dlopen' is ignored for archives"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+       func_warning "\`-l' and \`-L' are ignored for archives" ;;
+      esac
+
+      test -n "$rpath" && \
+       func_warning "\`-rpath' is ignored for archives"
+
+      test -n "$xrpath" && \
+       func_warning "\`-R' is ignored for archives"
+
+      test -n "$vinfo" && \
+       func_warning "\`-version-info/-version-number' is ignored for archives"
+
+      test -n "$release" && \
+       func_warning "\`-release' is ignored for archives"
+
+      test -n "$export_symbols$export_symbols_regex" && \
+       func_warning "\`-export-symbols' is ignored for archives"
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs="$output"
+      objs="$objs$old_deplibs"
+      ;;
+
+    lib)
+      # Make sure we only generate libraries of the form `libNAME.la'.
+      case $outputname in
+      lib*)
+       func_stripname 'lib' '.la' "$outputname"
+       name=$func_stripname_result
+       eval shared_ext=\"$shrext_cmds\"
+       eval libname=\"$libname_spec\"
+       ;;
+      *)
+       test "$module" = no && \
+         func_fatal_help "libtool library \`$output' must begin with \`lib'"
+
+       if test "$need_lib_prefix" != no; then
+         # Add the "lib" prefix for modules if required
+         func_stripname '' '.la' "$outputname"
+         name=$func_stripname_result
+         eval shared_ext=\"$shrext_cmds\"
+         eval libname=\"$libname_spec\"
+       else
+         func_stripname '' '.la' "$outputname"
+         libname=$func_stripname_result
+       fi
+       ;;
+      esac
+
+      if test -n "$objs"; then
+       if test "$deplibs_check_method" != pass_all; then
+         func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+       else
+         $ECHO
+         $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+         $ECHO "*** objects $objs is not portable!"
+         libobjs="$libobjs $objs"
+       fi
+      fi
+
+      test "$dlself" != no && \
+       func_warning "\`-dlopen self' is ignored for libtool libraries"
+
+      set dummy $rpath
+      shift
+      test "$#" -gt 1 && \
+       func_warning "ignoring multiple \`-rpath's for a libtool library"
+
+      install_libdir="$1"
+
+      oldlibs=
+      if test -z "$rpath"; then
+       if test "$build_libtool_libs" = yes; then
+         # Building a libtool convenience library.
+         # Some compilers have problems with a `.al' extension so
+         # convenience libraries should have the same extension an
+         # archive normally would.
+         oldlibs="$output_objdir/$libname.$libext $oldlibs"
+         build_libtool_libs=convenience
+         build_old_libs=yes
+       fi
+
+       test -n "$vinfo" && \
+         func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+
+       test -n "$release" && \
+         func_warning "\`-release' is ignored for convenience libraries"
+      else
+
+       # Parse the version information argument.
+       save_ifs="$IFS"; IFS=':'
+       set dummy $vinfo 0 0 0
+       shift
+       IFS="$save_ifs"
+
+       test -n "$7" && \
+         func_fatal_help "too many parameters to \`-version-info'"
+
+       # convert absolute version numbers to libtool ages
+       # this retains compatibility with .la files and attempts
+       # to make the code below a bit more comprehensible
+
+       case $vinfo_number in
+       yes)
+         number_major="$1"
+         number_minor="$2"
+         number_revision="$3"
+         #
+         # There are really only two kinds -- those that
+         # use the current revision as the major version
+         # and those that subtract age and use age as
+         # a minor version.  But, then there is irix
+         # which has an extra 1 added just for fun
+         #
+         case $version_type in
+         darwin|linux|osf|windows|none)
+           func_arith $number_major + $number_minor
+           current=$func_arith_result
+           age="$number_minor"
+           revision="$number_revision"
+           ;;
+         freebsd-aout|freebsd-elf|sunos)
+           current="$number_major"
+           revision="$number_minor"
+           age="0"
+           ;;
+         irix|nonstopux)
+           func_arith $number_major + $number_minor
+           current=$func_arith_result
+           age="$number_minor"
+           revision="$number_minor"
+           lt_irix_increment=no
+           ;;
+         esac
+         ;;
+       no)
+         current="$1"
+         revision="$2"
+         age="$3"
+         ;;
+       esac
+
+       # Check that each of the things are valid numbers.
+       case $current in
+       0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+       *)
+         func_error "CURRENT \`$current' must be a nonnegative integer"
+         func_fatal_error "\`$vinfo' is not valid version information"
+         ;;
+       esac
+
+       case $revision in
+       0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+       *)
+         func_error "REVISION \`$revision' must be a nonnegative integer"
+         func_fatal_error "\`$vinfo' is not valid version information"
+         ;;
+       esac
+
+       case $age in
+       0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+       *)
+         func_error "AGE \`$age' must be a nonnegative integer"
+         func_fatal_error "\`$vinfo' is not valid version information"
+         ;;
+       esac
+
+       if test "$age" -gt "$current"; then
+         func_error "AGE \`$age' is greater than the current interface number \`$current'"
+         func_fatal_error "\`$vinfo' is not valid version information"
+       fi
+
+       # Calculate the version variables.
+       major=
+       versuffix=
+       verstring=
+       case $version_type in
+       none) ;;
+
+       darwin)
+         # Like Linux, but with the current version available in
+         # verstring for coding it into the library header
+         func_arith $current - $age
+         major=.$func_arith_result
+         versuffix="$major.$age.$revision"
+         # Darwin ld doesn't like 0 for these options...
+         func_arith $current + 1
+         minor_current=$func_arith_result
+         xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+         verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+         ;;
+
+       freebsd-aout)
+         major=".$current"
+         versuffix=".$current.$revision";
+         ;;
+
+       freebsd-elf)
+         major=".$current"
+         versuffix=".$current"
+         ;;
+
+       irix | nonstopux)
+         if test "X$lt_irix_increment" = "Xno"; then
+           func_arith $current - $age
+         else
+           func_arith $current - $age + 1
+         fi
+         major=$func_arith_result
+
+         case $version_type in
+           nonstopux) verstring_prefix=nonstopux ;;
+           *)         verstring_prefix=sgi ;;
+         esac
+         verstring="$verstring_prefix$major.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$revision
+         while test "$loop" -ne 0; do
+           func_arith $revision - $loop
+           iface=$func_arith_result
+           func_arith $loop - 1
+           loop=$func_arith_result
+           verstring="$verstring_prefix$major.$iface:$verstring"
+         done
+
+         # Before this point, $major must not contain `.'.
+         major=.$major
+         versuffix="$major.$revision"
+         ;;
+
+       linux)
+         func_arith $current - $age
+         major=.$func_arith_result
+         versuffix="$major.$age.$revision"
+         ;;
+
+       osf)
+         func_arith $current - $age
+         major=.$func_arith_result
+         versuffix=".$current.$age.$revision"
+         verstring="$current.$age.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$age
+         while test "$loop" -ne 0; do
+           func_arith $current - $loop
+           iface=$func_arith_result
+           func_arith $loop - 1
+           loop=$func_arith_result
+           verstring="$verstring:${iface}.0"
+         done
+
+         # Make executables depend on our current version.
+         verstring="$verstring:${current}.0"
+         ;;
+
+       qnx)
+         major=".$current"
+         versuffix=".$current"
+         ;;
+
+       sunos)
+         major=".$current"
+         versuffix=".$current.$revision"
+         ;;
+
+       windows)
+         # Use '-' rather than '.', since we only want one
+         # extension on DOS 8.3 filesystems.
+         func_arith $current - $age
+         major=$func_arith_result
+         versuffix="-$major"
+         ;;
+
+       *)
+         func_fatal_configuration "unknown library version type \`$version_type'"
+         ;;
+       esac
+
+       # Clear the version info if we defaulted, and they specified a release.
+       if test -z "$vinfo" && test -n "$release"; then
+         major=
+         case $version_type in
+         darwin)
+           # we can't check for "0.0" in archive_cmds due to quoting
+           # problems, so we reset it completely
+           verstring=
+           ;;
+         *)
+           verstring="0.0"
+           ;;
+         esac
+         if test "$need_version" = no; then
+           versuffix=
+         else
+           versuffix=".0.0"
+         fi
+       fi
+
+       # Remove version info from name if versioning should be avoided
+       if test "$avoid_version" = yes && test "$need_version" = no; then
+         major=
+         versuffix=
+         verstring=""
+       fi
+
+       # Check to see if the archive will have undefined symbols.
+       if test "$allow_undefined" = yes; then
+         if test "$allow_undefined_flag" = unsupported; then
+           func_warning "undefined symbols not allowed in $host shared libraries"
+           build_libtool_libs=no
+           build_old_libs=yes
+         fi
+       else
+         # Don't allow undefined symbols.
+         allow_undefined_flag="$no_undefined_flag"
+       fi
+
+      fi
+
+      func_generate_dlsyms "$libname" "$libname" "yes"
+      libobjs="$libobjs $symfileobj"
+      test "X$libobjs" = "X " && libobjs=
+
+      if test "$mode" != relink; then
+       # Remove our outputs, but don't remove object files since they
+       # may have been created when compiling PIC objects.
+       removelist=
+       tempremovelist=`$ECHO "$output_objdir/*"`
+       for p in $tempremovelist; do
+         case $p in
+           *.$objext | *.gcno)
+              ;;
+           $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+              if test "X$precious_files_regex" != "X"; then
+                if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+                then
+                  continue
+                fi
+              fi
+              removelist="$removelist $p"
+              ;;
+           *) ;;
+         esac
+       done
+       test -n "$removelist" && \
+         func_show_eval "${RM}r \$removelist"
+      fi
+
+      # Now set the variables for building old libraries.
+      if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+       oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+       # Transform .lo files to .o files.
+       oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+      fi
+
+      # Eliminate all temporary directories.
+      #for path in $notinst_path; do
+      #        lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"`
+      #        deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"`
+      #        dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"`
+      #done
+
+      if test -n "$xrpath"; then
+       # If the user specified any rpath flags, then add them.
+       temp_xrpath=
+       for libdir in $xrpath; do
+         temp_xrpath="$temp_xrpath -R$libdir"
+         case "$finalize_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_rpath="$finalize_rpath $libdir" ;;
+         esac
+       done
+       if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+         dependency_libs="$temp_xrpath $dependency_libs"
+       fi
+      fi
+
+      # Make sure dlfiles contains only unique files that won't be dlpreopened
+      old_dlfiles="$dlfiles"
+      dlfiles=
+      for lib in $old_dlfiles; do
+       case " $dlprefiles $dlfiles " in
+       *" $lib "*) ;;
+       *) dlfiles="$dlfiles $lib" ;;
+       esac
+      done
+
+      # Make sure dlprefiles contains only unique files
+      old_dlprefiles="$dlprefiles"
+      dlprefiles=
+      for lib in $old_dlprefiles; do
+       case "$dlprefiles " in
+       *" $lib "*) ;;
+       *) dlprefiles="$dlprefiles $lib" ;;
+       esac
+      done
+
+      if test "$build_libtool_libs" = yes; then
+       if test -n "$rpath"; then
+         case $host in
+         *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*)
+           # these systems don't actually have a c library (as such)!
+           ;;
+         *-*-rhapsody* | *-*-darwin1.[012])
+           # Rhapsody C library is in the System framework
+           deplibs="$deplibs System.ltframework"
+           ;;
+         *-*-netbsd*)
+           # Don't link with libc until the a.out ld.so is fixed.
+           ;;
+         *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+           # Do not include libc due to us having libc/libc_r.
+           ;;
+         *-*-sco3.2v5* | *-*-sco5v6*)
+           # Causes problems with __ctype
+           ;;
+         *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+           # Compiler inserts libc in the correct place for threads to work
+           ;;
+         *)
+           # Add libc to deplibs on all other systems if necessary.
+           if test "$build_libtool_need_lc" = "yes"; then
+             deplibs="$deplibs -lc"
+           fi
+           ;;
+         esac
+       fi
+
+       # Transform deplibs into only deplibs that can be linked in shared.
+       name_save=$name
+       libname_save=$libname
+       release_save=$release
+       versuffix_save=$versuffix
+       major_save=$major
+       # I'm not sure if I'm treating the release correctly.  I think
+       # release should show up in the -l (ie -lgmp5) so we don't want to
+       # add it in twice.  Is that correct?
+       release=""
+       versuffix=""
+       major=""
+       newdeplibs=
+       droppeddeps=no
+       case $deplibs_check_method in
+       pass_all)
+         # Don't check for shared/static.  Everything works.
+         # This might be a little naive.  We might want to check
+         # whether the library exists or not.  But this is on
+         # osf3 & osf4 and I'm not really sure... Just
+         # implementing what was already the behavior.
+         newdeplibs=$deplibs
+         ;;
+       test_compile)
+         # This code stresses the "libraries are programs" paradigm to its
+         # limits. Maybe even breaks it.  We compile a program, linking it
+         # against the deplibs as a proxy for the library.  Then we can check
+         # whether they linked in statically or dynamically with ldd.
+         $opt_dry_run || $RM conftest.c
+         cat > conftest.c <<EOF
+         int main() { return 0; }
+EOF
+         $opt_dry_run || $RM conftest
+         if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+           ldd_output=`ldd conftest`
+           for i in $deplibs; do
+             case $i in
+             -l*)
+               func_stripname -l '' "$i"
+               name=$func_stripname_result
+               if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+                 case " $predeps $postdeps " in
+                 *" $i "*)
+                   newdeplibs="$newdeplibs $i"
+                   i=""
+                   ;;
+                 esac
+               fi
+               if test -n "$i" ; then
+                 libname=`eval "\\$ECHO \"$libname_spec\""`
+                 deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+                 set dummy $deplib_matches; shift
+                 deplib_match=$1
+                 if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                   newdeplibs="$newdeplibs $i"
+                 else
+                   droppeddeps=yes
+                   $ECHO
+                   $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+                   $ECHO "*** I have the capability to make that library automatically link in when"
+                   $ECHO "*** you link to this library.  But I can only do this if you have a"
+                   $ECHO "*** shared version of the library, which I believe you do not have"
+                   $ECHO "*** because a test_compile did reveal that the linker did not use it for"
+                   $ECHO "*** its dynamic dependency list that programs get resolved with at runtime."
+                 fi
+               fi
+               ;;
+             *)
+               newdeplibs="$newdeplibs $i"
+               ;;
+             esac
+           done
+         else
+           # Error occurred in the first compile.  Let's try to salvage
+           # the situation: Compile a separate program for each library.
+           for i in $deplibs; do
+             case $i in
+             -l*)
+               func_stripname -l '' "$i"
+               name=$func_stripname_result
+               $opt_dry_run || $RM conftest
+               if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+                 ldd_output=`ldd conftest`
+                 if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+                   case " $predeps $postdeps " in
+                   *" $i "*)
+                     newdeplibs="$newdeplibs $i"
+                     i=""
+                     ;;
+                   esac
+                 fi
+                 if test -n "$i" ; then
+                   libname=`eval "\\$ECHO \"$libname_spec\""`
+                   deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+                   set dummy $deplib_matches; shift
+                   deplib_match=$1
+                   if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                     newdeplibs="$newdeplibs $i"
+                   else
+                     droppeddeps=yes
+                     $ECHO
+                     $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+                     $ECHO "*** I have the capability to make that library automatically link in when"
+                     $ECHO "*** you link to this library.  But I can only do this if you have a"
+                     $ECHO "*** shared version of the library, which you do not appear to have"
+                     $ECHO "*** because a test_compile did reveal that the linker did not use this one"
+                     $ECHO "*** as a dynamic dependency that programs can get resolved with at runtime."
+                   fi
+                 fi
+               else
+                 droppeddeps=yes
+                 $ECHO
+                 $ECHO "*** Warning!  Library $i is needed by this library but I was not able to"
+                 $ECHO "*** make it link in!  You will probably need to install it or some"
+                 $ECHO "*** library that it depends on before this library will be fully"
+                 $ECHO "*** functional.  Installing it before continuing would be even better."
+               fi
+               ;;
+             *)
+               newdeplibs="$newdeplibs $i"
+               ;;
+             esac
+           done
+         fi
+         ;;
+       file_magic*)
+         set dummy $deplibs_check_method; shift
+         file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+         for a_deplib in $deplibs; do
+           case $a_deplib in
+           -l*)
+             func_stripname -l '' "$a_deplib"
+             name=$func_stripname_result
+             if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+               case " $predeps $postdeps " in
+               *" $a_deplib "*)
+                 newdeplibs="$newdeplibs $a_deplib"
+                 a_deplib=""
+                 ;;
+               esac
+             fi
+             if test -n "$a_deplib" ; then
+               libname=`eval "\\$ECHO \"$libname_spec\""`
+               for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+                 potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+                 for potent_lib in $potential_libs; do
+                     # Follow soft links.
+                     if ls -lLd "$potent_lib" 2>/dev/null |
+                        $GREP " -> " >/dev/null; then
+                       continue
+                     fi
+                     # The statement above tries to avoid entering an
+                     # endless loop below, in case of cyclic links.
+                     # We might still enter an endless loop, since a link
+                     # loop can be closed while we follow links,
+                     # but so what?
+                     potlib="$potent_lib"
+                     while test -h "$potlib" 2>/dev/null; do
+                       potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+                       case $potliblink in
+                       [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+                       *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+                       esac
+                     done
+                     if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+                        $SED -e 10q |
+                        $EGREP "$file_magic_regex" > /dev/null; then
+                       newdeplibs="$newdeplibs $a_deplib"
+                       a_deplib=""
+                       break 2
+                     fi
+                 done
+               done
+             fi
+             if test -n "$a_deplib" ; then
+               droppeddeps=yes
+               $ECHO
+               $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+               $ECHO "*** I have the capability to make that library automatically link in when"
+               $ECHO "*** you link to this library.  But I can only do this if you have a"
+               $ECHO "*** shared version of the library, which you do not appear to have"
+               $ECHO "*** because I did check the linker path looking for a file starting"
+               if test -z "$potlib" ; then
+                 $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+               else
+                 $ECHO "*** with $libname and none of the candidates passed a file format test"
+                 $ECHO "*** using a file magic. Last file checked: $potlib"
+               fi
+             fi
+             ;;
+           *)
+             # Add a -L argument.
+             newdeplibs="$newdeplibs $a_deplib"
+             ;;
+           esac
+         done # Gone through all deplibs.
+         ;;
+       match_pattern*)
+         set dummy $deplibs_check_method; shift
+         match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+         for a_deplib in $deplibs; do
+           case $a_deplib in
+           -l*)
+             func_stripname -l '' "$a_deplib"
+             name=$func_stripname_result
+             if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+               case " $predeps $postdeps " in
+               *" $a_deplib "*)
+                 newdeplibs="$newdeplibs $a_deplib"
+                 a_deplib=""
+                 ;;
+               esac
+             fi
+             if test -n "$a_deplib" ; then
+               libname=`eval "\\$ECHO \"$libname_spec\""`
+               for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+                 potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+                 for potent_lib in $potential_libs; do
+                   potlib="$potent_lib" # see symlink-check above in file_magic test
+                   if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \
+                      $EGREP "$match_pattern_regex" > /dev/null; then
+                     newdeplibs="$newdeplibs $a_deplib"
+                     a_deplib=""
+                     break 2
+                   fi
+                 done
+               done
+             fi
+             if test -n "$a_deplib" ; then
+               droppeddeps=yes
+               $ECHO
+               $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+               $ECHO "*** I have the capability to make that library automatically link in when"
+               $ECHO "*** you link to this library.  But I can only do this if you have a"
+               $ECHO "*** shared version of the library, which you do not appear to have"
+               $ECHO "*** because I did check the linker path looking for a file starting"
+               if test -z "$potlib" ; then
+                 $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+               else
+                 $ECHO "*** with $libname and none of the candidates passed a file format test"
+                 $ECHO "*** using a regex pattern. Last file checked: $potlib"
+               fi
+             fi
+             ;;
+           *)
+             # Add a -L argument.
+             newdeplibs="$newdeplibs $a_deplib"
+             ;;
+           esac
+         done # Gone through all deplibs.
+         ;;
+       none | unknown | *)
+         newdeplibs=""
+         tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \
+             -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'`
+         if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+           for i in $predeps $postdeps ; do
+             # can't use Xsed below, because $i might contain '/'
+             tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"`
+           done
+         fi
+         if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[      ]//g' |
+            $GREP . >/dev/null; then
+           $ECHO
+           if test "X$deplibs_check_method" = "Xnone"; then
+             $ECHO "*** Warning: inter-library dependencies are not supported in this platform."
+           else
+             $ECHO "*** Warning: inter-library dependencies are not known to be supported."
+           fi
+           $ECHO "*** All declared inter-library dependencies are being dropped."
+           droppeddeps=yes
+         fi
+         ;;
+       esac
+       versuffix=$versuffix_save
+       major=$major_save
+       release=$release_save
+       libname=$libname_save
+       name=$name_save
+
+       case $host in
+       *-*-rhapsody* | *-*-darwin1.[012])
+         # On Rhapsody replace the C library with the System framework
+         newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+         ;;
+       esac
+
+       if test "$droppeddeps" = yes; then
+         if test "$module" = yes; then
+           $ECHO
+           $ECHO "*** Warning: libtool could not satisfy all declared inter-library"
+           $ECHO "*** dependencies of module $libname.  Therefore, libtool will create"
+           $ECHO "*** a static module, that should work as long as the dlopening"
+           $ECHO "*** application is linked with the -dlopen flag."
+           if test -z "$global_symbol_pipe"; then
+             $ECHO
+             $ECHO "*** However, this would only work if libtool was able to extract symbol"
+             $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+             $ECHO "*** not find such a program.  So, this module is probably useless."
+             $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+           fi
+           if test "$build_old_libs" = no; then
+             oldlibs="$output_objdir/$libname.$libext"
+             build_libtool_libs=module
+             build_old_libs=yes
+           else
+             build_libtool_libs=no
+           fi
+         else
+           $ECHO "*** The inter-library dependencies that have been dropped here will be"
+           $ECHO "*** automatically added whenever a program is linked with this library"
+           $ECHO "*** or is declared to -dlopen it."
+
+           if test "$allow_undefined" = no; then
+             $ECHO
+             $ECHO "*** Since this library must not contain undefined symbols,"
+             $ECHO "*** because either the platform does not support them or"
+             $ECHO "*** it was explicitly requested with -no-undefined,"
+             $ECHO "*** libtool will only create a static version of it."
+             if test "$build_old_libs" = no; then
+               oldlibs="$output_objdir/$libname.$libext"
+               build_libtool_libs=module
+               build_old_libs=yes
+             else
+               build_libtool_libs=no
+             fi
+           fi
+         fi
+       fi
+       # Done checking deplibs!
+       deplibs=$newdeplibs
+      fi
+      # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+      case $host in
+       *-*-darwin*)
+         newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+         new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+         deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+         ;;
+      esac
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+       case " $new_libs " in
+       *" -L$path/$objdir "*) ;;
+       *)
+         case " $deplibs " in
+         *" -L$path/$objdir "*)
+           new_libs="$new_libs -L$path/$objdir" ;;
+         esac
+         ;;
+       esac
+      done
+      for deplib in $deplibs; do
+       case $deplib in
+       -L*)
+         case " $new_libs " in
+         *" $deplib "*) ;;
+         *) new_libs="$new_libs $deplib" ;;
+         esac
+         ;;
+       *) new_libs="$new_libs $deplib" ;;
+       esac
+      done
+      deplibs="$new_libs"
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+
+      # Test again, we may have decided not to build it any more
+      if test "$build_libtool_libs" = yes; then
+       if test "$hardcode_into_libs" = yes; then
+         # Hardcode the library paths
+         hardcode_libdirs=
+         dep_rpath=
+         rpath="$finalize_rpath"
+         test "$mode" != relink && rpath="$compile_rpath$rpath"
+         for libdir in $rpath; do
+           if test -n "$hardcode_libdir_flag_spec"; then
+             if test -n "$hardcode_libdir_separator"; then
+               if test -z "$hardcode_libdirs"; then
+                 hardcode_libdirs="$libdir"
+               else
+                 # Just accumulate the unique libdirs.
+                 case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+                 *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+                   ;;
+                 *)
+                   hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+                   ;;
+                 esac
+               fi
+             else
+               eval flag=\"$hardcode_libdir_flag_spec\"
+               dep_rpath="$dep_rpath $flag"
+             fi
+           elif test -n "$runpath_var"; then
+             case "$perm_rpath " in
+             *" $libdir "*) ;;
+             *) perm_rpath="$perm_rpath $libdir" ;;
+             esac
+           fi
+         done
+         # Substitute the hardcoded libdirs into the rpath.
+         if test -n "$hardcode_libdir_separator" &&
+            test -n "$hardcode_libdirs"; then
+           libdir="$hardcode_libdirs"
+           if test -n "$hardcode_libdir_flag_spec_ld"; then
+             eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\"
+           else
+             eval dep_rpath=\"$hardcode_libdir_flag_spec\"
+           fi
+         fi
+         if test -n "$runpath_var" && test -n "$perm_rpath"; then
+           # We should set the runpath_var.
+           rpath=
+           for dir in $perm_rpath; do
+             rpath="$rpath$dir:"
+           done
+           eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+         fi
+         test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+       fi
+
+       shlibpath="$finalize_shlibpath"
+       test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+       if test -n "$shlibpath"; then
+         eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+       fi
+
+       # Get the real and link names of the library.
+       eval shared_ext=\"$shrext_cmds\"
+       eval library_names=\"$library_names_spec\"
+       set dummy $library_names
+       shift
+       realname="$1"
+       shift
+
+       if test -n "$soname_spec"; then
+         eval soname=\"$soname_spec\"
+       else
+         soname="$realname"
+       fi
+       if test -z "$dlname"; then
+         dlname=$soname
+       fi
+
+       lib="$output_objdir/$realname"
+       linknames=
+       for link
+       do
+         linknames="$linknames $link"
+       done
+
+       # Use standard objects if they are pic
+       test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+       test "X$libobjs" = "X " && libobjs=
+
+       delfiles=
+       if test -n "$export_symbols" && test -n "$include_expsyms"; then
+         $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+         export_symbols="$output_objdir/$libname.uexp"
+         delfiles="$delfiles $export_symbols"
+       fi
+
+       orig_export_symbols=
+       case $host_os in
+       cygwin* | mingw* | cegcc*)
+         if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+           # exporting using user supplied symfile
+           if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+             # and it's NOT already a .def file. Must figure out
+             # which of the given symbols are data symbols and tag
+             # them as such. So, trigger use of export_symbols_cmds.
+             # export_symbols gets reassigned inside the "prepare
+             # the list of exported symbols" if statement, so the
+             # include_expsyms logic still works.
+             orig_export_symbols="$export_symbols"
+             export_symbols=
+             always_export_symbols=yes
+           fi
+         fi
+         ;;
+       esac
+
+       # Prepare the list of exported symbols
+       if test -z "$export_symbols"; then
+         if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+           func_verbose "generating symbol list for \`$libname.la'"
+           export_symbols="$output_objdir/$libname.exp"
+           $opt_dry_run || $RM $export_symbols
+           cmds=$export_symbols_cmds
+           save_ifs="$IFS"; IFS='~'
+           for cmd in $cmds; do
+             IFS="$save_ifs"
+             eval cmd=\"$cmd\"
+             func_len " $cmd"
+             len=$func_len_result
+             if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+               func_show_eval "$cmd" 'exit $?'
+               skipped_export=false
+             else
+               # The command line is too long to execute in one step.
+               func_verbose "using reloadable object file for export list..."
+               skipped_export=:
+               # Break out early, otherwise skipped_export may be
+               # set to false by a later but shorter cmd.
+               break
+             fi
+           done
+           IFS="$save_ifs"
+           if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+             func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+             func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+           fi
+         fi
+       fi
+
+       if test -n "$export_symbols" && test -n "$include_expsyms"; then
+         tmp_export_symbols="$export_symbols"
+         test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+         $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+       fi
+
+       if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+         # The given exports_symbols file has to be filtered, so filter it.
+         func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+         # FIXME: $output_objdir/$libname.filter potentially contains lots of
+         # 's' commands which not all seds can handle. GNU sed should be fine
+         # though. Also, the filter scales superlinearly with the number of
+         # global variables. join(1) would be nice here, but unfortunately
+         # isn't a blessed tool.
+         $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+         delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+         export_symbols=$output_objdir/$libname.def
+         $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+       fi
+
+       tmp_deplibs=
+       for test_deplib in $deplibs; do
+         case " $convenience " in
+         *" $test_deplib "*) ;;
+         *)
+           tmp_deplibs="$tmp_deplibs $test_deplib"
+           ;;
+         esac
+       done
+       deplibs="$tmp_deplibs"
+
+       if test -n "$convenience"; then
+         if test -n "$whole_archive_flag_spec" &&
+           test "$compiler_needs_object" = yes &&
+           test -z "$libobjs"; then
+           # extract the archives, so we have objects to list.
+           # TODO: could optimize this to just extract one archive.
+           whole_archive_flag_spec=
+         fi
+         if test -n "$whole_archive_flag_spec"; then
+           save_libobjs=$libobjs
+           eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+           test "X$libobjs" = "X " && libobjs=
+         else
+           gentop="$output_objdir/${outputname}x"
+           generated="$generated $gentop"
+
+           func_extract_archives $gentop $convenience
+           libobjs="$libobjs $func_extract_archives_result"
+           test "X$libobjs" = "X " && libobjs=
+         fi
+       fi
+
+       if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+         eval flag=\"$thread_safe_flag_spec\"
+         linker_flags="$linker_flags $flag"
+       fi
+
+       # Make a backup of the uninstalled library when relinking
+       if test "$mode" = relink; then
+         $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+       fi
+
+       # Do each of the archive commands.
+       if test "$module" = yes && test -n "$module_cmds" ; then
+         if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+           eval test_cmds=\"$module_expsym_cmds\"
+           cmds=$module_expsym_cmds
+         else
+           eval test_cmds=\"$module_cmds\"
+           cmds=$module_cmds
+         fi
+       else
+         if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+           eval test_cmds=\"$archive_expsym_cmds\"
+           cmds=$archive_expsym_cmds
+         else
+           eval test_cmds=\"$archive_cmds\"
+           cmds=$archive_cmds
+         fi
+       fi
+
+       if test "X$skipped_export" != "X:" &&
+          func_len " $test_cmds" &&
+          len=$func_len_result &&
+          test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+         :
+       else
+         # The command line is too long to link in one step, link piecewise
+         # or, if using GNU ld and skipped_export is not :, use a linker
+         # script.
+
+         # Save the value of $output and $libobjs because we want to
+         # use them later.  If we have whole_archive_flag_spec, we
+         # want to use save_libobjs as it was before
+         # whole_archive_flag_spec was expanded, because we can't
+         # assume the linker understands whole_archive_flag_spec.
+         # This may have to be revisited, in case too many
+         # convenience libraries get linked in and end up exceeding
+         # the spec.
+         if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+           save_libobjs=$libobjs
+         fi
+         save_output=$output
+         output_la=`$ECHO "X$output" | $Xsed -e "$basename"`
+
+         # Clear the reloadable object creation command queue and
+         # initialize k to one.
+         test_cmds=
+         concat_cmds=
+         objlist=
+         last_robj=
+         k=1
+
+         if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
+           output=${output_objdir}/${output_la}.lnkscript
+           func_verbose "creating GNU ld script: $output"
+           $ECHO 'INPUT (' > $output
+           for obj in $save_libobjs
+           do
+             $ECHO "$obj" >> $output
+           done
+           $ECHO ')' >> $output
+           delfiles="$delfiles $output"
+         elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
+           output=${output_objdir}/${output_la}.lnk
+           func_verbose "creating linker input file list: $output"
+           : > $output
+           set x $save_libobjs
+           shift
+           firstobj=
+           if test "$compiler_needs_object" = yes; then
+             firstobj="$1 "
+             shift
+           fi
+           for obj
+           do
+             $ECHO "$obj" >> $output
+           done
+           delfiles="$delfiles $output"
+           output=$firstobj\"$file_list_spec$output\"
+         else
+           if test -n "$save_libobjs"; then
+             func_verbose "creating reloadable object files..."
+             output=$output_objdir/$output_la-${k}.$objext
+             eval test_cmds=\"$reload_cmds\"
+             func_len " $test_cmds"
+             len0=$func_len_result
+             len=$len0
+
+             # Loop over the list of objects to be linked.
+             for obj in $save_libobjs
+             do
+               func_len " $obj"
+               func_arith $len + $func_len_result
+               len=$func_arith_result
+               if test "X$objlist" = X ||
+                  test "$len" -lt "$max_cmd_len"; then
+                 func_append objlist " $obj"
+               else
+                 # The command $test_cmds is almost too long, add a
+                 # command to the queue.
+                 if test "$k" -eq 1 ; then
+                   # The first file doesn't have a previous command to add.
+                   eval concat_cmds=\"$reload_cmds $objlist $last_robj\"
+                 else
+                   # All subsequent reloadable object files will link in
+                   # the last one created.
+                   eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\"
+                 fi
+                 last_robj=$output_objdir/$output_la-${k}.$objext
+                 func_arith $k + 1
+                 k=$func_arith_result
+                 output=$output_objdir/$output_la-${k}.$objext
+                 objlist=$obj
+                 func_len " $last_robj"
+                 func_arith $len0 + $func_len_result
+                 len=$func_arith_result
+               fi
+             done
+             # Handle the remaining objects by creating one last
+             # reloadable object file.  All subsequent reloadable object
+             # files will link in the last one created.
+             test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+             eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\"
+             if test -n "$last_robj"; then
+               eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+             fi
+             delfiles="$delfiles $output"
+
+           else
+             output=
+           fi
+
+           if ${skipped_export-false}; then
+             func_verbose "generating symbol list for \`$libname.la'"
+             export_symbols="$output_objdir/$libname.exp"
+             $opt_dry_run || $RM $export_symbols
+             libobjs=$output
+             # Append the command to create the export file.
+             test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+             eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+             if test -n "$last_robj"; then
+               eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+             fi
+           fi
+
+           test -n "$save_libobjs" &&
+             func_verbose "creating a temporary reloadable object file: $output"
+
+           # Loop through the commands generated above and execute them.
+           save_ifs="$IFS"; IFS='~'
+           for cmd in $concat_cmds; do
+             IFS="$save_ifs"
+             $opt_silent || {
+                 func_quote_for_expand "$cmd"
+                 eval "func_echo $func_quote_for_expand_result"
+             }
+             $opt_dry_run || eval "$cmd" || {
+               lt_exit=$?
+
+               # Restore the uninstalled library and exit
+               if test "$mode" = relink; then
+                 ( cd "$output_objdir" && \
+                   $RM "${realname}T" && \
+                   $MV "${realname}U" "$realname" )
+               fi
+
+               exit $lt_exit
+             }
+           done
+           IFS="$save_ifs"
+
+           if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+             func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+             func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+           fi
+         fi
+
+          if ${skipped_export-false}; then
+           if test -n "$export_symbols" && test -n "$include_expsyms"; then
+             tmp_export_symbols="$export_symbols"
+             test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+             $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+           fi
+
+           if test -n "$orig_export_symbols"; then
+             # The given exports_symbols file has to be filtered, so filter it.
+             func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+             # FIXME: $output_objdir/$libname.filter potentially contains lots of
+             # 's' commands which not all seds can handle. GNU sed should be fine
+             # though. Also, the filter scales superlinearly with the number of
+             # global variables. join(1) would be nice here, but unfortunately
+             # isn't a blessed tool.
+             $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+             delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+             export_symbols=$output_objdir/$libname.def
+             $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+           fi
+         fi
+
+         libobjs=$output
+         # Restore the value of output.
+         output=$save_output
+
+         if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+           eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+           test "X$libobjs" = "X " && libobjs=
+         fi
+         # Expand the library linking commands again to reset the
+         # value of $libobjs for piecewise linking.
+
+         # Do each of the archive commands.
+         if test "$module" = yes && test -n "$module_cmds" ; then
+           if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+             cmds=$module_expsym_cmds
+           else
+             cmds=$module_cmds
+           fi
+         else
+           if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+             cmds=$archive_expsym_cmds
+           else
+             cmds=$archive_cmds
+           fi
+         fi
+       fi
+
+       if test -n "$delfiles"; then
+         # Append the command to remove temporary files to $cmds.
+         eval cmds=\"\$cmds~\$RM $delfiles\"
+       fi
+
+       # Add any objects from preloaded convenience libraries
+       if test -n "$dlprefiles"; then
+         gentop="$output_objdir/${outputname}x"
+         generated="$generated $gentop"
+
+         func_extract_archives $gentop $dlprefiles
+         libobjs="$libobjs $func_extract_archives_result"
+         test "X$libobjs" = "X " && libobjs=
+       fi
+
+       save_ifs="$IFS"; IFS='~'
+       for cmd in $cmds; do
+         IFS="$save_ifs"
+         eval cmd=\"$cmd\"
+         $opt_silent || {
+           func_quote_for_expand "$cmd"
+           eval "func_echo $func_quote_for_expand_result"
+         }
+         $opt_dry_run || eval "$cmd" || {
+           lt_exit=$?
+
+           # Restore the uninstalled library and exit
+           if test "$mode" = relink; then
+             ( cd "$output_objdir" && \
+               $RM "${realname}T" && \
+               $MV "${realname}U" "$realname" )
+           fi
+
+           exit $lt_exit
+         }
+       done
+       IFS="$save_ifs"
+
+       # Restore the uninstalled library and exit
+       if test "$mode" = relink; then
+         $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+         if test -n "$convenience"; then
+           if test -z "$whole_archive_flag_spec"; then
+             func_show_eval '${RM}r "$gentop"'
+           fi
+         fi
+
+         exit $EXIT_SUCCESS
+       fi
+
+       # Create links to the real library.
+       for linkname in $linknames; do
+         if test "$realname" != "$linkname"; then
+           func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+         fi
+       done
+
+       # If -module or -export-dynamic was specified, set the dlname.
+       if test "$module" = yes || test "$export_dynamic" = yes; then
+         # On all known operating systems, these are identical.
+         dlname="$soname"
+       fi
+      fi
+      ;;
+
+    obj)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       func_warning "\`-dlopen' is ignored for objects"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+       func_warning "\`-l' and \`-L' are ignored for objects" ;;
+      esac
+
+      test -n "$rpath" && \
+       func_warning "\`-rpath' is ignored for objects"
+
+      test -n "$xrpath" && \
+       func_warning "\`-R' is ignored for objects"
+
+      test -n "$vinfo" && \
+       func_warning "\`-version-info' is ignored for objects"
+
+      test -n "$release" && \
+       func_warning "\`-release' is ignored for objects"
+
+      case $output in
+      *.lo)
+       test -n "$objs$old_deplibs" && \
+         func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+
+       libobj=$output
+       func_lo2o "$libobj"
+       obj=$func_lo2o_result
+       ;;
+      *)
+       libobj=
+       obj="$output"
+       ;;
+      esac
+
+      # Delete the old objects.
+      $opt_dry_run || $RM $obj $libobj
+
+      # Objects from convenience libraries.  This assumes
+      # single-version convenience libraries.  Whenever we create
+      # different ones for PIC/non-PIC, this we'll have to duplicate
+      # the extraction.
+      reload_conv_objs=
+      gentop=
+      # reload_cmds runs $LD directly, so let us get rid of
+      # -Wl from whole_archive_flag_spec and hope we can get by with
+      # turning comma into space..
+      wl=
+
+      if test -n "$convenience"; then
+       if test -n "$whole_archive_flag_spec"; then
+         eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+         reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'`
+       else
+         gentop="$output_objdir/${obj}x"
+         generated="$generated $gentop"
+
+         func_extract_archives $gentop $convenience
+         reload_conv_objs="$reload_objs $func_extract_archives_result"
+       fi
+      fi
+
+      # Create the old-style object.
+      reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+      output="$obj"
+      func_execute_cmds "$reload_cmds" 'exit $?'
+
+      # Exit if we aren't doing a library object file.
+      if test -z "$libobj"; then
+       if test -n "$gentop"; then
+         func_show_eval '${RM}r "$gentop"'
+       fi
+
+       exit $EXIT_SUCCESS
+      fi
+
+      if test "$build_libtool_libs" != yes; then
+       if test -n "$gentop"; then
+         func_show_eval '${RM}r "$gentop"'
+       fi
+
+       # Create an invalid libtool object if no PIC, so that we don't
+       # accidentally link it into a program.
+       # $show "echo timestamp > $libobj"
+       # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+       exit $EXIT_SUCCESS
+      fi
+
+      if test -n "$pic_flag" || test "$pic_mode" != default; then
+       # Only do commands if we really have different PIC objects.
+       reload_objs="$libobjs $reload_conv_objs"
+       output="$libobj"
+       func_execute_cmds "$reload_cmds" 'exit $?'
+      fi
+
+      if test -n "$gentop"; then
+       func_show_eval '${RM}r "$gentop"'
+      fi
+
+      exit $EXIT_SUCCESS
+      ;;
+
+    prog)
+      case $host in
+       *cygwin*) func_stripname '' '.exe' "$output"
+                 output=$func_stripname_result.exe;;
+      esac
+      test -n "$vinfo" && \
+       func_warning "\`-version-info' is ignored for programs"
+
+      test -n "$release" && \
+       func_warning "\`-release' is ignored for programs"
+
+      test "$preload" = yes \
+        && test "$dlopen_support" = unknown \
+       && test "$dlopen_self" = unknown \
+       && test "$dlopen_self_static" = unknown && \
+         func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+      case $host in
+      *-*-rhapsody* | *-*-darwin1.[012])
+       # On Rhapsody replace the C library is the System framework
+       compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+       finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+       ;;
+      esac
+
+      case $host in
+      *-*-darwin*)
+       # Don't allow lazy linking, it breaks C++ global constructors
+       # But is supposedly fixed on 10.4 or later (yay!).
+       if test "$tagname" = CXX ; then
+         case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+           10.[0123])
+             compile_command="$compile_command ${wl}-bind_at_load"
+             finalize_command="$finalize_command ${wl}-bind_at_load"
+           ;;
+         esac
+       fi
+       # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+       compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+       finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+       ;;
+      esac
+
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+       case " $new_libs " in
+       *" -L$path/$objdir "*) ;;
+       *)
+         case " $compile_deplibs " in
+         *" -L$path/$objdir "*)
+           new_libs="$new_libs -L$path/$objdir" ;;
+         esac
+         ;;
+       esac
+      done
+      for deplib in $compile_deplibs; do
+       case $deplib in
+       -L*)
+         case " $new_libs " in
+         *" $deplib "*) ;;
+         *) new_libs="$new_libs $deplib" ;;
+         esac
+         ;;
+       *) new_libs="$new_libs $deplib" ;;
+       esac
+      done
+      compile_deplibs="$new_libs"
+
+
+      compile_command="$compile_command $compile_deplibs"
+      finalize_command="$finalize_command $finalize_deplibs"
+
+      if test -n "$rpath$xrpath"; then
+       # If the user specified any rpath flags, then add them.
+       for libdir in $rpath $xrpath; do
+         # This is the magic to use -rpath.
+         case "$finalize_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_rpath="$finalize_rpath $libdir" ;;
+         esac
+       done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           rpath="$rpath $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$perm_rpath " in
+         *" $libdir "*) ;;
+         *) perm_rpath="$perm_rpath $libdir" ;;
+         esac
+       fi
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+         testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+         case :$dllsearchpath: in
+         *":$libdir:"*) ;;
+         ::) dllsearchpath=$libdir;;
+         *) dllsearchpath="$dllsearchpath:$libdir";;
+         esac
+         case :$dllsearchpath: in
+         *":$testbindir:"*) ;;
+         ::) dllsearchpath=$testbindir;;
+         *) dllsearchpath="$dllsearchpath:$testbindir";;
+         esac
+         ;;
+       esac
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath="$rpath"
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           rpath="$rpath $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$finalize_perm_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+         esac
+       fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath="$rpath"
+
+      if test -n "$libobjs" && test "$build_old_libs" = yes; then
+       # Transform all the library objects into standard objects.
+       compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+       finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+      fi
+
+      func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+
+      # template prelinking step
+      if test -n "$prelink_cmds"; then
+       func_execute_cmds "$prelink_cmds" 'exit $?'
+      fi
+
+      wrappers_required=yes
+      case $host in
+      *cygwin* | *mingw* )
+        if test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      *cegcc)
+        # Disable wrappers for cegcc, we are cross compiling anyway.
+        wrappers_required=no
+        ;;
+      *)
+        if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      esac
+      if test "$wrappers_required" = no; then
+       # Replace the output file specification.
+       compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+       link_command="$compile_command$compile_rpath"
+
+       # We have no uninstalled library dependencies, so finalize right now.
+       exit_status=0
+       func_show_eval "$link_command" 'exit_status=$?'
+
+       # Delete the generated files.
+       if test -f "$output_objdir/${outputname}S.${objext}"; then
+         func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+       fi
+
+       exit $exit_status
+      fi
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+       compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+       finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+       if test -n "$perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $perm_rpath; do
+           rpath="$rpath$dir:"
+         done
+         compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+       if test -n "$finalize_perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $finalize_perm_rpath; do
+           rpath="$rpath$dir:"
+         done
+         finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+      fi
+
+      if test "$no_install" = yes; then
+       # We don't need to create a wrapper script.
+       link_command="$compile_var$compile_command$compile_rpath"
+       # Replace the output file specification.
+       link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+       # Delete the old output file.
+       $opt_dry_run || $RM $output
+       # Link the executable and exit
+       func_show_eval "$link_command" 'exit $?'
+       exit $EXIT_SUCCESS
+      fi
+
+      if test "$hardcode_action" = relink; then
+       # Fast installation is not supported
+       link_command="$compile_var$compile_command$compile_rpath"
+       relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+       func_warning "this platform does not like uninstalled shared libraries"
+       func_warning "\`$output' will be relinked during installation"
+      else
+       if test "$fast_install" != no; then
+         link_command="$finalize_var$compile_command$finalize_rpath"
+         if test "$fast_install" = yes; then
+           relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+         else
+           # fast_install is set to needless
+           relink_command=
+         fi
+       else
+         link_command="$compile_var$compile_command$compile_rpath"
+         relink_command="$finalize_var$finalize_command$finalize_rpath"
+       fi
+      fi
+
+      # Replace the output file specification.
+      link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+      # Delete the old output files.
+      $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      func_show_eval "$link_command" 'exit $?'
+
+      # Now create the wrapper script.
+      func_verbose "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+       # Preserve any variables that may affect compiler behavior
+       for var in $variables_saved_for_relink; do
+         if eval test -z \"\${$var+set}\"; then
+           relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+         elif eval var_value=\$$var; test -z "$var_value"; then
+           relink_command="$var=; export $var; $relink_command"
+         else
+           func_quote_for_eval "$var_value"
+           relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+         fi
+       done
+       relink_command="(cd `pwd`; $relink_command)"
+       relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Quote $ECHO for shipping.
+      if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then
+       case $progpath in
+       [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";;
+       *) qecho="$SHELL `pwd`/$progpath --fallback-echo";;
+       esac
+       qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"`
+      else
+       qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Only actually do things if not in dry run mode.
+      $opt_dry_run || {
+       # win32 will think the script is a binary if it has
+       # a .exe suffix, so we strip it off here.
+       case $output in
+         *.exe) func_stripname '' '.exe' "$output"
+                output=$func_stripname_result ;;
+       esac
+       # test for cygwin because mv fails w/o .exe extensions
+       case $host in
+         *cygwin*)
+           exeext=.exe
+           func_stripname '' '.exe' "$outputname"
+           outputname=$func_stripname_result ;;
+         *) exeext= ;;
+       esac
+       case $host in
+         *cygwin* | *mingw* )
+           func_dirname_and_basename "$output" "" "."
+           output_name=$func_basename_result
+           output_path=$func_dirname_result
+           cwrappersource="$output_path/$objdir/lt-$output_name.c"
+           cwrapper="$output_path/$output_name.exe"
+           $RM $cwrappersource $cwrapper
+           trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+           func_emit_cwrapperexe_src > $cwrappersource
+
+           # The wrapper executable is built using the $host compiler,
+           # because it contains $host paths and files. If cross-
+           # compiling, it, like the target executable, must be
+           # executed on the $host or under an emulation environment.
+           $opt_dry_run || {
+             $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+             $STRIP $cwrapper
+           }
+
+           # Now, create the wrapper script for func_source use:
+           func_ltwrapper_scriptname $cwrapper
+           $RM $func_ltwrapper_scriptname_result
+           trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+           $opt_dry_run || {
+             # note: this script will not be executed, so do not chmod.
+             if test "x$build" = "x$host" ; then
+               $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+             else
+               func_emit_wrapper no > $func_ltwrapper_scriptname_result
+             fi
+           }
+         ;;
+         * )
+           $RM $output
+           trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+           func_emit_wrapper no > $output
+           chmod +x $output
+         ;;
+       esac
+      }
+      exit $EXIT_SUCCESS
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      if test "$build_libtool_libs" = convenience; then
+       oldobjs="$libobjs_save $symfileobj"
+       addlibs="$convenience"
+       build_libtool_libs=no
+      else
+       if test "$build_libtool_libs" = module; then
+         oldobjs="$libobjs_save"
+         build_libtool_libs=no
+       else
+         oldobjs="$old_deplibs $non_pic_objects"
+         if test "$preload" = yes && test -f "$symfileobj"; then
+           oldobjs="$oldobjs $symfileobj"
+         fi
+       fi
+       addlibs="$old_convenience"
+      fi
+
+      if test -n "$addlibs"; then
+       gentop="$output_objdir/${outputname}x"
+       generated="$generated $gentop"
+
+       func_extract_archives $gentop $addlibs
+       oldobjs="$oldobjs $func_extract_archives_result"
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+       cmds=$old_archive_from_new_cmds
+      else
+
+       # Add any objects from preloaded convenience libraries
+       if test -n "$dlprefiles"; then
+         gentop="$output_objdir/${outputname}x"
+         generated="$generated $gentop"
+
+         func_extract_archives $gentop $dlprefiles
+         oldobjs="$oldobjs $func_extract_archives_result"
+       fi
+
+       # POSIX demands no paths to be encoded in archives.  We have
+       # to avoid creating archives with duplicate basenames if we
+       # might have to extract them afterwards, e.g., when creating a
+       # static archive out of a convenience library, or when linking
+       # the entirety of a libtool archive into another (currently
+       # not supported by libtool).
+       if (for obj in $oldobjs
+           do
+             func_basename "$obj"
+             $ECHO "$func_basename_result"
+           done | sort | sort -uc >/dev/null 2>&1); then
+         :
+       else
+         $ECHO "copying selected object files to avoid basename conflicts..."
+         gentop="$output_objdir/${outputname}x"
+         generated="$generated $gentop"
+         func_mkdir_p "$gentop"
+         save_oldobjs=$oldobjs
+         oldobjs=
+         counter=1
+         for obj in $save_oldobjs
+         do
+           func_basename "$obj"
+           objbase="$func_basename_result"
+           case " $oldobjs " in
+           " ") oldobjs=$obj ;;
+           *[\ /]"$objbase "*)
+             while :; do
+               # Make sure we don't pick an alternate name that also
+               # overlaps.
+               newobj=lt$counter-$objbase
+               func_arith $counter + 1
+               counter=$func_arith_result
+               case " $oldobjs " in
+               *[\ /]"$newobj "*) ;;
+               *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+               esac
+             done
+             func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+             oldobjs="$oldobjs $gentop/$newobj"
+             ;;
+           *) oldobjs="$oldobjs $obj" ;;
+           esac
+         done
+       fi
+       eval cmds=\"$old_archive_cmds\"
+
+       func_len " $cmds"
+       len=$func_len_result
+       if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+         cmds=$old_archive_cmds
+       else
+         # the command line is too long to link in one step, link in parts
+         func_verbose "using piecewise archive linking..."
+         save_RANLIB=$RANLIB
+         RANLIB=:
+         objlist=
+         concat_cmds=
+         save_oldobjs=$oldobjs
+         oldobjs=
+         # Is there a better way of finding the last object in the list?
+         for obj in $save_oldobjs
+         do
+           last_oldobj=$obj
+         done
+         eval test_cmds=\"$old_archive_cmds\"
+         func_len " $test_cmds"
+         len0=$func_len_result
+         len=$len0
+         for obj in $save_oldobjs
+         do
+           func_len " $obj"
+           func_arith $len + $func_len_result
+           len=$func_arith_result
+           func_append objlist " $obj"
+           if test "$len" -lt "$max_cmd_len"; then
+             :
+           else
+             # the above command should be used before it gets too long
+             oldobjs=$objlist
+             if test "$obj" = "$last_oldobj" ; then
+               RANLIB=$save_RANLIB
+             fi
+             test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+             eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+             objlist=
+             len=$len0
+           fi
+         done
+         RANLIB=$save_RANLIB
+         oldobjs=$objlist
+         if test "X$oldobjs" = "X" ; then
+           eval cmds=\"\$concat_cmds\"
+         else
+           eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+         fi
+       fi
+      fi
+      func_execute_cmds "$cmds" 'exit $?'
+    done
+
+    test -n "$generated" && \
+      func_show_eval "${RM}r$generated"
+
+    # Now create the libtool archive.
+    case $output in
+    *.la)
+      old_library=
+      test "$build_old_libs" = yes && old_library="$libname.$libext"
+      func_verbose "creating $output"
+
+      # Preserve any variables that may affect compiler behavior
+      for var in $variables_saved_for_relink; do
+       if eval test -z \"\${$var+set}\"; then
+         relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+       elif eval var_value=\$$var; test -z "$var_value"; then
+         relink_command="$var=; export $var; $relink_command"
+       else
+         func_quote_for_eval "$var_value"
+         relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+       fi
+      done
+      # Quote the link command for shipping.
+      relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+      relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+      if test "$hardcode_automatic" = yes ; then
+       relink_command=
+      fi
+
+      # Only create the output if not a dry run.
+      $opt_dry_run || {
+       for installed in no yes; do
+         if test "$installed" = yes; then
+           if test -z "$install_libdir"; then
+             break
+           fi
+           output="$output_objdir/$outputname"i
+           # Replace all uninstalled libtool libraries with the installed ones
+           newdependency_libs=
+           for deplib in $dependency_libs; do
+             case $deplib in
+             *.la)
+               func_basename "$deplib"
+               name="$func_basename_result"
+               eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+               test -z "$libdir" && \
+                 func_fatal_error "\`$deplib' is not a valid libtool archive"
+               newdependency_libs="$newdependency_libs $libdir/$name"
+               ;;
+             *) newdependency_libs="$newdependency_libs $deplib" ;;
+             esac
+           done
+           dependency_libs="$newdependency_libs"
+           newdlfiles=
+
+           for lib in $dlfiles; do
+             case $lib in
+             *.la)
+               func_basename "$lib"
+               name="$func_basename_result"
+               eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+               test -z "$libdir" && \
+                 func_fatal_error "\`$lib' is not a valid libtool archive"
+               newdlfiles="$newdlfiles $libdir/$name"
+               ;;
+             *) newdlfiles="$newdlfiles $lib" ;;
+             esac
+           done
+           dlfiles="$newdlfiles"
+           newdlprefiles=
+           for lib in $dlprefiles; do
+             case $lib in
+             *.la)
+               # Only pass preopened files to the pseudo-archive (for
+               # eventual linking with the app. that links it) if we
+               # didn't already link the preopened objects directly into
+               # the library:
+               func_basename "$lib"
+               name="$func_basename_result"
+               eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+               test -z "$libdir" && \
+                 func_fatal_error "\`$lib' is not a valid libtool archive"
+               newdlprefiles="$newdlprefiles $libdir/$name"
+               ;;
+             esac
+           done
+           dlprefiles="$newdlprefiles"
+         else
+           newdlfiles=
+           for lib in $dlfiles; do
+             case $lib in
+               [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+               *) abs=`pwd`"/$lib" ;;
+             esac
+             newdlfiles="$newdlfiles $abs"
+           done
+           dlfiles="$newdlfiles"
+           newdlprefiles=
+           for lib in $dlprefiles; do
+             case $lib in
+               [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+               *) abs=`pwd`"/$lib" ;;
+             esac
+             newdlprefiles="$newdlprefiles $abs"
+           done
+           dlprefiles="$newdlprefiles"
+         fi
+         $RM $output
+         # place dlname in correct position for cygwin
+         tdlname=$dlname
+         case $host,$output,$installed,$module,$dlname in
+           *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
+         esac
+         $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+         if test "$installed" = no && test "$need_relink" = yes; then
+           $ECHO >> $output "\
+relink_command=\"$relink_command\""
+         fi
+       done
+      }
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+      ;;
+    esac
+    exit $EXIT_SUCCESS
+}
+
+{ test "$mode" = link || test "$mode" = relink; } &&
+    func_mode_link ${1+"$@"}
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+    $opt_debug
+    RM="$nonopt"
+    files=
+    rmforce=
+    exit_status=0
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    for arg
+    do
+      case $arg in
+      -f) RM="$RM $arg"; rmforce=yes ;;
+      -*) RM="$RM $arg" ;;
+      *) files="$files $arg" ;;
+      esac
+    done
+
+    test -z "$RM" && \
+      func_fatal_help "you must specify an RM program"
+
+    rmdirs=
+
+    origobjdir="$objdir"
+    for file in $files; do
+      func_dirname "$file" "" "."
+      dir="$func_dirname_result"
+      if test "X$dir" = X.; then
+       objdir="$origobjdir"
+      else
+       objdir="$dir/$origobjdir"
+      fi
+      func_basename "$file"
+      name="$func_basename_result"
+      test "$mode" = uninstall && objdir="$dir"
+
+      # Remember objdir for removal later, being careful to avoid duplicates
+      if test "$mode" = clean; then
+       case " $rmdirs " in
+         *" $objdir "*) ;;
+         *) rmdirs="$rmdirs $objdir" ;;
+       esac
+      fi
+
+      # Don't error if the file doesn't exist and rm -f was used.
+      if { test -L "$file"; } >/dev/null 2>&1 ||
+        { test -h "$file"; } >/dev/null 2>&1 ||
+        test -f "$file"; then
+       :
+      elif test -d "$file"; then
+       exit_status=1
+       continue
+      elif test "$rmforce" = yes; then
+       continue
+      fi
+
+      rmfiles="$file"
+
+      case $name in
+      *.la)
+       # Possibly a libtool archive, so verify it.
+       if func_lalib_p "$file"; then
+         func_source $dir/$name
+
+         # Delete the libtool libraries and symlinks.
+         for n in $library_names; do
+           rmfiles="$rmfiles $objdir/$n"
+         done
+         test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library"
+
+         case "$mode" in
+         clean)
+           case "  $library_names " in
+           # "  " in the beginning catches empty $dlname
+           *" $dlname "*) ;;
+           *) rmfiles="$rmfiles $objdir/$dlname" ;;
+           esac
+           test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i"
+           ;;
+         uninstall)
+           if test -n "$library_names"; then
+             # Do each command in the postuninstall commands.
+             func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+           fi
+
+           if test -n "$old_library"; then
+             # Do each command in the old_postuninstall commands.
+             func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+           fi
+           # FIXME: should reinstall the best remaining shared library.
+           ;;
+         esac
+       fi
+       ;;
+
+      *.lo)
+       # Possibly a libtool object, so verify it.
+       if func_lalib_p "$file"; then
+
+         # Read the .lo file
+         func_source $dir/$name
+
+         # Add PIC object to the list of files to remove.
+         if test -n "$pic_object" &&
+            test "$pic_object" != none; then
+           rmfiles="$rmfiles $dir/$pic_object"
+         fi
+
+         # Add non-PIC object to the list of files to remove.
+         if test -n "$non_pic_object" &&
+            test "$non_pic_object" != none; then
+           rmfiles="$rmfiles $dir/$non_pic_object"
+         fi
+       fi
+       ;;
+
+      *)
+       if test "$mode" = clean ; then
+         noexename=$name
+         case $file in
+         *.exe)
+           func_stripname '' '.exe' "$file"
+           file=$func_stripname_result
+           func_stripname '' '.exe' "$name"
+           noexename=$func_stripname_result
+           # $file with .exe has already been added to rmfiles,
+           # add $file without .exe
+           rmfiles="$rmfiles $file"
+           ;;
+         esac
+         # Do a test to see if this is a libtool program.
+         if func_ltwrapper_p "$file"; then
+           if func_ltwrapper_executable_p "$file"; then
+             func_ltwrapper_scriptname "$file"
+             relink_command=
+             func_source $func_ltwrapper_scriptname_result
+             rmfiles="$rmfiles $func_ltwrapper_scriptname_result"
+           else
+             relink_command=
+             func_source $dir/$noexename
+           fi
+
+           # note $name still contains .exe if it was in $file originally
+           # as does the version of $file that was added into $rmfiles
+           rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}"
+           if test "$fast_install" = yes && test -n "$relink_command"; then
+             rmfiles="$rmfiles $objdir/lt-$name"
+           fi
+           if test "X$noexename" != "X$name" ; then
+             rmfiles="$rmfiles $objdir/lt-${noexename}.c"
+           fi
+         fi
+       fi
+       ;;
+      esac
+      func_show_eval "$RM $rmfiles" 'exit_status=1'
+    done
+    objdir="$origobjdir"
+
+    # Try to remove the ${objdir}s in the directories where we deleted files
+    for dir in $rmdirs; do
+      if test -d "$dir"; then
+       func_show_eval "rmdir $dir >/dev/null 2>&1"
+      fi
+    done
+
+    exit $exit_status
+}
+
+{ test "$mode" = uninstall || test "$mode" = clean; } &&
+    func_mode_uninstall ${1+"$@"}
+
+test -z "$mode" && {
+  help="$generic_help"
+  func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+  func_fatal_help "invalid operation mode \`$mode'"
+
+if test -n "$exec_cmd"; then
+  eval exec "$exec_cmd"
+  exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries.  Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them.  This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration.  But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
+# vi:sw=2
+
diff --git a/missing b/missing
new file mode 100755 (executable)
index 0000000..28055d2
--- /dev/null
+++ b/missing
@@ -0,0 +1,376 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
+# 2008, 2009 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try \`$0 --help' for more information"
+  exit 1
+fi
+
+run=:
+sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
+sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+  configure_ac=configure.ac
+else
+  configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case $1 in
+--run)
+  # Try to run requested program, and just exit if it succeeds.
+  run=
+  shift
+  "$@" && exit 0
+  # Exit code 63 means version mismatch.  This often happens
+  # when the user try to use an ancient version of a tool on
+  # a file that requires a minimum version.  In this case we
+  # we should proceed has if the program had been absent, or
+  # if --run hadn't been passed.
+  if test $? = 63; then
+    run=:
+    msg="probably too old"
+  fi
+  ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+  --run           try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+  aclocal      touch file \`aclocal.m4'
+  autoconf     touch file \`configure'
+  autoheader   touch file \`config.h.in'
+  autom4te     touch the output file, or create a stub one
+  automake     touch all \`Makefile.in' files
+  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
+  flex         create \`lex.yy.c', if possible, from existing .c
+  help2man     touch the output file
+  lex          create \`lex.yy.c', if possible, from existing .c
+  makeinfo     touch the output file
+  tar          try tar, gnutar, gtar, then tar without non-portable flags
+  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
+\`g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit $?
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit $?
+    ;;
+
+  -*)
+    echo 1>&2 "$0: Unknown \`$1' option"
+    echo 1>&2 "Try \`$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# normalize program name to check for.
+program=`echo "$1" | sed '
+  s/^gnu-//; t
+  s/^gnu//; t
+  s/^g//; t'`
+
+# Now exit if we have it, but it failed.  Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program).  This is about non-GNU programs, so use $1 not
+# $program.
+case $1 in
+  lex*|yacc*)
+    # Not GNU programs, they don't have --version.
+    ;;
+
+  tar*)
+    if test -n "$run"; then
+       echo 1>&2 "ERROR: \`tar' requires --run"
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       exit 1
+    fi
+    ;;
+
+  *)
+    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+       # We have it, but it failed.
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       # Could not run --version or --help.  This is probably someone
+       # running `$TOOL --version' or `$TOOL --help' to check whether
+       # $TOOL exists and not knowing $TOOL uses missing.
+       exit 1
+    fi
+    ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case $program in
+  aclocal*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
+         to install the \`Automake' and \`Perl' packages.  Grab them from
+         any GNU archive site."
+    touch aclocal.m4
+    ;;
+
+  autoconf*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`${configure_ac}'.  You might want to install the
+         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
+         archive site."
+    touch configure
+    ;;
+
+  autoheader*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
+         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
+         from any GNU archive site."
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+    test -z "$files" && files="config.h"
+    touch_files=
+    for f in $files; do
+      case $f in
+      *:*) touch_files="$touch_files "`echo "$f" |
+                                      sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+      *) touch_files="$touch_files $f.in";;
+      esac
+    done
+    touch $touch_files
+    ;;
+
+  automake*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+         You might want to install the \`Automake' and \`Perl' packages.
+         Grab them from any GNU archive site."
+    find . -type f -name Makefile.am -print |
+          sed 's/\.am$/.in/' |
+          while read f; do touch "$f"; done
+    ;;
+
+  autom4te*)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.
+         You can get \`$1' as part of \`Autoconf' from any GNU
+         archive site."
+
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -f "$file"; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo "#! /bin/sh"
+       echo "# Created by GNU Automake missing as a replacement of"
+       echo "#  $ $@"
+       echo "exit 0"
+       chmod +x $file
+       exit 1
+    fi
+    ;;
+
+  bison*|yacc*)
+    echo 1>&2 "\
+WARNING: \`$1' $msg.  You should only need it if
+         you modified a \`.y' file.  You may need the \`Bison' package
+         in order for those modifications to take effect.  You can get
+         \`Bison' from any GNU archive site."
+    rm -f y.tab.c y.tab.h
+    if test $# -ne 1; then
+        eval LASTARG="\${$#}"
+       case $LASTARG in
+       *.y)
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+           if test -f "$SRCFILE"; then
+                cp "$SRCFILE" y.tab.c
+           fi
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+           if test -f "$SRCFILE"; then
+                cp "$SRCFILE" y.tab.h
+           fi
+         ;;
+       esac
+    fi
+    if test ! -f y.tab.h; then
+       echo >y.tab.h
+    fi
+    if test ! -f y.tab.c; then
+       echo 'main() { return 0; }' >y.tab.c
+    fi
+    ;;
+
+  lex*|flex*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.l' file.  You may need the \`Flex' package
+         in order for those modifications to take effect.  You can get
+         \`Flex' from any GNU archive site."
+    rm -f lex.yy.c
+    if test $# -ne 1; then
+        eval LASTARG="\${$#}"
+       case $LASTARG in
+       *.l)
+           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+           if test -f "$SRCFILE"; then
+                cp "$SRCFILE" lex.yy.c
+           fi
+         ;;
+       esac
+    fi
+    if test ! -f lex.yy.c; then
+       echo 'main() { return 0; }' >lex.yy.c
+    fi
+    ;;
+
+  help2man*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+        you modified a dependency of a manual page.  You may need the
+        \`Help2man' package in order for those modifications to take
+        effect.  You can get \`Help2man' from any GNU archive site."
+
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -f "$file"; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo ".ab help2man is required to generate this page"
+       exit $?
+    fi
+    ;;
+
+  makeinfo*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.texi' or \`.texinfo' file, or any other file
+         indirectly affecting the aspect of the manual.  The spurious
+         call might also be the consequence of using a buggy \`make' (AIX,
+         DU, IRIX).  You might want to install the \`Texinfo' package or
+         the \`GNU make' package.  Grab either from any GNU archive site."
+    # The file to touch is that specified with -o ...
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -z "$file"; then
+      # ... or it is the one specified with @setfilename ...
+      infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+      file=`sed -n '
+       /^@setfilename/{
+         s/.* \([^ ]*\) *$/\1/
+         p
+         q
+       }' $infile`
+      # ... or it is derived from the source name (dir/f.texi becomes f.info)
+      test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+    fi
+    # If the file does not exist, the user really needs makeinfo;
+    # let's fail without touching anything.
+    test -f $file || exit 1
+    touch $file
+    ;;
+
+  tar*)
+    shift
+
+    # We have already tried tar in the generic part.
+    # Look for gnutar/gtar before invocation to avoid ugly error
+    # messages.
+    if (gnutar --version > /dev/null 2>&1); then
+       gnutar "$@" && exit 0
+    fi
+    if (gtar --version > /dev/null 2>&1); then
+       gtar "$@" && exit 0
+    fi
+    firstarg="$1"
+    if shift; then
+       case $firstarg in
+       *o*)
+           firstarg=`echo "$firstarg" | sed s/o//`
+           tar "$firstarg" "$@" && exit 0
+           ;;
+       esac
+       case $firstarg in
+       *h*)
+           firstarg=`echo "$firstarg" | sed s/h//`
+           tar "$firstarg" "$@" && exit 0
+           ;;
+       esac
+    fi
+
+    echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+         You may want to install GNU tar or Free paxutils, or check the
+         command line arguments."
+    exit 1
+    ;;
+
+  *)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.  Check the \`README' file,
+         it often tells you about the needed prerequisites for installing
+         this package.  You may also peek at any GNU archive site, in case
+         some other package would contain this missing \`$1' program."
+    exit 1
+    ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/ofono.pc.in b/ofono.pc.in
new file mode 100644 (file)
index 0000000..b4d0a77
--- /dev/null
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+plugindir=${libdir}/@PACKAGE@/plugins
+
+Name: @PACKAGE@
+Description: oFono - Open Source Telephony
+Version: @VERSION@
+Requires: glib-2.0 dbus-1
+Cflags: -I${includedir}
+Libs: -module -avoid-version -export-symbols-regex '@PACKAGE@_plugin_desc'
diff --git a/packaging/0001-ifxmodem-Add-support-for-IPv6-and-dual-mode-contexts.patch b/packaging/0001-ifxmodem-Add-support-for-IPv6-and-dual-mode-contexts.patch
new file mode 100644 (file)
index 0000000..b32a493
--- /dev/null
@@ -0,0 +1,61 @@
+From 0411aa492849de5838d2794a9a9cd78095cae63d Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri, 10 Feb 2012 11:45:34 +0100
+Subject: [PATCH 1/7] ifxmodem: Add support for IPv6 and dual mode contexts
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+---
+ drivers/ifxmodem/gprs-context.c |   22 +++++++++++++++-------
+ 1 files changed, 15 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/ifxmodem/gprs-context.c b/drivers/ifxmodem/gprs-context.c
+index 6902f17..f66e1a1 100644
+--- a/drivers/ifxmodem/gprs-context.c
++++ b/drivers/ifxmodem/gprs-context.c
+@@ -320,11 +320,7 @@ static void ifx_gprs_activate_primary(struct ofono_gprs_context *gc,
+ {
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+-      int len;
+-
+-      /* IPv6 support not implemented */
+-      if (ctx->proto != OFONO_GPRS_PROTO_IP)
+-              goto error;
++      int len = 0;
+       DBG("cid %u", ctx->cid);
+@@ -336,7 +332,20 @@ static void ifx_gprs_activate_primary(struct ofono_gprs_context *gc,
+       gcd->state = STATE_ENABLING;
+-      len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
++      switch (ctx->proto) {
++      case OFONO_GPRS_PROTO_IP:
++              len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
++                                                              ctx->cid);
++              break;
++      case OFONO_GPRS_PROTO_IPV6:
++              len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
++                                                              ctx->cid);
++              break;
++      case OFONO_GPRS_PROTO_IPV4V6:
++              len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
++                                                              ctx->cid);
++              break;
++      }
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len - 3,
+@@ -346,7 +355,6 @@ static void ifx_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               setup_cb, gc, NULL) > 0)
+               return;
+-error:
+       CALLBACK_WITH_FAILURE(cb, data);
+ }
+-- 
+1.7.5.4
+
diff --git a/packaging/0002-ifxmodem-Add-support-for-dynamic-DNS-for-IPv6-and-du.patch b/packaging/0002-ifxmodem-Add-support-for-dynamic-DNS-for-IPv6-and-du.patch
new file mode 100644 (file)
index 0000000..f06d64a
--- /dev/null
@@ -0,0 +1,62 @@
+From b34e1a6e19467baff678e4c22afd234f0e0b137b Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri, 10 Feb 2012 12:02:42 +0100
+Subject: [PATCH 2/7] ifxmodem: Add support for dynamic DNS for IPv6 and dual
+ mode contexts
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+---
+ drivers/ifxmodem/gprs-context.c |   18 +++++++++++++++++-
+ 1 files changed, 17 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/ifxmodem/gprs-context.c b/drivers/ifxmodem/gprs-context.c
+index f66e1a1..4cca265 100644
+--- a/drivers/ifxmodem/gprs-context.c
++++ b/drivers/ifxmodem/gprs-context.c
+@@ -64,6 +64,7 @@ struct gprs_context_data {
+       char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+       GAtRawIP *rawip;
+       enum state state;
++      enum ofono_gprs_proto proto;
+       char address[32];
+       char dns1[32];
+       char dns2[32];
+@@ -301,11 +302,25 @@ static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+       if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+               goto error;
+-      sprintf(buf, "AT+XDNS=%u,1", gcd->active_context);
++      g_at_chat_send(gcd->chat, "AT+XDNS=?", none_prefix, NULL, NULL, NULL);
++
++      switch (gcd->proto) {
++      case OFONO_GPRS_PROTO_IP:
++              sprintf(buf, "AT+XDNS=%u,1", gcd->active_context);
++              break;
++      case OFONO_GPRS_PROTO_IPV6:
++              sprintf(buf, "AT+XDNS=%u,2", gcd->active_context);
++              break;
++      case OFONO_GPRS_PROTO_IPV4V6:
++              sprintf(buf, "AT+XDNS=%u,3", gcd->active_context);
++              break;
++      }
++
+       if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+               goto error;
+       sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
++
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               activate_cb, gc, NULL) > 0)
+               return;
+@@ -331,6 +346,7 @@ static void ifx_gprs_activate_primary(struct ofono_gprs_context *gc,
+       memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+       gcd->state = STATE_ENABLING;
++      gcd->proto = ctx->proto;
+       switch (ctx->proto) {
+       case OFONO_GPRS_PROTO_IP:
+-- 
+1.7.5.4
+
diff --git a/packaging/0003-ifx-Setup-CSCS-to-use-GSM-for-the-aux-port.patch b/packaging/0003-ifx-Setup-CSCS-to-use-GSM-for-the-aux-port.patch
new file mode 100644 (file)
index 0000000..af36a28
--- /dev/null
@@ -0,0 +1,31 @@
+From dd1e6b2ff3e3d1be2ea09350e6684a77a667005f Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri, 10 Feb 2012 12:19:42 +0100
+Subject: [PATCH 3/7] ifx: Setup CSCS to use GSM for the aux port
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+This change is needed since otherwise umlauts and other characters
+for USSD responses will not work properly.
+---
+ plugins/ifx.c |    4 ++++
+ 1 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/plugins/ifx.c b/plugins/ifx.c
+index d1b622c..552b461 100644
+--- a/plugins/ifx.c
++++ b/plugins/ifx.c
+@@ -308,6 +308,10 @@ static void xgendata_query(gboolean ok, GAtResult *result, gpointer user_data)
+       DBG("\n%s", gendata);
++      /* switch to GSM character set instead of IRA */
++      g_at_chat_send(data->dlcs[AUX_DLC], "AT+CSCS=\"GSM\"", none_prefix,
++                                                      NULL, NULL, NULL);
++
+       /* disable UART for power saving */
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+XPOW=0,0,0", none_prefix,
+                                                       NULL, NULL, NULL);
+-- 
+1.7.5.4
+
diff --git a/packaging/0004-atmodem-Fix-handling-of-IFX-signal-strength-indicati.patch b/packaging/0004-atmodem-Fix-handling-of-IFX-signal-strength-indicati.patch
new file mode 100644 (file)
index 0000000..5a500e6
--- /dev/null
@@ -0,0 +1,39 @@
+From d8a084c7484e2794d175c395debee019e4dacf00 Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Tue, 14 Feb 2012 15:23:59 +0100
+Subject: [PATCH 4/7] atmodem: Fix handling of IFX signal strength indication
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+---
+ drivers/atmodem/network-registration.c |   10 ++++++++--
+ 1 files changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/atmodem/network-registration.c b/drivers/atmodem/network-registration.c
+index 936a674..9c639c0 100644
+--- a/drivers/atmodem/network-registration.c
++++ b/drivers/atmodem/network-registration.c
+@@ -692,12 +692,18 @@ static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
+       if (!g_at_result_iter_next_number(&iter, &ind))
+               return;
+-      if (ind == 0)
++      /*
++       * Radio signal strength indicators are defined for 0-7,
++       * but in some cases XCIEV just returns CSQ 0-31,99 values.
++       */
++      if (ind == 0 || ind == 99)
+               strength = -1;
+       else if (ind == 7)
+               strength = 100;
+-      else
++      else if (ind < 7)
+               strength = (ind * 15);
++      else if (ind > 7)
++              strength = (ind * 100) / 31;
+       ofono_netreg_strength_notify(netreg, strength);
+ }
+-- 
+1.7.5.4
+
diff --git a/packaging/0005-call-volume.c-Register-the-call-volume-interface-jus.patch b/packaging/0005-call-volume.c-Register-the-call-volume-interface-jus.patch
new file mode 100644 (file)
index 0000000..37781c5
--- /dev/null
@@ -0,0 +1,37 @@
+From 65a65eb70ca5f2af4efccb2fc7d4e00661631520 Mon Sep 17 00:00:00 2001
+From: Philippe Nunes <philippe.nunes@linux.intel.com>
+Date: Wed, 15 Feb 2012 11:38:59 +0100
+Subject: [PATCH] call-volume.c: Register the call-volume interface just after the +CMUT query
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+With this change, the mute status and the volume level are initialised in the
+call-volume atom. This allows also to expose the call-volume interface even
+if the command +CLVL is not supported as it is the case for IFX.
+---
+ drivers/atmodem/call-volume.c |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/atmodem/call-volume.c b/drivers/atmodem/call-volume.c
+index e2535b1..4c32ba7 100644
+--- a/drivers/atmodem/call-volume.c
++++ b/drivers/atmodem/call-volume.c
+@@ -65,6 +65,7 @@ static void cmut_query(gboolean ok, GAtResult *result, gpointer user_data)
+       if (g_at_result_iter_next_number(&iter, &muted) == FALSE)
+               return;
++      ofono_call_volume_register(cv);
+       ofono_call_volume_set_muted(cv, muted);
+ }
+@@ -94,7 +95,6 @@ static void clvl_query(gboolean ok, GAtResult *result, gpointer user_data)
+                               (cvd->clvl_max - cvd->clvl_min);
+       ofono_call_volume_set_speaker_volume(cv, percent);
+-      ofono_call_volume_register(cv);
+ }
+ static void clvl_range_query(gboolean ok, GAtResult *result, gpointer user_data)
+-- 
+1.7.1
+
diff --git a/packaging/0006-gisi-Remove-includes-of-glib-gtypes.h.patch b/packaging/0006-gisi-Remove-includes-of-glib-gtypes.h.patch
new file mode 100644 (file)
index 0000000..3f4309c
--- /dev/null
@@ -0,0 +1,51 @@
+From dc41204950b570771efcb0ca1d4b219572216b36 Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon, 19 Mar 2012 16:12:12 -0700
+Subject: [PATCH 5/6] gisi: Remove includes of <glib/gtypes.h>
+
+---
+ gisi/client.h  |    1 -
+ gisi/message.h |    2 +-
+ gisi/modem.h   |    1 -
+ 3 files changed, 1 insertions(+), 3 deletions(-)
+
+diff --git a/gisi/client.h b/gisi/client.h
+index 8224cd2..77b04fe 100644
+--- a/gisi/client.h
++++ b/gisi/client.h
+@@ -27,7 +27,6 @@ extern "C" {
+ #endif
+ #include <stdint.h>
+-#include <glib/gtypes.h>
+ #include "modem.h"
+diff --git a/gisi/message.h b/gisi/message.h
+index 5bdd7ba..f28b889 100644
+--- a/gisi/message.h
++++ b/gisi/message.h
+@@ -27,7 +27,7 @@ extern "C" {
+ #endif
+ #include <stdint.h>
+-#include <glib/gtypes.h>
++#include <glib.h>
+ #include "phonet.h"
+diff --git a/gisi/modem.h b/gisi/modem.h
+index 0a741f6..46370ee 100644
+--- a/gisi/modem.h
++++ b/gisi/modem.h
+@@ -23,7 +23,6 @@
+ #define __GISI_MODEM_H
+ #include <stdint.h>
+-#include <glib/gtypes.h>
+ #include "phonet.h"
+ #include "message.h"
+-- 
+1.7.2.2
+
diff --git a/packaging/0007-isimodem-Remove-includes-of-glib-gtypes.h.patch b/packaging/0007-isimodem-Remove-includes-of-glib-gtypes.h.patch
new file mode 100644 (file)
index 0000000..365db7f
--- /dev/null
@@ -0,0 +1,38 @@
+From 6b0880f8a77b2f161ae9ad14e247a5a1ab769447 Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon, 19 Mar 2012 16:12:36 -0700
+Subject: [PATCH 7/7] isimodem: Remove includes of <glib/gtypes.h>
+
+---
+ drivers/isimodem/uicc-util.h |    1 -
+ drivers/isimodem/uicc.h      |    2 --
+ 2 files changed, 0 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/isimodem/uicc-util.h b/drivers/isimodem/uicc-util.h
+index 7c8179e..aa846d0 100644
+--- a/drivers/isimodem/uicc-util.h
++++ b/drivers/isimodem/uicc-util.h
+@@ -27,7 +27,6 @@
+ extern "C" {
+ #endif
+-#include <glib/gtypes.h>
+ #include <gisi/client.h>
+ struct uicc_sim_data;
+diff --git a/drivers/isimodem/uicc.h b/drivers/isimodem/uicc.h
+index dfcd476..b715932 100644
+--- a/drivers/isimodem/uicc.h
++++ b/drivers/isimodem/uicc.h
+@@ -27,8 +27,6 @@
+ extern "C" {
+ #endif
+-#include <glib/gtypes.h>
+-
+ #include <gisi/client.h>
+ #include <gisi/modem.h>
+-- 
+1.7.2.2
+
diff --git a/packaging/init_ofono b/packaging/init_ofono
new file mode 100644 (file)
index 0000000..a8e95cb
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+export OFONO_AT_DEBUG=1
+ofonod -nd &> /tmp/ofono.log &
+
diff --git a/packaging/ofono-1.4.tar.bz2 b/packaging/ofono-1.4.tar.bz2
new file mode 100644 (file)
index 0000000..e58bd43
Binary files /dev/null and b/packaging/ofono-1.4.tar.bz2 differ
diff --git a/packaging/ofono.changes b/packaging/ofono.changes
new file mode 100644 (file)
index 0000000..52e393e
--- /dev/null
@@ -0,0 +1,304 @@
+* Wed May 30 03:17:39 UTC 2012 - Lin Yang <lin.a.yang@intel.con> - 1.4-6
+- Initial commit to Gerrit
+
+* Thu Mar 29 2012 Arron <arron.wang@intel.com> - 1.4
+- Enable bluetooth build support fixed TDIS-60
+
+* Mon Feb 6 2012 Arron <arron.wang@intel.com> - 1.2
+- Add build support for ofono rules
+
+* Fri Jan 20 2012 Arron <arron.wang@intel.com> - 1.2
+- upgrade to version 1.2
+
+* Tue Nov 22 2011 Arron <arron.wang@intel.com> - 1.0
+- upgrade to version 1.0
+
+* Wed Jul 13 2011 Yu <yu.a.wang@intel.com> - 0.51
+- upgrade to 0.51
+
+* Thu Jun 23 2011 Yu <yu.a.wang@intel.com> - 0.50
+- upgrade to 0.50
+
+* Thu Jun 23 2011 Yu <yu.a.wang@intel.com> - 0.49
+- Remove requirement for package usb-modeswitch and use-modeswitch-date due to this is not the right way to fix BMC#19097
+- Release engineer already added these two packages to package-groups
+
+* Wed Jun 15 2011 Yu <yu.a.wang@intel.com> - 0.49
+- Add requirement for package usb-modeswitch and usb-modeswitch-data fix BMC#19097
+
+* Wed Jun 08 2011 Yu <yu.a.wang@intel.com> - 0.49
+- upgrade to 0.49
+
+* Wed May 25 2011 Chris Ferron <chris.e.ferron@linux.intel.com> - 0.48
+- Updated spec file to change systemctl as a requires to the package systemd 
+- for each scriplet section. Also added a Requires systemd as systemd is configured 
+- as a build option for this package exposing systemd features. 
+
+* Tue May 03 2011 Chris Ferron <chris.e.ferron@linux.intel.com> - 0.48
+- FEA#16109 - [FEA] Implement SystemD as MeeGo init provide
+- Updated the ofono package to be usable by systemd as needed 
+  to implement systemd as the init provider of MeeGo. For this 
+  a systemd ofono.service file was added and installed. This will 
+  allow systemd to start stop and track the service. 
+
+* Fri Apr 29 2011 Junfeng Dong <junfeng.dong@intel.com> - 0.48  
+- Fix the error caused by updating libtool. 
+
+* Tue Apr 26 2011 Yu <yu.a.wang@intel.com> - 0.48
+- upgrade to 0.48 for BMC #14213
+- Fix issue with crash due to not stopped PPP timers.
+- Fix issue with offline mode handling and Huawei modem.
+- Fix issue with missing check for Huawei modem device open.
+- Fix issue with USSD and use of non-cloned GAtChat object.
+
+* Sun Apr 17 2011 Yu <yu.a.wang@intel.com> - 0.47
+- upgrade to 0.47 for BMC #14213
+- Fix issue with entering offline mode prematurely.
+- Add support for CPHS CSP network registration handling.
+
+* Wed Apr 13 2011 Yu <yu.a.wang@intel.com> - 0.46
+- upgrade to 0.46
+- Fix issue with operator name reading and older ISI modems.
+- Fix issue with networking registration and older ISI modems.
+- Fix issue with missing handling of PIN/SIM states and ISI modems.
+- Fix issue with voice call state reporting and ISI modems.
+- Fix issue with STK handling of environment variables.
+- Fix issue with STK and empty URL for launch browser.
+- Fix issue with voice call pause character validation.
+- Fix issue with buffer length and long phone numbers.
+- Fix issue with SMS sending retries and network timeout.
+- Fix issue with missing SMS submit canceled history status.
+- Add support for cancellation of SMS submission.
+- Add support for handling SIM Toolkit display action commands.
+- Add support for handling call forwarding and SIM refresh.
+- Add support for handling EFimg and EFiidf changes.
+- Add support for handling EFmsisdn and EFsdn changes.
+- Add support for handling emergency calls without SIM.
+- Add support for handling emergency calls without PIN.
+- Add support for handling emergency number updates.
+- Add support for assisted satellite navigation interface.
+- Add support for IPv6 contexts and ISI modems.
+- Add support for dual-stack GPRS contexts.
+- Add limited support for CDMA connection manager interface.
+
+* Mon Mar 28 2011 Yu <yu.a.wang@intel.com> - 0.45
+- upgrade to 0.45
+- remove three already integrated patches
+- Fix issue with SIM Toolkit null data object.
+- Fix issue with SIM filesystem and modem release.
+- Fix issue with disconnect handling and Huawei modems.
+- Add support for improved SSN and voicecall handling.
+- Add support for SIM Toolkit Refresh handled by the modem.
+- Add support for multiple AT channels and STE modems.
+- Add support for ISI drivers and wgmodem2.5 handling.
+- Add support for optimized ringbuffer operations.
+- Add support for optimized PPP buffer management.
+
+* Fri Feb 18 2011 Martin Xu <martin.xu@intel.com> - 0.41
+- Add patches:
+-   0001_fix_huawei_em770w.patch
+-   0002_fix_huawei_em770w.patch
+-   0003_fix_huawei_em770w.patch
+- to fix BMC #6944 #10018 #9797 #9201
+
+* Wed Feb 09 2011 Martin Xu <martin.xu@intel.com> - 0.41
+- upgrade to 0.41 for BMC #12692
+- Fix issue with SIM callback handling.
+- Fix issue with XTMS handling and IFX modem.
+- Add support for alphabets and SMS encoding.
+- Add support for generic PIN retries handling.
+- Add support for PIN retries and MBM modem.
+- Add support for radio settings and MBM modem.
+- Add support for cell broadcast and STE modem.
+- Add support for handling ECAV status Released.
+
+* Thu Jan 27 2011 Jouni Peltonen <jouni.peltonen@cybercom.com> - 0.39
+- Fixes BMC#12559. 
+- 0001-isimodem-Fix-race-condition-in-SIM-probe.patch upstream commit: 9306837053cd6ce35e0fe02f03c3cd0eba443f6c.
+- 0002-n900-Fix-online.patch upstream commit: f6f0f4d12116cbf8787928146b3b97df21acb739
+
+* Fri Jan 21 2011 Martin Xu <martin.xu@intel.com> - 0.39
+- upgrade to 0.39 for BMC #12692
+-  Fix issue with not handling empty EFecc properly.
+-  Fix issue with string length and DTMF handling.
+-  Fix issue with missing info for terminal busy result.
+-  Fix issue with signal strength handling and IFX modem.
+-  Fix handling of SIM Toolkit enabling and IFX modem.
+-  Add support for packet switched bearer notifications.
+-  Add support for handling called line identification.
+-  Add support for PIN retry counter interface.
+-  Add support for ST-Ericsson modem init daemon.
+-  Add support for Cinterion TC65 modem.
+-  Add support for simple ISI client interface.
+
+* Fri Jan 07 2011 Martin Xu <martin.xu@intel.com> - 0.38
+- upgrade to 0.38 for BMC #12501
+- Change CalledLine* to ConnectedLine* properties.
+- Fix issue with calling presentation property.
+- Fix issue with network time and ISI modems.
+- Fix issue with timezone reporting and HSO modems.
+- Fix issue with SIM ready status and HSO modems.
+- Fix issue with hidden caller ID and STE modems.
+- Fix issue with handling of STK Setup Menu.
+- Fix issue with missing STK text and icon checks.
+- Fix issue with missing signal strength query.
+
+* Wed Dec 08 2010 Martin Xu <martin.xu@intel.com> - 0.36
+- upgrade to 0.36
+-  Fix issue with CLIR Invocation and Suppression.
+-  Fix issue with power/online transition with ZTE devices.
+-  Fix segmentation fault when removing Nokia Datacard.
+-  Add support for Nokia CS-17 dongles.
+-  Add support for Ericsson F5521gw devices.
+-  Add support for CAIF network interface management.
+-  Add support for COLR in generic AT modem driver.
+-  Add support for SMS Point-to-Point download to UICC.
+-  Add support for checking specific service availability.
+-  Add support for handling null text field for STK.
+
+* Mon Nov 15 2010 Martin Xu <martin.xu@intel.com> - 0.35
+- upgrade to 0.35
+
+* Wed Nov 03 2010 Martin Xu <martin.xu@intel.com> - 0.34
+- upgrade to 0.34
+
+* Tue Oct 26 2010 Martin Xu <martin.xu@intel.com> - 0.33
+- upgrade to 0.33 to fix IFX-MAL bugs
+
+* Wed Oct 13 2010 Anas Nashif <nashif@linux.intel.com> - 0.31
+- Remove requirement on ofono-config
+- Remove unused and obsolete patches
+
+* Mon Oct 11 2010 Martin Xu <martin.xu@intel.com> - 0.31
+- upgrade to 0.31
+- Remove the N900 package patches, waiting for them in upstream
+
+* Tue Sep 21 2010 Marko Saukko <marko.saukko@cybercom.com> - 0.26
+- Updated N900 patch
+- Added modem.conf back as phonet is autodetected with the new patches.
+- This is the final piece of implementing FEA#4134, FEA#4135, FEA#4136
+  (Dialer - Make call, end call, receive call) on ARM/N900. 
+- Fixes BMC#5662 (Nokia N900 modem does not turn on in Ofono)
+
+* Fri Sep 3 2010 Carsten Valdemar Munk <carsten@maemo.org> - 0.26
+- Include N900 patch
+- Seperate modem.conf out into seperate packages providing ofono-config. Reasoning is because there might be other
+  devices with phonet0, not all n900modem.
+
+* Thu Aug 26 2010 Martin Xu <martin.xu@intel.com> - 0.26
+- upgrade to 0.26
+
+* Mon Jul 19 2010 Martin Xu <martin.xu@intel.com> - 0.25
+- upgrade to 0.25
+
+* Wed Jul 14 2010 Martin Xu <martin.xu@intel.com> - 0.24
+- upgrade to 0.24
+
+* Tue Jun 22 2010 Martin Xu <martin.xu@intel.com> - 0.23
+- Upgrade to 0.23
+
+* Fri May 07 2010 Prajwal Mohan <prajwal.karur.mohan@intel.com> - 0.20  
+- Enabling phonesim for handset images  
+* Thu Mar 25 2010 Martin Xu <martin.xu@intel.com> - 0.20
+- Upgrade to 0.20
+
+* Tue Feb 23 2010 Martin Xu <martin.xu@intel.com> - 0.18
+- upgrade to 0.18 
+- remove 0001-add-netmask-to-hso-gprs-context-driver.patch
+
+* Mon Feb 22 2010 Anas Nashif <anas.nashif@intel.com> - 0.15
+- Use spectacle
+- Update Group
+
+* Tue Jan 05 2010 Martin Xu <martin.xu@intel.com> - 0.15
+- upgrade to 0.15
+- add patch 0001-add-netmask-to-hso-gprs-context-driver.patch 
+
+* Monday Dec 14 2009 Martin Xu <martin.xu@intel.com> - 0.14
+- upgrade to 0.14
+
+* Thu Dec 7 2009 Martin Xu <martin.xu@intel.com> - 0.13
+- remove 0001-Allow-builds-to-install-the-test-scripts-for-debuggi.patch
+- remove use_AT_CFUN_to_query_powered_state.patch
+- patches has been merged in upstream
+
+* Thu Dec 7 2009 Martin Xu <martin.xu@intel.com> - 0.13
+- upgrade to 0.13
+
+* Thu Dec 3 2009 Martin Xu <martin.xu@intel.com> - 0.12
+- upgrade to 0.12
+- clean up spec file
+- add test subpackage
+
+* Wed Nov 25 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.11
+Update to version 0.11
+
+* Wed Oct 21 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Additional helper scripts added
+
+* Mon Oct 19 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Fix install perms on .desktop file
+
+* Mon Oct 19 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Fixed .ini file and actually added the .desktop file this time
+
+* Mon Oct 19 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Add .desktop file back in
+
+* Wed Oct 14 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Fix build
+
+* Wed Oct 14 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Fix build
+
+* Wed Oct 14 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Remove service autostart and allow to run as default user
+
+* Tue Oct 06 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Fix hardcoded modem path in ofono-modem-power script
+
+* Tue Oct 06 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.7
+Update to version 0.7
+
+* Fri Sep 11 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.4
+Obsolete now defunct ofono-extras package
+
+* Thu Sep 10 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.4
+Updated ofono-devel.files with man files and header glob
+
+* Thu Sep 10 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.4
+Fixed bad ref to modem.conf in ofono.files
+
+* Thu Sep 10 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.4
+Created missing bin dir
+
+* Thu Sep 10 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.4
+Created missing autostart and dbus dirs
+
+* Thu Sep 10 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.4
+Fixed bad sysconfdir reference
+
+* Thu Sep 10 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.4
+Update to version 0.4
+Added moblin specific configurations and patches
+
+* Fri Jul 31 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.2
+- Add new headers to ofono-devel.files list
+
+* Fri Jul 31 2009 Shane Bryan <shane.bryan@linux.intel.com> - 0.2
+- Update to tip of tree (0.2+)
+
+* Wed Jun 24 2009 Shane Bryan <shane.bryan@linux.intel.com> 0.0
+- Tip of tree update (127b56baccc8830eb1), plus my patches
+
+* Tue Jun 23 2009 Shane Bryan <shane.bryan@linux.intel.com> 0.0
+- Add new history.h to installed devel file list
+
+* Tue Jun 23 2009 Shane Bryan <shane.bryan@linux.intel.com> 0.0
+- Pulling in latest from tip or tree
+
+* Thu May 21 2009 Shane Bryan <shane.bryan@linux.intel.com> 0.0
+- Initial import into Moblin, based on upstream ofono.org git,
+  commit c427cdfdfacbec9b0221e157797e6c9d33113e91
+
diff --git a/packaging/ofono.spec b/packaging/ofono.spec
new file mode 100644 (file)
index 0000000..59214bf
--- /dev/null
@@ -0,0 +1,107 @@
+Name:       ofono
+Summary:    Open Source Telephony
+Version:    1.4
+Release:    6
+Group:      Communications/Connectivity Adaptation
+License:    GPLv2
+URL:        http://ofono.org
+Source0:    %{name}-%{version}.tar.bz2
+Source1:    init_ofono
+Patch0:     0001-ifxmodem-Add-support-for-IPv6-and-dual-mode-contexts.patch
+Patch1:     0002-ifxmodem-Add-support-for-dynamic-DNS-for-IPv6-and-du.patch
+Patch2:     0003-ifx-Setup-CSCS-to-use-GSM-for-the-aux-port.patch
+Patch3:     0004-atmodem-Fix-handling-of-IFX-signal-strength-indicati.patch
+Patch4:     0005-call-volume.c-Register-the-call-volume-interface-jus.patch
+Patch5:     0006-gisi-Remove-includes-of-glib-gtypes.h.patch
+Patch6:     0007-isimodem-Remove-includes-of-glib-gtypes.h.patch
+
+Requires(post): /bin/ln
+Requires:   dbus
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(dbus-1)
+BuildRequires:  pkgconfig(libudev) >= 145
+BuildRequires:  pkgconfig(bluez) >= 4.85
+BuildRequires:  libtool
+BuildRequires:  automake
+BuildRequires:  autoconf
+BuildRequires:  pkgconfig(mobile-broadband-provider-info)
+
+
+%description
+Telephony stack
+
+
+%package devel
+Summary:    Headers for oFono
+Group:      Development/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description devel
+Development headers and libraries for oFono
+
+%package test
+Summary:    Test Scripts for oFono
+Group:      Development/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description test
+Scripts for testing oFono and its functionality
+
+
+%prep
+%setup -q -n %{name}-%{version}
+# 0001-ifxmodem-Add-support-for-IPv6-and-dual-mode-contexts.patch
+%patch0 -p1
+# 0002-ifxmodem-Add-support-for-dynamic-DNS-for-IPv6-and-du.patch
+%patch1 -p1
+# 0003-ifx-Setup-CSCS-to-use-GSM-for-the-aux-port.patch
+%patch2 -p1
+# 0004-atmodem-Fix-handling-of-IFX-signal-strength-indicati.patch
+%patch3 -p1
+# 0005-call-volume.c-Register-the-call-volume-interface-jus.patch
+%patch4 -p1
+# 0001-gisi-Remove-includes-of-glib-gtypes.h.patch
+%patch5 -p1
+# 0002-isimodem-Remove-includes-of-glib-gtypes.h.patch
+%patch6 -p1
+
+%build
+autoreconf --force --install
+
+%configure --disable-static \
+    --enable-test \
+    --with-systemdunitdir="/%{_lib}/systemd/system"
+
+make %{?jobs:-j%jobs}
+
+%install
+%make_install
+
+mkdir -p %{buildroot}/%{_lib}/systemd/system/network.target.wants
+ln -s ../ofono.service %{buildroot}/%{_lib}/systemd/system/network.target.wants/ofono.service
+
+mkdir -p %{buildroot}/etc/rc.d/init.d
+cp %{SOURCE1} %{buildroot}/etc/rc.d/init.d/ofono
+chmod +x %{buildroot}/etc/rc.d/init.d/ofono
+
+%post
+ln -sf ../init.d/ofono /etc/rc.d/rc3.d/S61ofono
+ln -sf ../init.d/ofono /etc/rc.d/rc5.d/S61ofono
+
+%docs_package
+
+%files
+%config(noreplace) %{_sysconfdir}/dbus-1/system.d/*.conf
+%{_sbindir}/*
+/%{_lib}/systemd/system/network.target.wants/ofono.service
+/%{_lib}/systemd/system/ofono.service
+/etc/ofono/phonesim.conf
+/etc/rc.d/init.d/*
+
+%files devel
+%{_includedir}/ofono/*.h
+%{_libdir}/pkgconfig/ofono.pc
+
+%files test
+%{_libdir}/%{name}/test/*
+
diff --git a/plugins/alcatel.c b/plugins/alcatel.c
new file mode 100644 (file)
index 0000000..fd0e4ec
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/phonebook.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+struct alcatel_data {
+       GAtChat *modem;
+       GAtChat *aux;
+       gboolean have_sim;
+       struct at_util_sim_state_query *sim_state_query;
+};
+
+static int alcatel_probe(struct ofono_modem *modem)
+{
+       struct alcatel_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct alcatel_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void alcatel_remove(struct ofono_modem *modem)
+{
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup potential SIM state polling */
+       at_util_sim_state_query_free(data->sim_state_query);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->aux);
+
+       g_free(data);
+}
+
+static void alcatel_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, alcatel_debug, debug);
+
+       return chat;
+}
+
+static void sim_state_cb(gboolean present, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       at_util_sim_state_query_free(data->sim_state_query);
+       data->sim_state_query = NULL;
+
+       data->have_sim = present;
+
+       ofono_modem_set_powered(modem, TRUE);
+
+       /* AT&C0 needs to be send separate and on both channel */
+       g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "AT&C0", NULL, NULL, NULL, NULL);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       data->sim_state_query = at_util_sim_state_query_new(data->aux,
+                                               2, 20, sim_state_cb, modem);
+}
+
+static int alcatel_enable(struct ofono_modem *modem)
+{
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               return -EINVAL;
+
+       data->aux = open_device(modem, "Aux", "Aux: ");
+       if (data->aux == NULL) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+               return -EIO;
+       }
+
+       g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "AT+CFUN=1", NULL,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int alcatel_disable(struct ofono_modem *modem)
+{
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       g_at_chat_send(data->aux, "AT+CFUN=0", NULL,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void alcatel_pre_sim(struct ofono_modem *modem)
+{
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->aux);
+       sim = ofono_sim_create(modem, 0, "atmodem", data->aux);
+
+       if (sim && data->have_sim == TRUE)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void alcatel_post_sim(struct ofono_modem *modem)
+{
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->aux);
+}
+
+static void alcatel_post_online(struct ofono_modem *modem)
+{
+       struct alcatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, 0, "atmodem", data->aux);
+}
+
+static struct ofono_modem_driver alcatel_driver = {
+       .name           = "alcatel",
+       .probe          = alcatel_probe,
+       .remove         = alcatel_remove,
+       .enable         = alcatel_enable,
+       .disable        = alcatel_disable,
+       .pre_sim        = alcatel_pre_sim,
+       .post_sim       = alcatel_post_sim,
+       .post_online    = alcatel_post_online,
+};
+
+static int alcatel_init(void)
+{
+       return ofono_modem_driver_register(&alcatel_driver);
+}
+
+static void alcatel_exit(void)
+{
+       ofono_modem_driver_unregister(&alcatel_driver);
+}
+
+OFONO_PLUGIN_DEFINE(alcatel, "Alcatel modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, alcatel_init, alcatel_exit)
diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c
new file mode 100644 (file)
index 0000000..dbf79eb
--- /dev/null
@@ -0,0 +1,986 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ProFUSION embedded systems
+ *  Copyright (C) 2010  Gustavo F. Padovan <gustavo@padovan.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+
+#include <btio.h>
+#include "bluetooth.h"
+
+static DBusConnection *connection;
+static GHashTable *uuid_hash = NULL;
+static GHashTable *adapter_address_hash = NULL;
+static gint bluetooth_refcount;
+static GSList *server_list = NULL;
+static const char *adapter_any_name = "any";
+static char *adapter_any_path;
+
+#define TIMEOUT 60 /* Timeout for user response (seconds) */
+
+struct server {
+       guint8 channel;
+       char *sdp_record;
+       guint32 handle;
+       GIOChannel *io;
+       ConnectFunc connect_cb;
+       gpointer user_data;
+};
+
+struct cb_data {
+       struct server *server;
+       char *path;
+       guint source;
+       GIOChannel *io;
+};
+
+void bluetooth_create_path(const char *dev_addr, const char *adapter_addr,
+                               char *buf, int size)
+{
+       int i, j;
+
+       for (i = 0, j = 0; adapter_addr[j] && i < size - 1; j++)
+               if (adapter_addr[j] >= '0' && adapter_addr[j] <= '9')
+                       buf[i++] = adapter_addr[j];
+               else if (adapter_addr[j] >= 'A' && adapter_addr[j] <= 'F')
+                       buf[i++] = adapter_addr[j];
+
+       if (i < size - 1)
+               buf[i++] = '_';
+
+       for (j = 0; dev_addr[j] && i < size - 1; j++)
+               if (dev_addr[j] >= '0' && dev_addr[j] <= '9')
+                       buf[i++] = dev_addr[j];
+               else if (dev_addr[j] >= 'A' && dev_addr[j] <= 'F')
+                       buf[i++] = dev_addr[j];
+
+       buf[i] = '\0';
+}
+
+int bluetooth_send_with_reply(const char *path, const char *interface,
+                               const char *method, DBusPendingCall **call,
+                               DBusPendingCallNotifyFunction cb,
+                               void *user_data, DBusFreeFunction free_func,
+                               int timeout, int type, ...)
+{
+       DBusMessage *msg;
+       DBusPendingCall *c;
+       va_list args;
+       int err;
+
+       msg = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                               interface, method);
+       if (msg == NULL) {
+               ofono_error("Unable to allocate new D-Bus %s message", method);
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       va_start(args, type);
+
+       if (!dbus_message_append_args_valist(msg, type, args)) {
+               va_end(args);
+               err = -EIO;
+               goto fail;
+       }
+
+       va_end(args);
+
+       if (timeout > 0)
+               timeout *= 1000;
+
+       if (!dbus_connection_send_with_reply(connection, msg, &c, timeout)) {
+               ofono_error("Sending %s failed", method);
+               err = -EIO;
+               goto fail;
+       }
+
+       if (call != NULL)
+               *call = c;
+
+       dbus_pending_call_set_notify(c, cb, user_data, free_func);
+       dbus_pending_call_unref(c);
+
+       dbus_message_unref(msg);
+
+       return 0;
+
+fail:
+       if (free_func && user_data)
+               free_func(user_data);
+
+       if (msg)
+               dbus_message_unref(msg);
+
+       return err;
+}
+
+typedef void (*PropertyHandler)(DBusMessageIter *iter, gpointer user_data);
+
+struct property_handler {
+       const char *property;
+       PropertyHandler callback;
+       gpointer user_data;
+};
+
+static gint property_handler_compare(gconstpointer a, gconstpointer b)
+{
+       const struct property_handler *handler = a;
+       const char *property = b;
+
+       return strcmp(handler->property, property);
+}
+
+void bluetooth_parse_properties(DBusMessage *reply, const char *property, ...)
+{
+       va_list args;
+       GSList *prop_handlers = NULL;
+       DBusMessageIter array, dict;
+
+       va_start(args, property);
+
+       while (property != NULL) {
+               struct property_handler *handler =
+                                       g_new0(struct property_handler, 1);
+
+               handler->property = property;
+               handler->callback = va_arg(args, PropertyHandler);
+               handler->user_data = va_arg(args, gpointer);
+
+               property = va_arg(args, const char *);
+
+               prop_handlers = g_slist_prepend(prop_handlers, handler);
+       }
+
+       va_end(args);
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+               GSList *l;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       goto done;
+
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       goto done;
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               l = g_slist_find_custom(prop_handlers, key,
+                                       property_handler_compare);
+
+               if (l) {
+                       struct property_handler *handler = l->data;
+
+                       handler->callback(&value, handler->user_data);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       g_slist_foreach(prop_handlers, (GFunc) g_free, NULL);
+       g_slist_free(prop_handlers);
+}
+
+static void parse_uuids(DBusMessageIter *array, gpointer user_data)
+{
+       GSList **uuids = user_data;
+       DBusMessageIter value;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
+               const char *uuid;
+
+               dbus_message_iter_get_basic(&value, &uuid);
+
+               *uuids = g_slist_prepend(*uuids, (char *) uuid);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static void parse_string(DBusMessageIter *iter, gpointer user_data)
+{
+       char **str = user_data;
+       int arg_type = dbus_message_iter_get_arg_type(iter);
+
+       if (arg_type != DBUS_TYPE_OBJECT_PATH && arg_type != DBUS_TYPE_STRING)
+               return;
+
+       dbus_message_iter_get_basic(iter, str);
+}
+
+static void bluetooth_probe(GSList *uuids, const char *path,
+                               const char *device, const char *adapter,
+                               const char *alias)
+{
+       for (; uuids; uuids = uuids->next) {
+               struct bluetooth_profile *driver;
+               const char *uuid = uuids->data;
+               int err;
+
+               driver = g_hash_table_lookup(uuid_hash, uuid);
+               if (driver == NULL)
+                       continue;
+
+               err = driver->probe(path, device, adapter, alias);
+               if (err == 0)
+                       continue;
+
+               ofono_error("%s probe: %s (%d)", driver->name, strerror(-err),
+                                                                       -err);
+       }
+}
+
+static void device_properties_cb(DBusPendingCall *call, gpointer user_data)
+{
+       DBusMessage *reply;
+       const char *path = user_data;
+       const char *adapter = NULL;
+       const char *adapter_addr = NULL;
+       const char *device_addr = NULL;
+       const char *alias = NULL;
+       struct DBusError derr;
+       GSList *uuids = NULL;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               ofono_error("Device.GetProperties replied an error: %s, %s",
+                                       derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       DBG("");
+
+       bluetooth_parse_properties(reply, "UUIDs", parse_uuids, &uuids,
+                               "Adapter", parse_string, &adapter,
+                               "Address", parse_string, &device_addr,
+                               "Alias", parse_string, &alias, NULL);
+
+       if (adapter)
+               adapter_addr = g_hash_table_lookup(adapter_address_hash,
+                                                       adapter);
+
+       if (!device_addr || !adapter_addr)
+               goto done;
+
+       bluetooth_probe(uuids, path, device_addr, adapter_addr, alias);
+
+done:
+       g_slist_free(uuids);
+       dbus_message_unref(reply);
+}
+
+static void parse_devices(DBusMessageIter *array, gpointer user_data)
+{
+       DBusMessageIter value;
+       GSList **device_list = user_data;
+
+       DBG("");
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value)
+                       == DBUS_TYPE_OBJECT_PATH) {
+               const char *path;
+
+               dbus_message_iter_get_basic(&value, &path);
+
+               *device_list = g_slist_prepend(*device_list, (gpointer) path);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static gboolean property_changed(DBusConnection *connection, DBusMessage *msg,
+                               void *user_data)
+{
+       const char *property;
+       DBusMessageIter iter;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return FALSE;
+
+       dbus_message_iter_get_basic(&iter, &property);
+       if (g_str_equal(property, "UUIDs") == TRUE) {
+               GSList *uuids = NULL;
+               const char *path = dbus_message_get_path(msg);
+               DBusMessageIter variant;
+
+               if (!dbus_message_iter_next(&iter))
+                       return FALSE;
+
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+                       return FALSE;
+
+               dbus_message_iter_recurse(&iter, &variant);
+
+               parse_uuids(&variant, &uuids);
+
+               /* We need the full set of properties to be able to create
+                * the modem properly, including Adapter and Alias, so
+                * refetch everything again
+                */
+               if (uuids)
+                       bluetooth_send_with_reply(path, BLUEZ_DEVICE_INTERFACE,
+                                       "GetProperties", NULL,
+                                       device_properties_cb, g_strdup(path),
+                                       g_free, -1, DBUS_TYPE_INVALID);
+       } else if (g_str_equal(property, "Alias") == TRUE) {
+               const char *path = dbus_message_get_path(msg);
+               struct bluetooth_profile *profile;
+               const char *alias = NULL;
+               DBusMessageIter variant;
+               GHashTableIter hash_iter;
+               gpointer key, value;
+
+               if (!dbus_message_iter_next(&iter))
+                       return FALSE;
+
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+                       return FALSE;
+
+               dbus_message_iter_recurse(&iter, &variant);
+
+               parse_string(&variant, &alias);
+
+               g_hash_table_iter_init(&hash_iter, uuid_hash);
+               while (g_hash_table_iter_next(&hash_iter, &key, &value)) {
+                       profile = value;
+                       if (profile->set_alias)
+                               profile->set_alias(path, alias);
+               }
+       }
+
+       return TRUE;
+}
+
+static void adapter_properties_cb(DBusPendingCall *call, gpointer user_data)
+{
+       const char *path = user_data;
+       DBusMessage *reply;
+       DBusError derr;
+       GSList *device_list = NULL;
+       GSList *l;
+       const char *addr;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               ofono_error("Adapter.GetProperties replied an error: %s, %s",
+                                       derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       DBG("");
+
+       bluetooth_parse_properties(reply,
+                                       "Devices", parse_devices, &device_list,
+                                       "Address", parse_string, &addr,
+                                       NULL);
+
+       DBG("Adapter Address: %s, Path: %s", addr, path);
+       g_hash_table_insert(adapter_address_hash,
+                               g_strdup(path), g_strdup(addr));
+
+       for (l = device_list; l; l = l->next) {
+               const char *device = l->data;
+
+               bluetooth_send_with_reply(device, BLUEZ_DEVICE_INTERFACE,
+                                       "GetProperties", NULL,
+                                       device_properties_cb, g_strdup(device),
+                                       g_free, -1, DBUS_TYPE_INVALID);
+       }
+
+done:
+       g_slist_free(device_list);
+       dbus_message_unref(reply);
+}
+
+static void get_adapter_properties(const char *path, const char *handle,
+                                               gpointer user_data)
+{
+       bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE,
+                       "GetProperties", NULL, adapter_properties_cb,
+                       g_strdup(path), g_free, -1, DBUS_TYPE_INVALID);
+}
+
+static void remove_record(struct server *server)
+{
+       DBusMessage *msg;
+
+       if (server->handle == 0)
+               return;
+
+       msg = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_any_path,
+                                       BLUEZ_SERVICE_INTERFACE,
+                                       "RemoveRecord");
+       if (msg == NULL) {
+               ofono_error("Unable to allocate D-Bus RemoveRecord message");
+               return;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_UINT32, &server->handle,
+                                       DBUS_TYPE_INVALID);
+       g_dbus_send_message(connection, msg);
+
+       ofono_info("Unregistered handle for channel %d: 0x%x",
+                       server->channel, server->handle);
+}
+
+static void cb_data_destroy(gpointer data)
+{
+       struct cb_data *cb_data = data;
+
+       if (cb_data->source != 0)
+               g_source_remove(cb_data->source);
+
+       g_free(cb_data->path);
+       g_free(cb_data);
+}
+
+static void cancel_authorization(struct cb_data *user_data)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(BLUEZ_SERVICE, user_data->path,
+                                               BLUEZ_SERVICE_INTERFACE,
+                                               "CancelAuthorization");
+
+       if (msg == NULL) {
+               ofono_error("Unable to allocate D-Bus CancelAuthorization"
+                               " message");
+               return;
+       }
+
+       g_dbus_send_message(connection, msg);
+}
+
+static gboolean client_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct cb_data *cb_data = data;
+
+       cancel_authorization(cb_data);
+       cb_data->source = 0;
+
+       return FALSE;
+}
+
+static void auth_cb(DBusPendingCall *call, gpointer user_data)
+{
+       struct cb_data *cb_data = user_data;
+       struct server *server = cb_data->server;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError derr;
+       GError *err = NULL;
+
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               ofono_error("RequestAuthorization error: %s, %s",
+                               derr.name, derr.message);
+
+               if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY))
+                       cancel_authorization(cb_data);
+
+               dbus_error_free(&derr);
+       } else {
+               ofono_info("RequestAuthorization succeeded");
+
+               if (!bt_io_accept(cb_data->io, server->connect_cb,
+                                       server->user_data, NULL, &err)) {
+                       ofono_error("%s", err->message);
+                       g_error_free(err);
+               }
+       }
+
+       dbus_message_unref(reply);
+}
+
+static void new_connection(GIOChannel *io, gpointer user_data)
+{
+       struct server *server = user_data;
+       struct cb_data *cbd;
+       const char *addr;
+       GError *err = NULL;
+       char laddress[18], raddress[18];
+       guint8 channel;
+       GHashTableIter iter;
+       gpointer key, value;
+       const char *path;
+
+       bt_io_get(io, BT_IO_RFCOMM, &err, BT_IO_OPT_SOURCE, laddress,
+                                       BT_IO_OPT_DEST, raddress,
+                                       BT_IO_OPT_CHANNEL, &channel,
+                                       BT_IO_OPT_INVALID);
+       if (err) {
+               ofono_error("%s", err->message);
+               g_error_free(err);
+               return;
+       }
+
+       ofono_info("New connection for %s on channel %u from: %s,", laddress,
+                                                       channel, raddress);
+
+       path = NULL;
+       g_hash_table_iter_init(&iter, adapter_address_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               if (g_str_equal(laddress, value) == TRUE) {
+                       path = key;
+                       break;
+               }
+       }
+
+       if (path == NULL)
+               return;
+
+       cbd = g_try_new0(struct cb_data, 1);
+       if (cbd == NULL) {
+               ofono_error("Unable to allocate client cb_data structure");
+               return;
+       }
+
+       cbd->path = g_strdup(path);
+       cbd->server = server;
+       cbd->io = io;
+
+       addr = raddress;
+
+       if (bluetooth_send_with_reply(path, BLUEZ_SERVICE_INTERFACE,
+                                       "RequestAuthorization", NULL,
+                                       auth_cb, cbd, cb_data_destroy,
+                                       TIMEOUT, DBUS_TYPE_STRING, &addr,
+                                       DBUS_TYPE_UINT32, &server->handle,
+                                       DBUS_TYPE_INVALID) < 0) {
+               ofono_error("Request Bluetooth authorization failed");
+               return;
+       }
+
+       ofono_info("RequestAuthorization(%s, 0x%x)", raddress, server->handle);
+
+       cbd->source = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                       client_event, cbd);
+}
+
+static void remove_service_handle(gpointer data, gpointer user_data)
+{
+       struct server *server = data;
+
+       server->handle = 0;
+}
+
+static void add_record_cb(DBusPendingCall *call, gpointer user_data)
+{
+       struct server *server = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError derr;
+
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               ofono_error("Replied with an error: %s, %s",
+                                       derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       dbus_message_get_args(reply, NULL, DBUS_TYPE_UINT32, &server->handle,
+                                       DBUS_TYPE_INVALID);
+
+       ofono_info("Registered handle for channel %d: 0x%x",
+                       server->channel, server->handle);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void add_record(gpointer data, gpointer user_data)
+{
+       struct server *server = data;
+
+       if (server->sdp_record == NULL)
+               return;
+
+       bluetooth_send_with_reply(adapter_any_path,
+                                       BLUEZ_SERVICE_INTERFACE, "AddRecord",
+                                       NULL, add_record_cb, server, NULL, -1,
+                                       DBUS_TYPE_STRING, &server->sdp_record,
+                                       DBUS_TYPE_INVALID);
+}
+
+static void find_adapter_cb(DBusPendingCall *call, gpointer user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError derr;
+       const char *path;
+
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               ofono_error("Replied with an error: %s, %s",
+                                       derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID);
+
+       adapter_any_path = g_strdup(path);
+
+       g_slist_foreach(server_list, (GFunc) add_record, NULL);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static gboolean adapter_added(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+
+       bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE,
+                       "GetProperties", NULL, adapter_properties_cb,
+                       g_strdup(path), g_free, -1, DBUS_TYPE_INVALID);
+
+       return TRUE;
+}
+
+static void bluetooth_remove(gpointer key, gpointer value, gpointer user_data)
+{
+       struct bluetooth_profile *profile = value;
+
+       profile->remove(user_data);
+}
+
+static gboolean adapter_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path;
+
+       if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID) == FALSE)
+               return FALSE;
+
+       g_hash_table_foreach(uuid_hash, bluetooth_remove, (gpointer) path);
+       g_hash_table_remove(adapter_address_hash, path);
+
+       return TRUE;
+}
+
+static gboolean device_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path;
+
+       if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID) == FALSE)
+               return FALSE;
+
+       g_hash_table_foreach(uuid_hash, bluetooth_remove, (gpointer) path);
+
+       return TRUE;
+}
+
+static void parse_adapters(DBusMessageIter *array, gpointer user_data)
+{
+       DBusMessageIter value;
+
+       DBG("");
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value)
+                       == DBUS_TYPE_OBJECT_PATH) {
+               const char *path;
+
+               dbus_message_iter_get_basic(&value, &path);
+
+               DBG("Calling GetProperties on %s", path);
+
+               bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE,
+                               "GetProperties", NULL, adapter_properties_cb,
+                               g_strdup(path), g_free, -1, DBUS_TYPE_INVALID);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static void manager_properties_cb(DBusPendingCall *call, gpointer user_data)
+{
+       DBusMessage *reply;
+       DBusError derr;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&derr);
+
+       if (dbus_set_error_from_message(&derr, reply)) {
+               ofono_error("Manager.GetProperties() replied an error: %s, %s",
+                                       derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       DBG("");
+
+       bluetooth_parse_properties(reply, "Adapters", parse_adapters, NULL,
+                                               NULL);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void bluetooth_connect(DBusConnection *connection, void *user_data)
+{
+       bluetooth_send_with_reply("/", BLUEZ_MANAGER_INTERFACE, "GetProperties",
+                               NULL, manager_properties_cb, NULL, NULL, -1,
+                               DBUS_TYPE_INVALID);
+
+       bluetooth_send_with_reply("/", BLUEZ_MANAGER_INTERFACE, "FindAdapter",
+                               NULL, find_adapter_cb, NULL, NULL, -1,
+                               DBUS_TYPE_STRING, &adapter_any_name,
+                               DBUS_TYPE_INVALID);
+}
+
+static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
+{
+       if (uuid_hash == NULL)
+               return;
+
+       g_hash_table_foreach(uuid_hash, bluetooth_remove, NULL);
+
+       g_slist_foreach(server_list, (GFunc) remove_service_handle, NULL);
+}
+
+static guint bluetooth_watch;
+static guint adapter_added_watch;
+static guint adapter_removed_watch;
+static guint device_removed_watch;
+static guint property_watch;
+
+static void bluetooth_ref(void)
+{
+       if (bluetooth_refcount > 0)
+               goto increment;
+
+       connection = ofono_dbus_get_connection();
+
+       bluetooth_watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
+                                       bluetooth_connect,
+                                       bluetooth_disconnect, NULL, NULL);
+
+       adapter_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               BLUEZ_MANAGER_INTERFACE,
+                                               "AdapterAdded",
+                                               adapter_added, NULL, NULL);
+
+       adapter_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               BLUEZ_MANAGER_INTERFACE,
+                                               "AdapterRemoved",
+                                               adapter_removed, NULL, NULL);
+
+       device_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               BLUEZ_ADAPTER_INTERFACE,
+                                               "DeviceRemoved",
+                                               device_removed, NULL, NULL);
+
+       property_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               BLUEZ_DEVICE_INTERFACE,
+                                               "PropertyChanged",
+                                               property_changed, NULL, NULL);
+
+       if (bluetooth_watch == 0 || adapter_added_watch == 0 ||
+                       adapter_removed_watch == 0 || property_watch == 0) {
+               goto remove;
+       }
+
+       uuid_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, NULL);
+
+       adapter_address_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, g_free);
+
+increment:
+       g_atomic_int_inc(&bluetooth_refcount);
+
+       return;
+
+remove:
+       g_dbus_remove_watch(connection, bluetooth_watch);
+       g_dbus_remove_watch(connection, adapter_added_watch);
+       g_dbus_remove_watch(connection, adapter_removed_watch);
+       g_dbus_remove_watch(connection, property_watch);
+}
+
+static void bluetooth_unref(void)
+{
+       if (g_atomic_int_dec_and_test(&bluetooth_refcount) == FALSE)
+               return;
+
+       g_free(adapter_any_path);
+       adapter_any_path = NULL;
+
+       g_dbus_remove_watch(connection, bluetooth_watch);
+       g_dbus_remove_watch(connection, adapter_added_watch);
+       g_dbus_remove_watch(connection, adapter_removed_watch);
+       g_dbus_remove_watch(connection, property_watch);
+
+       g_hash_table_destroy(uuid_hash);
+       g_hash_table_destroy(adapter_address_hash);
+}
+
+void bluetooth_get_properties()
+{
+       g_hash_table_foreach(adapter_address_hash,
+                               (GHFunc) get_adapter_properties, NULL);
+}
+
+int bluetooth_register_uuid(const char *uuid, struct bluetooth_profile *profile)
+{
+       bluetooth_ref();
+
+       g_hash_table_insert(uuid_hash, g_strdup(uuid), profile);
+
+       g_hash_table_foreach(adapter_address_hash,
+                               (GHFunc) get_adapter_properties, NULL);
+
+       return 0;
+}
+
+void bluetooth_unregister_uuid(const char *uuid)
+{
+       g_hash_table_remove(uuid_hash, uuid);
+
+       bluetooth_unref();
+}
+
+struct server *bluetooth_register_server(guint8 channel, const char *sdp_record,
+                                       ConnectFunc cb, gpointer user_data)
+{
+       struct server *server;
+       GError *err = NULL;
+
+       server = g_try_new0(struct server, 1);
+       if (!server)
+               return NULL;
+
+       server->channel = channel;
+
+       server->io = bt_io_listen(BT_IO_RFCOMM, NULL, new_connection,
+                                       server, NULL, &err,
+                                       BT_IO_OPT_CHANNEL, server->channel,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_INVALID);
+       if (server->io == NULL) {
+               g_error_free(err);
+               g_free(server);
+               return NULL;
+       }
+
+       bluetooth_ref();
+
+       if (sdp_record != NULL)
+               server->sdp_record = g_strdup(sdp_record);
+
+       server->connect_cb = cb;
+       server->user_data = user_data;
+
+       server_list = g_slist_prepend(server_list, server);
+
+       if (adapter_any_path != NULL)
+               add_record(server, NULL);
+
+       return server;
+}
+
+void bluetooth_unregister_server(struct server *server)
+{
+       server_list = g_slist_remove(server_list, server);
+
+       remove_record(server);
+
+       if (server->io != NULL) {
+               g_io_channel_shutdown(server->io, TRUE, NULL);
+               g_io_channel_unref(server->io);
+               server->io = NULL;
+       }
+
+       g_free(server->sdp_record);
+       g_free(server);
+
+       bluetooth_unref();
+}
+
+OFONO_PLUGIN_DEFINE(bluetooth, "Bluetooth Utils Plugins", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, NULL, NULL)
diff --git a/plugins/bluetooth.h b/plugins/bluetooth.h
new file mode 100644 (file)
index 0000000..daa1873
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Gustavo F. Padovan <gustavo@padovan.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <ofono/modem.h>
+#include <ofono/dbus.h>
+
+#define        BLUEZ_SERVICE "org.bluez"
+#define        BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
+#define        BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
+#define        BLUEZ_DEVICE_INTERFACE          BLUEZ_SERVICE ".Device"
+#define        BLUEZ_SERVICE_INTERFACE         BLUEZ_SERVICE ".Service"
+
+#define DBUS_TIMEOUT 15
+
+#define DUN_GW_UUID    "00001103-0000-1000-8000-00805f9b34fb"
+#define HFP_AG_UUID    "0000111f-0000-1000-8000-00805f9b34fb"
+#define HFP_HS_UUID    "0000111e-0000-1000-8000-00805f9b34fb"
+#define SAP_UUID       "0000112d-0000-1000-8000-00805f9b34fb"
+
+struct bluetooth_profile {
+       const char *name;
+       int (*probe)(const char *device, const char *dev_addr,
+                       const char *adapter_addr, const char *alias);
+       void (*remove)(const char *prefix);
+       void (*set_alias)(const char *device, const char *);
+};
+
+struct bluetooth_sap_driver {
+       const char *name;
+       int (*enable) (struct ofono_modem *modem, struct ofono_modem *sap_modem,
+                                                               int bt_fd);
+       void (*pre_sim) (struct ofono_modem *modem);
+       void (*post_sim) (struct ofono_modem *modem);
+       void (*set_online) (struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data);
+       void (*post_online) (struct ofono_modem *modem);
+       int (*disable) (struct ofono_modem *modem);
+};
+
+struct server;
+
+typedef void (*ConnectFunc)(GIOChannel *io, GError *err, gpointer user_data);
+
+void bluetooth_get_properties();
+int bluetooth_register_uuid(const char *uuid,
+                               struct bluetooth_profile *profile);
+void bluetooth_unregister_uuid(const char *uuid);
+
+struct server *bluetooth_register_server(guint8 channel, const char *sdp_record,
+                                       ConnectFunc cb, gpointer user_data);
+void bluetooth_unregister_server(struct server *server);
+
+void bluetooth_create_path(const char *dev_addr, const char *adapter_addr,
+                                                       char *buf, int size);
+
+int bluetooth_send_with_reply(const char *path, const char *interface,
+                               const char *method, DBusPendingCall **call,
+                               DBusPendingCallNotifyFunction cb,
+                               void *user_data, DBusFreeFunction free_func,
+                               int timeout, int type, ...);
+void bluetooth_parse_properties(DBusMessage *reply, const char *property, ...);
+
+int bluetooth_sap_client_register(struct bluetooth_sap_driver *sap,
+                                       struct ofono_modem *modem);
+void bluetooth_sap_client_unregister(struct ofono_modem *modem);
diff --git a/plugins/caif.c b/plugins/caif.c
new file mode 100644 (file)
index 0000000..97b13be
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/log.h>
+
+static GSList *modem_list = NULL;
+
+static int caif_init(void)
+{
+#if 0
+       struct ofono_modem *modem;
+
+       modem = ofono_modem_create("caif", "ste");
+       if (modem == NULL)
+               return -EIO;
+
+       modem_list = g_slist_prepend(modem_list, modem);
+
+       ofono_modem_register(modem);
+#endif
+
+       return 0;
+}
+
+static void caif_exit(void)
+{
+       GSList *list;
+
+       for (list = modem_list; list; list = list->next) {
+               struct ofono_modem *modem = list->data;
+
+               ofono_modem_remove(modem);
+       }
+
+       g_slist_free(modem_list);
+       modem_list = NULL;
+}
+
+OFONO_PLUGIN_DEFINE(caif, "CAIF device detection", VERSION,
+                       OFONO_PLUGIN_PRIORITY_LOW, caif_init, caif_exit)
diff --git a/plugins/calypso.c b/plugins/calypso.c
new file mode 100644 (file)
index 0000000..8154899
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <termios.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+#include <gatmux.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-volume.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+#include <ofono/stk.h>
+
+#include <drivers/atmodem/vendor.h>
+
+#define CALYPSO_POWER_PATH "/sys/bus/platform/devices/gta02-pm-gsm.0/power_on"
+#define CALYPSO_RESET_PATH "/sys/bus/platform/devices/gta02-pm-gsm.0/reset"
+
+enum powercycle_state {
+       POWERCYCLE_STATE_POWER0 = 0,
+       POWERCYCLE_STATE_RESET0,
+       POWERCYCLE_STATE_POWER1,
+       POWERCYCLE_STATE_RESET1,
+       POWERCYCLE_STATE_FINISHED,
+};
+
+#define NUM_DLC 4
+
+#define VOICE_DLC   0
+#define NETREG_DLC  1
+#define SMS_DLC     2
+#define AUX_DLC     3
+#define SETUP_DLC   3
+
+static char *debug_prefixes[NUM_DLC] = { "Voice: ", "Net: ", "SMS: ", "Aux: " };
+
+struct calypso_data {
+       GAtMux *mux;
+       GAtChat *dlcs[NUM_DLC];
+       enum powercycle_state state;
+       gboolean phonebook_added;
+       gboolean sms_added;
+       gboolean have_sim;
+       struct ofono_sim *sim;
+};
+
+static const char *cpin_prefix[] = { "+CPIN:", NULL };
+static const char *none_prefix[] = { NULL };
+
+static void calypso_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int calypso_probe(struct ofono_modem *modem)
+{
+       const char *device;
+       struct calypso_data *data;
+
+       DBG("%p", modem);
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL)
+               return -EINVAL;
+
+       DBG("%s", device);
+
+       data = g_new0(struct calypso_data, 1);
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void calypso_remove(struct ofono_modem *modem)
+{
+       struct calypso_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_free(data);
+}
+
+static void cstat_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct calypso_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       const char *stat;
+       int enabled;
+
+       DBG("phonebook: %d, sms: %d", data->phonebook_added, data->sms_added);
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "%CSTAT:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &stat))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &enabled))
+               return;
+
+       DBG("stat: %s, enabled: %d", stat, enabled);
+
+       if (!g_strcmp0(stat, "PHB") && enabled == 1 && !data->phonebook_added) {
+               data->phonebook_added = TRUE;
+               ofono_phonebook_create(modem, 0, "atmodem",
+                                       data->dlcs[AUX_DLC]);
+       }
+
+       if (!g_strcmp0(stat, "SMS") && enabled == 1 && !data->sms_added) {
+               data->sms_added = TRUE;
+               ofono_sms_create(modem, 0, "atmodem", data->dlcs[SMS_DLC]);
+       }
+}
+
+static void simind_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct calypso_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+
+       if (data->sim == NULL)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "%SIMREM:"))
+               ofono_sim_inserted_notify(data->sim, FALSE);
+       else if (g_at_result_iter_next(&iter, "%SIMINS:"))
+               ofono_sim_inserted_notify(data->sim, TRUE);
+}
+
+static void setup_modem(struct ofono_modem *modem)
+{
+       struct calypso_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       /* Generate unsolicited notifications as soon as they're generated */
+       for (i = 0; i < NUM_DLC; i++) {
+               g_at_chat_send(data->dlcs[i], "ATE0", NULL, NULL, NULL, NULL);
+               g_at_chat_send(data->dlcs[i], "AT%CUNS=0",
+                               NULL, NULL, NULL, NULL);
+               g_at_chat_send(data->dlcs[i], "AT+CMEE=1",
+                               NULL, NULL, NULL, NULL);
+       }
+
+       /* CSTAT tells us when SMS & Phonebook are ready to be used */
+       g_at_chat_register(data->dlcs[SETUP_DLC], "%CSTAT:", cstat_notify,
+                               FALSE, modem, NULL);
+       g_at_chat_send(data->dlcs[SETUP_DLC], "AT%CSTAT=1", NULL,
+                               NULL, NULL, NULL);
+
+       /* audio side tone: set to minimum */
+       g_at_chat_send(data->dlcs[SETUP_DLC], "AT@ST=\"-26\"", NULL,
+                       NULL, NULL, NULL);
+
+       /* Disable deep sleep */
+       g_at_chat_send(data->dlcs[SETUP_DLC], "AT%SLEEP=2", NULL,
+                       NULL, NULL, NULL);
+
+       /* Enable SIM removed/inserted notifications */
+       g_at_chat_register(data->dlcs[SETUP_DLC], "%SIMREM:", simind_notify,
+                               FALSE, modem, NULL);
+       g_at_chat_register(data->dlcs[SETUP_DLC], "%SIMINS:", simind_notify,
+                               FALSE, modem, NULL);
+       g_at_chat_send(data->dlcs[SETUP_DLC], "AT%SIMIND=1", NULL,
+                               NULL, NULL, NULL);
+}
+
+static void simpin_check_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct calypso_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       /* Modem returns ERROR if there is no SIM in slot. */
+       data->have_sim = ok;
+
+       setup_modem(modem);
+
+       ofono_modem_set_powered(modem, TRUE);
+}
+
+static void init_simpin_check(struct ofono_modem *modem)
+{
+       struct calypso_data *data = ofono_modem_get_data(modem);
+
+       /*
+        * Check for SIM presence by seeing if AT+CPIN? succeeds.
+        * The SIM can not be practically inserted/removed without
+        * restarting the device so there's no need to check more
+        * than once.
+        */
+       g_at_chat_send(data->dlcs[SETUP_DLC], "AT+CPIN?", cpin_prefix,
+                       simpin_check_cb, modem, NULL);
+}
+
+static void mux_setup(GAtMux *mux, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct calypso_data *data = ofono_modem_get_data(modem);
+       GIOChannel *io;
+       GAtSyntax *syntax;
+       int i;
+
+       DBG("%p", mux);
+
+       if (mux == NULL) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       data->mux = mux;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_mux_set_debug(data->mux, calypso_debug, "MUX: ");
+
+       g_at_mux_start(mux);
+
+       for (i = 0; i < NUM_DLC; i++) {
+               io = g_at_mux_create_channel(mux);
+
+               syntax = g_at_syntax_new_gsm_permissive();
+               data->dlcs[i] = g_at_chat_new(io, syntax);
+               g_at_syntax_unref(syntax);
+               g_io_channel_unref(io);
+
+               if (getenv("OFONO_AT_DEBUG"))
+                       g_at_chat_set_debug(data->dlcs[i], calypso_debug,
+                                                       debug_prefixes[i]);
+
+               g_at_chat_set_wakeup_command(data->dlcs[i], "AT\r", 500, 5000);
+       }
+
+       init_simpin_check(modem);
+}
+
+static void modem_initialize(struct ofono_modem *modem)
+{
+       GAtSyntax *syntax;
+       GAtChat *chat;
+       const char *device;
+       GIOChannel *io;
+       GHashTable *options;
+
+       DBG("");
+
+       device = ofono_modem_get_string(modem, "Device");
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               goto error;
+
+       g_hash_table_insert(options, "Baud", "115200");
+       g_hash_table_insert(options, "Parity", "none");
+       g_hash_table_insert(options, "StopBits", "1");
+       g_hash_table_insert(options, "DataBits", "8");
+       g_hash_table_insert(options, "XonXoff", "on");
+       g_hash_table_insert(options, "Local", "on");
+       g_hash_table_insert(options, "RtsCts", "on");
+
+       io = g_at_tty_open(device, options);
+       g_hash_table_destroy(options);
+
+       if (io == NULL)
+               goto error;
+
+       /* Calypso is normally compliant to 27.007, except the vendor-specific
+        * notifications (like %CSTAT) are not prefixed by \r\n
+        */
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       if (chat == NULL)
+               goto error;
+
+       if (getenv("OFONO_AT_DEBUG") != NULL)
+               g_at_chat_set_debug(chat, calypso_debug, "Setup: ");
+
+       g_at_chat_set_wakeup_command(chat, "AT\r", 500, 5000);
+
+       g_at_chat_send(chat, "ATE0", NULL, NULL, NULL, NULL);
+
+       g_at_mux_setup_gsm0710(chat, mux_setup, modem, NULL);
+       g_at_chat_unref(chat);
+
+       return;
+
+error:
+       ofono_modem_set_powered(modem, FALSE);
+}
+
+static gboolean write_file(const char *file, gboolean on)
+{
+       int fd;
+       int r;
+
+       fd = open(file, O_WRONLY);
+
+       if (fd == -1)
+               return FALSE;
+
+       DBG("%s, %s", file, on ? "1" : "0");
+
+       if (on)
+               r = write(fd, "1\n", 2);
+       else
+               r = write(fd, "0\n", 2);
+
+       close(fd);
+
+       return r > 0 ? TRUE : FALSE;
+}
+
+static gboolean poweron_cycle(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct calypso_data *data = ofono_modem_get_data(modem);
+
+       switch (data->state) {
+       case POWERCYCLE_STATE_POWER0:
+               if (write_file(CALYPSO_RESET_PATH, FALSE)) {
+                       data->state = POWERCYCLE_STATE_RESET0;
+                       return TRUE;
+               }
+
+               break;
+
+       case POWERCYCLE_STATE_RESET0:
+               if (write_file(CALYPSO_POWER_PATH, TRUE)) {
+                       data->state = POWERCYCLE_STATE_POWER1;
+                       return TRUE;
+               }
+
+               break;
+
+       case POWERCYCLE_STATE_POWER1:
+               if (write_file(CALYPSO_RESET_PATH, TRUE)) {
+                       data->state = POWERCYCLE_STATE_RESET1;
+                       return TRUE;
+               }
+
+               break;
+
+       case POWERCYCLE_STATE_RESET1:
+               if (write_file(CALYPSO_RESET_PATH, FALSE)) {
+                       data->state = POWERCYCLE_STATE_FINISHED;
+                       return TRUE;
+               }
+
+               break;
+
+       case POWERCYCLE_STATE_FINISHED:
+               modem_initialize(modem);
+               return FALSE;
+
+       default:
+               break;
+       };
+
+       ofono_modem_set_powered(modem, FALSE);
+       return FALSE;
+}
+
+/* power up hardware */
+static int calypso_enable(struct ofono_modem *modem)
+{
+       struct calypso_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (write_file(CALYPSO_POWER_PATH, FALSE) == FALSE)
+               return -EINVAL;
+
+       data->state = POWERCYCLE_STATE_POWER0;
+       g_timeout_add_seconds(1, poweron_cycle, modem);
+
+       return -EINPROGRESS;
+}
+
+static int calypso_disable(struct ofono_modem *modem)
+{
+       struct calypso_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("%p", modem);
+
+       for (i = 0; i < NUM_DLC; i++) {
+               g_at_chat_unref(data->dlcs[i]);
+               data->dlcs[i] = NULL;
+       }
+
+       g_at_mux_shutdown(data->mux);
+       g_at_mux_unref(data->mux);
+       data->mux = NULL;
+
+       data->phonebook_added = FALSE;
+       data->sms_added = FALSE;
+
+       if (write_file(CALYPSO_POWER_PATH, FALSE))
+               return 0;
+
+       return -EINVAL;
+}
+
+static void calypso_pre_sim(struct ofono_modem *modem)
+{
+       struct calypso_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       data->sim = ofono_sim_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_voicecall_create(modem, 0, "calypsomodem", data->dlcs[VOICE_DLC]);
+
+       /*
+        * The STK atom is only useful after SIM has been initialised,
+        * so really it belongs in post_sim.  However, the order of the
+        * following three actions is adapted to work around different
+        * issues with the Calypso's firmware in its different versions
+        * (may have been fixed starting at some version, but this order
+        * should work with any version).
+        *
+        * To deal with PIN-enabled and PIN-disabled SIM cards, the order
+        * needs to be as follows:
+        *
+        * AT%SATC="..."
+        * ...
+        * AT+CFUN=1
+        * ...
+        * AT+CPIN="..."
+        *
+        * %SATC comes before the other two actions because it provides
+        * the Terminal Profile data to the modem, which will be used
+        * during the Profile Download either during +CFUN=1 (on
+        * unprotected cards) or +CPIN="..." (on protected cards).
+        * The STK atom needs to be present at this time because the
+        * card may start issuing proactive commands immediately after
+        * the Download.
+        *
+        * +CFUN=1 appears before PIN entry because switching from +CFUN
+        * mode 0 later, on the Calypso has side effects at least on some
+        * versions of the firmware:
+        *
+        * mode 0 -> 1 transition forces PIN re-authentication.
+        * mode 0 -> 4 doesn't work at all.
+        * mode 1 -> 4 and
+        * mode 4 -> 1 transitions work and have no side effects.
+        *
+        * So in order to switch to Offline mode at startup,
+        * AT+CFUN=1;+CFUN=4 would be needed.
+        *
+        * Additionally AT+CFUN=1 response is not checked: on PIN-enabled
+        * cards, it will in most situations return "+CME ERROR: SIM PIN
+        * required" (CME ERROR 11) even though the switch to mode 1
+        * succeeds.  It will not perform Profile Download on those cards
+        * though, until another +CPIN command.
+        */
+       if (data->have_sim && data->sim)
+               ofono_stk_create(modem, 0, "calypsomodem", data->dlcs[AUX_DLC]);
+
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=1",
+                       none_prefix, NULL, NULL, NULL);
+
+       if (data->have_sim && data->sim)
+               ofono_sim_inserted_notify(data->sim, TRUE);
+}
+
+static void calypso_post_sim(struct ofono_modem *modem)
+{
+       struct calypso_data *data = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+
+       DBG("%p", modem);
+
+       ofono_ussd_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_forwarding_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_settings_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_netreg_create(modem, OFONO_VENDOR_CALYPSO, "atmodem",
+                               data->dlcs[NETREG_DLC]);
+       ofono_call_meter_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_barring_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_volume_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver calypso_driver = {
+       .name           = "calypso",
+       .probe          = calypso_probe,
+       .remove         = calypso_remove,
+       .enable         = calypso_enable,
+       .disable        = calypso_disable,
+       .pre_sim        = calypso_pre_sim,
+       .post_sim       = calypso_post_sim,
+};
+
+static int calypso_init(void)
+{
+       return ofono_modem_driver_register(&calypso_driver);
+}
+
+static void calypso_exit(void)
+{
+       ofono_modem_driver_unregister(&calypso_driver);
+}
+
+OFONO_PLUGIN_DEFINE(calypso, "TI Calypso modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       calypso_init, calypso_exit)
diff --git a/plugins/cdma-provision.c b/plugins/cdma-provision.c
new file mode 100644 (file)
index 0000000..1ac7be7
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/types.h>
+#include <ofono/log.h>
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-provision.h>
+#include <ofono/cdma-provision.h>
+
+#include "mbpi.h"
+
+static int cdma_provision_get_provider_name(const char *sid, char **name)
+{
+       GError *error = NULL;
+
+       DBG("Search provider name for SID %s", sid);
+
+       *name = mbpi_lookup_cdma_provider_name(sid, &error);
+       if (*name == NULL) {
+               if (error != NULL) {
+                       ofono_error("%s", error->message);
+                       g_error_free(error);
+               }
+
+               return -ENOENT;
+       }
+
+       DBG("Found provider name: %s", *name);
+
+       return 0;
+}
+
+static struct ofono_cdma_provision_driver provision_driver = {
+       .name = "CDMA provisioning",
+       .get_provider_name = cdma_provision_get_provider_name
+};
+
+static int cdma_provision_init(void)
+{
+       return ofono_cdma_provision_driver_register(&provision_driver);
+}
+
+static void cdma_provision_exit(void)
+{
+       ofono_cdma_provision_driver_unregister(&provision_driver);
+}
+
+OFONO_PLUGIN_DEFINE(cdma_provision, "CDMA provisioning Plugin", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       cdma_provision_init, cdma_provision_exit)
diff --git a/plugins/connman.c b/plugins/connman.c
new file mode 100644 (file)
index 0000000..875dd2d
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <gdbus.h>
+#include <string.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/log.h>
+#include <ofono/dbus.h>
+#include <ofono/plugin.h>
+#include <ofono/private-network.h>
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define CONNMAN_SERVICE                        "net.connman"
+#define CONNMAN_PATH                   "/net/connman"
+
+#define CONNMAN_MANAGER_INTERFACE      CONNMAN_SERVICE ".Manager"
+#define CONNMAN_MANAGER_PATH           "/"
+
+static DBusConnection *connection;
+static GHashTable *requests;
+static unsigned int id;
+
+struct connman_req {
+       int uid;
+       DBusPendingCall *pending;
+       ofono_private_network_cb_t cb;
+       void *data;
+       gboolean redundant;
+       char *path;
+};
+
+static void send_release(const char *path)
+{
+       DBusMessage *message;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                               "ReleasePrivateNetwork");
+       if (message == NULL)
+               return;
+
+       dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID);
+       dbus_message_set_no_reply(message, TRUE);
+       dbus_connection_send(connection, message, NULL);
+       dbus_message_unref(message);
+}
+
+static void connman_release(int uid)
+{
+       struct connman_req *req;
+
+       DBG("");
+
+       req = g_hash_table_lookup(requests, &uid);
+       if (req == NULL)
+               return;
+
+       if (req->pending) {
+               /*
+               * We want to cancel the request but we have to wait
+               * the response of ConnMan. So we mark request as
+               * redundant until we get the response, then we remove
+               * it from hash table.
+               */
+               req->redundant = TRUE;
+               return;
+       }
+
+       send_release(req->path);
+       g_hash_table_remove(requests, &req->uid);
+}
+
+static gboolean parse_reply(DBusMessage *reply, const char **path,
+                               struct ofono_private_network_settings *pns)
+{
+       DBusMessageIter array, dict, entry;
+
+       if (!reply)
+               return FALSE;
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               return FALSE;
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               return FALSE;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_OBJECT_PATH)
+               return FALSE;
+
+       dbus_message_iter_get_basic(&array, path);
+
+       dbus_message_iter_next(&array);
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               return FALSE;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter iter;
+               const char *key;
+               int type;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &iter);
+
+               type = dbus_message_iter_get_arg_type(&iter);
+               if (type != DBUS_TYPE_STRING)
+                       break;
+
+               if (g_str_equal(key, "ServerIPv4") &&
+                               type == DBUS_TYPE_STRING)
+                       dbus_message_iter_get_basic(&iter, &pns->server_ip);
+               else if (g_str_equal(key, "PeerIPv4") &&
+                               type == DBUS_TYPE_STRING)
+                       dbus_message_iter_get_basic(&iter, &pns->peer_ip);
+               else if (g_str_equal(key, "PrimaryDNS") &&
+                               type == DBUS_TYPE_STRING)
+                       dbus_message_iter_get_basic(&iter, &pns->primary_dns);
+               else if (g_str_equal(key, "SecondaryDNS") &&
+                               type == DBUS_TYPE_STRING)
+                       dbus_message_iter_get_basic(&iter, &pns->secondary_dns);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       dbus_message_iter_next(&array);
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_UNIX_FD)
+               return FALSE;
+
+       dbus_message_iter_get_basic(&array, &pns->fd);
+
+       return TRUE;
+}
+
+static void request_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_req *req = user_data;
+       DBusMessage *reply;
+       const char *path = NULL;
+       struct ofono_private_network_settings pns;
+
+       DBG("");
+
+       req->pending = NULL;
+
+       memset(&pns, 0, sizeof(pns));
+       pns.fd = -1;
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               goto badreply;
+
+       if (parse_reply(reply, &path, &pns) == FALSE)
+               goto error;
+
+       DBG("fd: %d, path: %s", pns.fd, path);
+
+       if (req->redundant == TRUE)
+               goto redundant;
+
+       if (pns.server_ip == NULL || pns.peer_ip == NULL ||
+                       pns.primary_dns == NULL || pns.secondary_dns == NULL ||
+                       pns.fd < 0) {
+               ofono_error("Error while reading dictionary...\n");
+               goto error;
+       }
+
+       req->path = g_strdup(path);
+       req->cb(&pns, req->data);
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+       return;
+
+error:
+redundant:
+       if (pns.fd != -1)
+               close(pns.fd);
+
+       if (path != NULL)
+               send_release(path);
+
+       dbus_message_unref(reply);
+
+badreply:
+       if (req->redundant == FALSE)
+               req->cb(NULL, req->data);
+
+       g_hash_table_remove(requests, &req->uid);
+       dbus_pending_call_unref(call);
+}
+
+static int connman_request(ofono_private_network_cb_t cb, void *data)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+       struct connman_req *req;
+
+       DBG("");
+
+       if (DBUS_TYPE_UNIX_FD < 0)
+               return -EBADF;
+
+       req = g_try_new(struct connman_req, 1);
+       if (req == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                               "RequestPrivateNetwork");
+
+       if (message == NULL) {
+               g_free(req);
+               return -ENOMEM;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, 5000) == FALSE) {
+               g_free(req);
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       id++;
+       req->pending = call;
+       req->cb = cb;
+       req->data = data;
+       req->uid = id;
+       req->redundant = FALSE;
+       req->path = NULL;
+
+       dbus_pending_call_set_notify(call, request_reply, req, NULL);
+       g_hash_table_insert(requests, &req->uid, req);
+       dbus_message_unref(message);
+
+       return req->uid;
+}
+
+static struct ofono_private_network_driver pn_driver = {
+       .name           = "ConnMan Private Network",
+       .request        = connman_request,
+       .release        = connman_release,
+};
+
+static void request_free(gpointer user_data)
+{
+       struct connman_req *req = user_data;
+
+       g_free(req->path);
+       g_free(req);
+}
+
+static int connman_init(void)
+{
+       DBG("");
+
+       connection = ofono_dbus_get_connection();
+       requests = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
+                                               request_free);
+
+       return ofono_private_network_driver_register(&pn_driver);
+}
+
+static void connman_exit(void)
+{
+       g_hash_table_destroy(requests);
+       ofono_private_network_driver_unregister(&pn_driver);
+}
+
+OFONO_PLUGIN_DEFINE(connman, "ConnMan plugin", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, connman_init, connman_exit)
diff --git a/plugins/dun_gw.c b/plugins/dun_gw.c
new file mode 100644 (file)
index 0000000..75b62eb
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <gdbus.h>
+
+#include "bluetooth.h"
+
+#define DUN_GW_CHANNEL 1
+
+static struct server *server;
+static guint modemwatch_id;
+static GList *modems;
+
+static const gchar *dun_record =
+"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+"<record>\n"
+"  <attribute id=\"0x0001\">\n"
+"    <sequence>\n"
+"      <uuid value=\"0x1103\"/>\n"
+"    </sequence>\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0004\">\n"
+"    <sequence>\n"
+"      <sequence>\n"
+"        <uuid value=\"0x0100\"/>\n"
+"      </sequence>\n"
+"      <sequence>\n"
+"        <uuid value=\"0x0003\"/>\n"
+"        <uint8 value=\"1\" name=\"channel\"/>\n"
+"      </sequence>\n"
+"    </sequence>\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0009\">\n"
+"    <sequence>\n"
+"      <sequence>\n"
+"        <uuid value=\"0x1103\"/>\n"
+"        <uint16 value=\"0x0100\" name=\"version\"/>\n"
+"      </sequence>\n"
+"    </sequence>\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0100\">\n"
+"    <text value=\"Dial-up Networking\" name=\"name\"/>\n"
+"  </attribute>\n"
+"</record>\n";
+
+static void dun_gw_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       struct ofono_modem *modem;
+       int fd;
+
+       DBG("");
+
+       if (err) {
+               DBG("%s", err->message);
+               g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
+
+       /* Pick the first powered modem */
+       modem = modems->data;
+       DBG("Picked modem %p for emulator", modem);
+
+       em = ofono_emulator_create(modem, OFONO_EMULATOR_TYPE_DUN);
+       if (em == NULL) {
+               g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
+
+       fd = g_io_channel_unix_get_fd(io);
+       g_io_channel_set_close_on_unref(io, FALSE);
+
+       ofono_emulator_register(em, fd);
+}
+
+static void gprs_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct ofono_modem *modem = data;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) {
+               modems = g_list_append(modems, modem);
+
+               if (modems->next == NULL)
+                       server = bluetooth_register_server(DUN_GW_CHANNEL,
+                                       dun_record,
+                                       dun_gw_connect_cb,
+                                       NULL);
+       } else {
+               modems = g_list_remove(modems, modem);
+               if (modems == NULL && server != NULL) {
+                       bluetooth_unregister_server(server);
+                       server = NULL;
+               }
+       }
+}
+
+static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
+{
+       DBG("modem: %p, added: %d", modem, added);
+
+       if (added == FALSE)
+               return;
+
+       __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_GPRS,
+                                               gprs_watch, modem, NULL);
+}
+
+static void call_modemwatch(struct ofono_modem *modem, void *user)
+{
+       modem_watch(modem, TRUE, user);
+}
+
+static int dun_gw_init(void)
+{
+       DBG("");
+
+       modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
+
+       __ofono_modem_foreach(call_modemwatch, NULL);
+
+       return 0;
+}
+
+static void dun_gw_exit(void)
+{
+       __ofono_modemwatch_remove(modemwatch_id);
+       g_list_free(modems);
+
+       if (server) {
+               bluetooth_unregister_server(server);
+               server = NULL;
+       }
+}
+
+OFONO_PLUGIN_DEFINE(dun_gw, "Dial-up Networking Profile Plugins", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, dun_gw_init, dun_gw_exit)
diff --git a/plugins/g1.c b/plugins/g1.c
new file mode 100644 (file)
index 0000000..d915a56
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009  Collabora Ltd. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+
+#include <drivers/atmodem/vendor.h>
+
+static void g1_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+/* Detect hardware, and initialize if found */
+static int g1_probe(struct ofono_modem *modem)
+{
+       DBG("");
+
+       return 0;
+}
+
+static void g1_remove(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (chat) {
+               g_at_chat_unref(chat);
+               ofono_modem_set_data(modem, NULL);
+       }
+}
+
+static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+
+       DBG("");
+
+       if (ok)
+               ofono_modem_set_powered(modem, TRUE);
+}
+
+/* power up hardware */
+static int g1_enable(struct ofono_modem *modem)
+{
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GAtChat *chat;
+       const char *device;
+
+       DBG("");
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL)
+               return -EINVAL;
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return -EIO;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_io_channel_unref(channel);
+       g_at_syntax_unref(syntax);
+
+       if (chat == NULL)
+               return -EIO;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, g1_debug, "");
+
+       ofono_modem_set_data(modem, chat);
+
+       /* ensure modem is in a known state; verbose on, echo/quiet off */
+       g_at_chat_send(chat, "ATE0Q0V1", NULL, NULL, NULL, NULL);
+
+       /* power up modem */
+       g_at_chat_send(chat, "AT+CFUN=1", NULL, cfun_set_on_cb, modem, NULL);
+
+       return 0;
+}
+
+static void cfun_set_off_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       GAtChat *chat = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(chat);
+       ofono_modem_set_data(modem, NULL);
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int g1_disable(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       /* power down modem */
+       g_at_chat_cancel_all(chat);
+       g_at_chat_unregister_all(chat);
+       g_at_chat_send(chat, "AT+CFUN=0", NULL, cfun_set_off_cb, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void g1_pre_sim(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("");
+
+       ofono_devinfo_create(modem, 0, "atmodem", chat);
+       sim = ofono_sim_create(modem, 0, "atmodem", chat);
+       ofono_voicecall_create(modem, 0, "atmodem", chat);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void g1_post_sim(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+
+       DBG("");
+
+       ofono_ussd_create(modem, 0, "atmodem", chat);
+       ofono_call_forwarding_create(modem, 0, "atmodem", chat);
+       ofono_call_settings_create(modem, 0, "atmodem", chat);
+       ofono_netreg_create(modem, 0, "atmodem", chat);
+       ofono_call_meter_create(modem, 0, "atmodem", chat);
+       ofono_call_barring_create(modem, 0, "atmodem", chat);
+       ofono_sms_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", chat);
+       ofono_phonebook_create(modem, 0, "atmodem", chat);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver g1_driver = {
+       .name           = "g1",
+       .probe          = g1_probe,
+       .remove         = g1_remove,
+       .enable         = g1_enable,
+       .disable        = g1_disable,
+       .pre_sim        = g1_pre_sim,
+       .post_sim       = g1_post_sim,
+};
+
+static int g1_init(void)
+{
+       return ofono_modem_driver_register(&g1_driver);
+}
+
+static void g1_exit(void)
+{
+       ofono_modem_driver_unregister(&g1_driver);
+}
+
+OFONO_PLUGIN_DEFINE(g1, "HTC G1 modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, g1_init, g1_exit)
diff --git a/plugins/gobi.c b/plugins/gobi.c
new file mode 100644 (file)
index 0000000..d8c974b
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct gobi_data {
+       GAtChat *chat;
+       struct ofono_sim *sim;
+       gboolean have_sim;
+       guint cfun_timeout;
+};
+
+static void gobi_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int gobi_probe(struct ofono_modem *modem)
+{
+       struct gobi_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct gobi_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void gobi_remove(struct ofono_modem *modem)
+{
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->chat);
+
+       g_free(data);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, gobi_debug, debug);
+
+       return chat;
+}
+
+static void simstat_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct gobi_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       const char *state, *tmp;
+
+       if (data->sim == NULL)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "$QCSIMSTAT:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &tmp))
+               return;
+
+       /*
+        * When receiving an unsolicited notification, the comma
+        * is missing ($QCSIMSTAT: 1 SIM INIT COMPLETED). Handle
+        * this gracefully.
+        */
+       if (g_str_has_prefix(tmp, "1 ") == TRUE)
+               state = tmp + 2;
+       else if (!g_at_result_iter_next_unquoted_string(&iter, &state))
+               return;
+
+       DBG("state %s", state);
+
+       if (g_str_equal(state, "SIM INIT COMPLETED") == TRUE) {
+               if (data->have_sim == FALSE) {
+                       ofono_sim_inserted_notify(data->sim, TRUE);
+                       data->have_sim = TRUE;
+               }
+       }
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (data->cfun_timeout > 0) {
+               g_source_remove(data->cfun_timeout);
+               data->cfun_timeout = 0;
+       }
+
+       if (!ok) {
+               g_at_chat_unref(data->chat);
+               data->chat = NULL;
+
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       data->have_sim = FALSE;
+
+       g_at_chat_register(data->chat, "$QCSIMSTAT:", simstat_notify,
+                                               FALSE, modem, NULL);
+
+       g_at_chat_send(data->chat, "AT$QCSIMSTAT=1", none_prefix,
+                                               NULL, NULL, NULL);
+
+       g_at_chat_send(data->chat, "AT$QCSIMSTAT?", none_prefix,
+                                               NULL, NULL, NULL);
+
+       ofono_modem_set_powered(modem, TRUE);
+}
+
+static gboolean cfun_timeout(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       ofono_error("Modem enabling timeout, RFKILL enabled?");
+
+       data->cfun_timeout = 0;
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       return FALSE;
+}
+
+static int gobi_enable(struct ofono_modem *modem)
+{
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->chat = open_device(modem, "Device", "Device: ");
+       if (data->chat == NULL)
+               return -EINVAL;
+
+       g_at_chat_send(data->chat, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
+                                       cfun_enable, modem, NULL);
+
+       data->cfun_timeout = g_timeout_add_seconds(5, cfun_timeout, modem);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int gobi_disable(struct ofono_modem *modem)
+{
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->chat);
+       g_at_chat_unregister_all(data->chat);
+
+       g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void gobi_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct gobi_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(data->chat, command, none_prefix,
+                                       set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void gobi_pre_sim(struct ofono_modem *modem)
+{
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+       data->sim = ofono_sim_create(modem, 0, "atmodem", data->chat);
+}
+
+static void gobi_post_sim(struct ofono_modem *modem)
+{
+       struct gobi_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_GOBI,
+                                               "atmodem", data->chat);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+}
+
+static void gobi_post_online(struct ofono_modem *modem)
+{
+       struct gobi_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, 0, "dunmodem", data->chat);
+}
+
+static struct ofono_modem_driver gobi_driver = {
+       .name           = "gobi",
+       .probe          = gobi_probe,
+       .remove         = gobi_remove,
+       .enable         = gobi_enable,
+       .disable        = gobi_disable,
+       .set_online     = gobi_set_online,
+       .pre_sim        = gobi_pre_sim,
+       .post_sim       = gobi_post_sim,
+       .post_online    = gobi_post_online,
+};
+
+static int gobi_init(void)
+{
+       return ofono_modem_driver_register(&gobi_driver);
+}
+
+static void gobi_exit(void)
+{
+       ofono_modem_driver_unregister(&gobi_driver);
+}
+
+OFONO_PLUGIN_DEFINE(gobi, "Qualcomm Gobi modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, gobi_init, gobi_exit)
diff --git a/plugins/hfp_ag.c b/plugins/hfp_ag.c
new file mode 100644 (file)
index 0000000..c2d1d30
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <gdbus.h>
+
+#include "bluetooth.h"
+
+#define HFP_AG_CHANNEL 13
+
+static struct server *server;
+static guint modemwatch_id;
+static GList *modems;
+static GHashTable *sim_hash = NULL;
+
+static const gchar *hfp_ag_record =
+"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+"<record>\n"
+"  <attribute id=\"0x0001\">\n"
+"    <sequence>\n"
+"      <uuid value=\"0x111F\"/>\n"
+"      <uuid value=\"0x1203\"/>\n"
+"    </sequence>\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0004\">\n"
+"    <sequence>\n"
+"      <sequence>\n"
+"        <uuid value=\"0x0100\"/>\n"
+"      </sequence>\n"
+"      <sequence>\n"
+"        <uuid value=\"0x0003\"/>\n"
+"        <uint8 value=\"13\" name=\"channel\"/>\n"
+"      </sequence>\n"
+"    </sequence>\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0009\">\n"
+"    <sequence>\n"
+"      <sequence>\n"
+"        <uuid value=\"0x111E\"/>\n"
+"        <uint16 value=\"0x0105\" name=\"version\"/>\n"
+"      </sequence>\n"
+"    </sequence>\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0100\">\n"
+"    <text value=\"Hands-Free Audio Gateway\" name=\"name\"/>\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0301\">\n"
+"    <uint8 value=\"0x01\" />\n"
+"  </attribute>\n"
+"\n"
+"  <attribute id=\"0x0311\">\n"
+"    <uint16 value=\"0x0001\" />\n"
+"  </attribute>\n"
+"</record>\n";
+
+static void hfp_ag_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+       struct ofono_modem *modem;
+       struct ofono_emulator *em;
+       int fd;
+
+       DBG("");
+
+       if (err) {
+               DBG("%s", err->message);
+               return;
+       }
+
+       /* Pick the first voicecall capable modem */
+       modem = modems->data;
+       if (modem == NULL)
+               return;
+
+       DBG("Picked modem %p for emulator", modem);
+
+       em = ofono_emulator_create(modem, OFONO_EMULATOR_TYPE_HFP);
+       if (em == NULL)
+               return;
+
+       fd = g_io_channel_unix_get_fd(io);
+       g_io_channel_set_close_on_unref(io, FALSE);
+
+       ofono_emulator_register(em, fd);
+}
+
+static void sim_state_watch(enum ofono_sim_state new_state, void *data)
+{
+       struct ofono_modem *modem = data;
+
+       if (new_state != OFONO_SIM_STATE_READY) {
+               modems = g_list_remove(modems, modem);
+               if (modems == NULL && server != NULL) {
+                       bluetooth_unregister_server(server);
+                       server = NULL;
+               }
+
+               return;
+       }
+
+       if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_VOICECALL) == NULL)
+               return;
+
+       modems = g_list_append(modems, modem);
+
+       if (modems->next != NULL)
+               return;
+
+       server = bluetooth_register_server(HFP_AG_CHANNEL, hfp_ag_record,
+                                               hfp_ag_connect_cb, NULL);
+}
+
+static gboolean sim_watch_remove(gpointer key, gpointer value,
+                               gpointer user_data)
+{
+       struct ofono_sim *sim = key;
+
+       ofono_sim_remove_state_watch(sim, GPOINTER_TO_UINT(value));
+
+       return TRUE;
+}
+
+static void sim_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct ofono_sim *sim = __ofono_atom_get_data(atom);
+       struct ofono_modem *modem = data;
+       int watch;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               sim_state_watch(OFONO_SIM_STATE_NOT_PRESENT, modem);
+
+               sim_watch_remove(sim, g_hash_table_lookup(sim_hash, sim), NULL);
+               g_hash_table_remove(sim_hash, sim);
+
+               return;
+       }
+
+       watch = ofono_sim_add_state_watch(sim, sim_state_watch, modem, NULL);
+       g_hash_table_insert(sim_hash, sim, GUINT_TO_POINTER(watch));
+       sim_state_watch(ofono_sim_get_state(sim), modem);
+}
+
+static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
+{
+       DBG("modem: %p, added: %d", modem, added);
+
+       if (added == FALSE)
+               return;
+
+       __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SIM,
+                                       sim_watch, modem, NULL);
+}
+
+static void call_modemwatch(struct ofono_modem *modem, void *user)
+{
+       modem_watch(modem, TRUE, user);
+}
+
+static int hfp_ag_init(void)
+{
+       sim_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+       modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
+       __ofono_modem_foreach(call_modemwatch, NULL);
+
+       return 0;
+}
+
+static void hfp_ag_exit(void)
+{
+       __ofono_modemwatch_remove(modemwatch_id);
+       g_list_free(modems);
+       g_hash_table_foreach_remove(sim_hash, sim_watch_remove, NULL);
+       g_hash_table_destroy(sim_hash);
+
+       if (server) {
+               bluetooth_unregister_server(server);
+               server = NULL;
+       }
+}
+
+OFONO_PLUGIN_DEFINE(hfp_ag, "Hands-Free Audio Gateway Profile Plugins", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, hfp_ag_init, hfp_ag_exit)
diff --git a/plugins/hfp_hf.c b/plugins/hfp_hf.c
new file mode 100644 (file)
index 0000000..48a734a
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ProFUSION embedded systems
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/voicecall.h>
+#include <ofono/call-volume.h>
+#include <ofono/handsfree.h>
+
+#include <drivers/hfpmodem/slc.h>
+
+#include "bluetooth.h"
+
+#define        BLUEZ_GATEWAY_INTERFACE         BLUEZ_SERVICE ".HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+#define HFP_AGENT_ERROR_INTERFACE "org.bluez.Error"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+static DBusConnection *connection;
+static GHashTable *modem_hash = NULL;
+
+struct hfp_data {
+       struct hfp_slc_info info;
+       char *handsfree_path;
+       char *handsfree_address;
+       DBusMessage *slc_msg;
+       gboolean agent_registered;
+       DBusPendingCall *call;
+};
+
+static void hfp_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static void slc_established(gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       DBusMessage *msg;
+
+       ofono_modem_set_powered(modem, TRUE);
+
+       msg = dbus_message_new_method_return(data->slc_msg);
+       g_dbus_send_message(connection, msg);
+       dbus_message_unref(data->slc_msg);
+       data->slc_msg = NULL;
+
+       ofono_info("Service level connection established");
+}
+
+static void slc_failed(gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       DBusMessage *msg;
+
+       msg = g_dbus_create_error(data->slc_msg, HFP_AGENT_ERROR_INTERFACE
+                                       ".Failed",
+                                       "HFP Handshake failed");
+       g_dbus_send_message(connection, msg);
+       dbus_message_unref(data->slc_msg);
+       data->slc_msg = NULL;
+
+       ofono_error("Service level connection failed");
+       ofono_modem_set_powered(modem, FALSE);
+
+       g_at_chat_unref(data->info.chat);
+       data->info.chat = NULL;
+}
+
+static void hfp_disconnected_cb(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hfp_data *data = ofono_modem_get_data(modem);
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       g_at_chat_unref(data->info.chat);
+       data->info.chat = NULL;
+}
+
+/* either oFono or Phone could request SLC connection */
+static int service_level_connection(struct ofono_modem *modem, int fd)
+{
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       GIOChannel *io;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       io = g_io_channel_unix_new(fd);
+       if (io == NULL) {
+               ofono_error("Service level connection failed: %s (%d)",
+                       strerror(errno), errno);
+               return -EIO;
+       }
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       if (chat == NULL)
+               return -ENOMEM;
+
+       g_at_chat_set_disconnect_function(chat, hfp_disconnected_cb, modem);
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, hfp_debug, "");
+
+       data->info.chat = chat;
+       hfp_slc_establish(&data->info, slc_established, slc_failed, modem);
+
+       return -EINPROGRESS;
+}
+
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       int fd, err;
+       struct ofono_modem *modem = data;
+       struct hfp_data *hfp_data = ofono_modem_get_data(modem);
+       guint16 version;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                               DBUS_TYPE_UINT16, &version, DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       hfp_slc_info_init(&hfp_data->info, version);
+
+       err = service_level_connection(modem, fd);
+       if (err < 0 && err != -EINPROGRESS)
+               return __ofono_error_failed(msg);
+
+       hfp_data->slc_msg = msg;
+       dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct hfp_data *hfp_data = ofono_modem_get_data(modem);
+       const char *obj_path = ofono_modem_get_path(modem);
+
+       g_dbus_unregister_interface(connection, obj_path, HFP_AGENT_INTERFACE);
+       hfp_data->agent_registered = FALSE;
+
+       ofono_modem_remove(modem);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable agent_methods[] = {
+       { "NewConnection", "hq", "", hfp_agent_new_connection,
+               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Release", "", "", hfp_agent_release },
+       { NULL, NULL, NULL, NULL }
+};
+
+static int hfp_hf_probe(const char *device, const char *dev_addr,
+                               const char *adapter_addr, const char *alias)
+{
+       struct ofono_modem *modem;
+       struct hfp_data *data;
+       char buf[256];
+
+       /* We already have this device in our hash, ignore */
+       if (g_hash_table_lookup(modem_hash, device) != NULL)
+               return -EALREADY;
+
+       ofono_info("Using device: %s, devaddr: %s, adapter: %s",
+                       device, dev_addr, adapter_addr);
+
+       strcpy(buf, "hfp/");
+       bluetooth_create_path(dev_addr, adapter_addr, buf + 4, sizeof(buf) - 4);
+
+       modem = ofono_modem_create(buf, "hfp");
+       if (modem == NULL)
+               return -ENOMEM;
+
+       data = g_try_new0(struct hfp_data, 1);
+       if (data == NULL)
+               goto free;
+
+       data->handsfree_path = g_strdup(device);
+       if (data->handsfree_path == NULL)
+               goto free;
+
+       data->handsfree_address = g_strdup(dev_addr);
+       if (data->handsfree_address == NULL)
+               goto free;
+
+       ofono_modem_set_data(modem, data);
+       ofono_modem_set_name(modem, alias);
+       ofono_modem_register(modem);
+
+       g_hash_table_insert(modem_hash, g_strdup(device), modem);
+
+       return 0;
+
+free:
+       if (data != NULL)
+               g_free(data->handsfree_path);
+
+       g_free(data);
+       ofono_modem_remove(modem);
+
+       return -ENOMEM;
+}
+
+static gboolean hfp_remove_modem(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct ofono_modem *modem = value;
+       const char *device = key;
+       const char *prefix = user_data;
+
+       if (prefix && g_str_has_prefix(device, prefix) == FALSE)
+               return FALSE;
+
+       ofono_modem_remove(modem);
+
+       return TRUE;
+}
+
+static void hfp_hf_remove(const char *prefix)
+{
+       DBG("%s", prefix);
+
+       if (modem_hash == NULL)
+               return;
+
+       g_hash_table_foreach_remove(modem_hash, hfp_remove_modem,
+                                                       (gpointer) prefix);
+}
+
+static void hfp_hf_set_alias(const char *device, const char *alias)
+{
+       struct ofono_modem *modem;
+
+       if (device == NULL || alias == NULL)
+               return;
+
+       modem = g_hash_table_lookup(modem_hash, device);
+       if (modem == NULL)
+               return;
+
+       ofono_modem_set_name(modem, alias);
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+       const char *obj_path = ofono_modem_get_path(modem);
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       DBusMessage *msg;
+
+       DBG("Registering oFono Agent to bluetooth daemon");
+
+       msg = dbus_message_new_method_call(BLUEZ_SERVICE, data->handsfree_path,
+                               BLUEZ_GATEWAY_INTERFACE, "RegisterAgent");
+       if (msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &obj_path,
+                               DBUS_TYPE_INVALID);
+
+       g_dbus_send_message(connection, msg);
+       return 0;
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+       const char *obj_path = ofono_modem_get_path(modem);
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       DBusMessage *msg;
+
+       DBG("Unregistering oFono Agent from bluetooth daemon");
+
+       msg = dbus_message_new_method_call(BLUEZ_SERVICE, data->handsfree_path,
+                               BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent");
+       if (msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &obj_path,
+                               DBUS_TYPE_INVALID);
+
+       g_dbus_send_message(connection, msg);
+       return 0;
+}
+
+static int hfp_probe(struct ofono_modem *modem)
+{
+       const char *obj_path = ofono_modem_get_path(modem);
+       struct hfp_data *data = ofono_modem_get_data(modem);
+
+       if (data == NULL)
+               return -EINVAL;
+
+       g_dbus_register_interface(connection, obj_path, HFP_AGENT_INTERFACE,
+                       agent_methods, NULL, NULL, modem, NULL);
+
+       data->agent_registered = TRUE;
+
+       if (hfp_register_ofono_handsfree(modem) != 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void hfp_remove(struct ofono_modem *modem)
+{
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       const char *obj_path = ofono_modem_get_path(modem);
+
+       if (data->call != NULL)
+               dbus_pending_call_cancel(data->call);
+
+       if (g_dbus_unregister_interface(connection, obj_path,
+                                       HFP_AGENT_INTERFACE))
+               hfp_unregister_ofono_handsfree(modem);
+
+       g_hash_table_remove(modem_hash, data->handsfree_path);
+
+       g_free(data->handsfree_address);
+       g_free(data->handsfree_path);
+       g_free(data);
+
+       ofono_modem_set_data(modem, NULL);
+}
+
+static void hfp_connect_reply(DBusPendingCall *call, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       DBusError derr;
+       DBusMessage *reply, *msg;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (ofono_modem_get_powered(modem))
+               goto done;
+
+       dbus_error_init(&derr);
+       if (!dbus_set_error_from_message(&derr, reply))
+               goto done;
+
+       DBG("Connect reply: %s", derr.message);
+
+       if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) {
+               msg = dbus_message_new_method_call(BLUEZ_SERVICE,
+                               data->handsfree_path,
+                               BLUEZ_GATEWAY_INTERFACE, "Disconnect");
+               if (msg == NULL)
+                       ofono_error("Disconnect failed");
+               else
+                       g_dbus_send_message(connection, msg);
+       }
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       dbus_error_free(&derr);
+
+done:
+       dbus_message_unref(reply);
+       data->call = NULL;
+}
+
+/* power up hardware */
+static int hfp_enable(struct ofono_modem *modem)
+{
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       int status;
+
+       DBG("%p", modem);
+
+       status = bluetooth_send_with_reply(data->handsfree_path,
+                                       BLUEZ_GATEWAY_INTERFACE, "Connect",
+                                       &data->call, hfp_connect_reply,
+                                       modem, NULL,
+                                       DBUS_TIMEOUT, DBUS_TYPE_INVALID);
+
+       if (status < 0)
+               return -EINVAL;
+
+       return -EINPROGRESS;
+}
+
+static void hfp_power_down(DBusPendingCall *call, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       DBusMessage *reply;
+       DBusError derr;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               DBG("Disconnect reply: %s", derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       ofono_modem_set_powered(modem, FALSE);
+
+done:
+       dbus_message_unref(reply);
+       data->call = NULL;
+}
+
+static int hfp_disable(struct ofono_modem *modem)
+{
+       struct hfp_data *data = ofono_modem_get_data(modem);
+       int status;
+
+       DBG("%p", modem);
+
+       g_at_chat_unref(data->info.chat);
+       data->info.chat = NULL;
+
+       if (data->agent_registered) {
+               status = bluetooth_send_with_reply(data->handsfree_path,
+                                       BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+                                       &data->call, hfp_power_down,
+                                       modem, NULL,
+                                       DBUS_TIMEOUT, DBUS_TYPE_INVALID);
+
+               if (status < 0)
+                       return -EINVAL;
+       }
+
+       return -EINPROGRESS;
+}
+
+static void hfp_pre_sim(struct ofono_modem *modem)
+{
+       struct hfp_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "hfpmodem", data->handsfree_address);
+       ofono_voicecall_create(modem, 0, "hfpmodem", &data->info);
+       ofono_netreg_create(modem, 0, "hfpmodem", &data->info);
+       ofono_call_volume_create(modem, 0, "hfpmodem", &data->info);
+       ofono_handsfree_create(modem, 0, "hfpmodem", &data->info);
+}
+
+static void hfp_post_sim(struct ofono_modem *modem)
+{
+       DBG("%p", modem);
+}
+
+static struct ofono_modem_driver hfp_driver = {
+       .name           = "hfp",
+       .modem_type     = OFONO_MODEM_TYPE_HFP,
+       .probe          = hfp_probe,
+       .remove         = hfp_remove,
+       .enable         = hfp_enable,
+       .disable        = hfp_disable,
+       .pre_sim        = hfp_pre_sim,
+       .post_sim       = hfp_post_sim,
+};
+
+static struct bluetooth_profile hfp_hf = {
+       .name           = "hfp_hf",
+       .probe          = hfp_hf_probe,
+       .remove         = hfp_hf_remove,
+       .set_alias      = hfp_hf_set_alias,
+};
+
+static int hfp_init(void)
+{
+       int err;
+
+       if (DBUS_TYPE_UNIX_FD < 0)
+               return -EBADF;
+
+       connection = ofono_dbus_get_connection();
+
+       err = ofono_modem_driver_register(&hfp_driver);
+       if (err < 0)
+               return err;
+
+       err = bluetooth_register_uuid(HFP_AG_UUID, &hfp_hf);
+       if (err < 0) {
+               ofono_modem_driver_unregister(&hfp_driver);
+               return err;
+       }
+
+       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, NULL);
+
+       return 0;
+}
+
+static void hfp_exit(void)
+{
+       bluetooth_unregister_uuid(HFP_AG_UUID);
+       ofono_modem_driver_unregister(&hfp_driver);
+
+       g_hash_table_destroy(modem_hash);
+}
+
+OFONO_PLUGIN_DEFINE(hfp, "Hands-Free Profile Plugins", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, hfp_init, hfp_exit)
diff --git a/plugins/hso.c b/plugins/hso.c
new file mode 100644 (file)
index 0000000..4834c56
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/radio-settings.h>
+#include <ofono/voicecall.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+static const char *opmn_prefix[] = { "_OPMN:", NULL };
+static const char *obls_prefix[] = { "_OBLS:", NULL };
+static const char *opcm_prefix[] = { "_OPCMENABLE:", NULL };
+
+struct hso_data {
+       GAtChat *app;
+       GAtChat *control;
+       guint sim_poll_source;
+       guint sim_poll_count;
+       ofono_bool_t have_sim;
+       ofono_bool_t have_voice;
+};
+
+static int hso_probe(struct ofono_modem *modem)
+{
+       struct hso_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct hso_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void hso_remove(struct ofono_modem *modem)
+{
+       struct hso_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_at_chat_unref(data->control);
+
+       if (data->sim_poll_source > 0)
+               g_source_remove(data->sim_poll_source);
+
+       g_free(data);
+}
+
+static void hso_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static void opcm_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hso_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+
+       DBG("");
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "_OPCMENABLE:"))
+               return;
+
+       g_at_chat_send(data->app, "AT_OPCMENABLE=1", none_prefix,
+                                               NULL, NULL, NULL);
+}
+
+static void opcm_support(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hso_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+
+       DBG("");
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "_OPCMENABLE:"))
+               return;
+
+       data->have_voice = TRUE;
+
+       g_at_chat_send(data->app, "AT_OPCMENABLE?", opcm_prefix,
+                                       opcm_query, modem, NULL);
+}
+
+static gboolean init_sim_check(gpointer user_data);
+
+static void sim_status(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hso_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       int sim, pb, sms;
+
+       DBG("");
+
+       if (data->sim_poll_source > 0) {
+               g_source_remove(data->sim_poll_source);
+               data->sim_poll_source = 0;
+       }
+
+       if (!ok) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "_OBLS:")) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       if (!g_at_result_iter_next_number(&iter, &sim)) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       if (!g_at_result_iter_next_number(&iter, &pb)) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       if (!g_at_result_iter_next_number(&iter, &sms)) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       DBG("status sim %d pb %d sms %d", sim, pb, sms);
+
+       switch (sim) {
+       case 0:         /* not ready */
+               data->have_sim = FALSE;
+
+               if (data->sim_poll_count++ < 5) {
+                       data->sim_poll_source = g_timeout_add_seconds(1,
+                                                       init_sim_check, modem);
+                       return;
+               }
+               break;
+       case 1:         /* SIM card ready */
+               data->have_sim = TRUE;
+               break;
+       case 2:         /* no SIM card */
+               data->have_sim = FALSE;
+               break;
+       default:
+               data->have_sim = FALSE;
+               break;
+       }
+
+       data->sim_poll_count = 0;
+
+       ofono_modem_set_powered(modem, data->have_sim);
+
+       if (data->have_sim == FALSE)
+               return;
+
+       /*
+        * Ensure that the modem is using GSM character set and not IRA,
+        * otherwise weirdness with umlauts and other non-ASCII characters
+        * can result
+        */
+       g_at_chat_send(data->control, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+       g_at_chat_send(data->app, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+
+       /*
+        * Option has the concept of Speech Service versus
+        * Data Service. Problem is that in Data Service mode
+        * the card will reject all voice calls. This is a
+        * problem for Multi-SIM cards where one of the SIM
+        * cards is used in a mobile phone and thus incoming
+        * calls would be not signalled on the phone.
+        *
+        *   0 = Speech Service enabled
+        *   1 = Data Service only mode
+        */
+       g_at_chat_send(data->app, "AT_ODO?", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(data->app, "AT_ODO=0", none_prefix, NULL, NULL, NULL);
+
+       g_at_chat_send(data->app, "AT_OPCMENABLE=?", opcm_prefix,
+                                       opcm_support, modem, NULL);
+}
+
+static gboolean init_sim_check(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hso_data *data = ofono_modem_get_data(modem);
+
+       data->sim_poll_source = 0;
+
+       g_at_chat_send(data->control, "AT_OBLS", obls_prefix,
+                                       sim_status, modem, NULL);
+
+       return FALSE;
+}
+
+static void check_model(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       GAtResultIter iter;
+       char const *model;
+
+       DBG("");
+
+       if (!ok)
+               goto done;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "_OPMN:"))
+               goto done;
+
+       if (g_at_result_iter_next_unquoted_string(&iter, &model))
+               ofono_info("Model is %s", model);
+
+done:
+       init_sim_check(modem);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hso_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       g_at_chat_send(data->control, "AT_OPMN", opmn_prefix,
+                                       check_model, modem, NULL);
+}
+
+static GAtChat *create_port(const char *device)
+{
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GAtChat *chat;
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       return chat;
+}
+
+static int hso_enable(struct ofono_modem *modem)
+{
+       struct hso_data *data = ofono_modem_get_data(modem);
+       const char *app;
+       const char *control;
+
+       DBG("%p", modem);
+
+       control = ofono_modem_get_string(modem, "ControlPort");
+       app = ofono_modem_get_string(modem, "ApplicationPort");
+
+       if (app == NULL || control == NULL)
+               return -EINVAL;
+
+       data->control = create_port(control);
+
+       if (data->control == NULL)
+               return -EIO;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->control, hso_debug, "Control: ");
+
+       data->app = create_port(app);
+
+       if (data->app == NULL) {
+               g_at_chat_unref(data->control);
+               data->control = NULL;
+
+               return -EIO;
+       }
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->app, hso_debug, "App: ");
+
+       g_at_chat_send(data->control, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->app, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->control, "AT+CFUN=4", none_prefix,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct hso_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->control);
+       data->control = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int hso_disable(struct ofono_modem *modem)
+{
+       struct hso_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (data->control == NULL)
+               return 0;
+
+       g_at_chat_cancel_all(data->control);
+       g_at_chat_unregister_all(data->control);
+
+       g_at_chat_unref(data->app);
+       data->app = NULL;
+
+       g_at_chat_send(data->control, "AT+CFUN=0", none_prefix,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       if (ok)
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void hso_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct hso_data *data = ofono_modem_get_data(modem);
+       GAtChat *chat = data->control;
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(chat, command, NULL, set_online_cb, cbd, g_free))
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void hso_pre_sim(struct ofono_modem *modem)
+{
+       struct hso_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->control);
+       sim = ofono_sim_create(modem, OFONO_VENDOR_OPTION_HSO,
+                                       "atmodem", data->control);
+
+       if (sim && data->have_sim == TRUE)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void hso_post_sim(struct ofono_modem *modem)
+{
+       struct hso_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (data->have_voice == TRUE) {
+               ofono_voicecall_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                               "atmodem", data->app);
+       }
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->app);
+       ofono_radio_settings_create(modem, 0, "hsomodem", data->app);
+
+       ofono_sms_create(modem, OFONO_VENDOR_OPTION_HSO, "atmodem", data->app);
+}
+
+static void hso_post_online(struct ofono_modem *modem)
+{
+       struct hso_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_OPTION_HSO,
+                                       "atmodem", data->app);
+
+       ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                       "atmodem", data->app);
+       ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                       "atmodem", data->app);
+
+       gprs = ofono_gprs_create(modem, 0, "atmodem", data->app);
+       gc = ofono_gprs_context_create(modem, 0, "hsomodem", data->control);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+}
+
+static struct ofono_modem_driver hso_driver = {
+       .name           = "hso",
+       .probe          = hso_probe,
+       .remove         = hso_remove,
+       .enable         = hso_enable,
+       .disable        = hso_disable,
+       .set_online     = hso_set_online,
+       .pre_sim        = hso_pre_sim,
+       .post_sim       = hso_post_sim,
+       .post_online    = hso_post_online,
+};
+
+static int hso_init(void)
+{
+       return ofono_modem_driver_register(&hso_driver);
+}
+
+static void hso_exit(void)
+{
+       ofono_modem_driver_unregister(&hso_driver);
+}
+
+OFONO_PLUGIN_DEFINE(hso, "Option HSO modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, hso_init, hso_exit)
diff --git a/plugins/huawei.c b/plugins/huawei.c
new file mode 100644 (file)
index 0000000..5d8875a
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/audio-settings.h>
+#include <ofono/radio-settings.h>
+#include <ofono/voicecall.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-barring.h>
+#include <ofono/phonebook.h>
+#include <ofono/message-waiting.h>
+#include <ofono/cdma-netreg.h>
+#include <ofono/cdma-connman.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+static const char *gcap_prefix[] = { "+GCAP:", NULL };
+static const char *rfswitch_prefix[] = { "^RFSWITCH:", NULL };
+static const char *sysinfo_prefix[] = { "^SYSINFO:", NULL };
+static const char *ussdmode_prefix[] = { "^USSDMODE:", NULL };
+static const char *dialmode_prefix[] = { "^DIALMODE:", NULL };
+static const char *cvoice_prefix[] = { "^CVOICE:", NULL };
+
+enum {
+       SIM_STATE_INVALID_OR_LOCKED =   0,
+       SIM_STATE_VALID =               1,
+       SIM_STATE_INVALID_CS =          2,
+       SIM_STATE_INVALID_PS =          3,
+       SIM_STATE_INVALID_PS_AND_CS =   4,
+       SIM_STATE_ROMSIM =              240,
+       SIM_STATE_NOT_EXISTENT =        255,
+};
+
+struct huawei_data {
+       GAtChat *modem;
+       GAtChat *pcui;
+       gboolean have_sim;
+       int sim_state;
+       guint sysinfo_poll_source;
+       guint sysinfo_poll_count;
+       struct cb_data *online_cbd;
+       const char *offline_command;
+       gboolean have_voice;
+       gboolean have_gsm;
+       gboolean have_cdma;
+       gboolean have_ndis;
+       gboolean have_ussdmode;
+};
+
+static int huawei_probe(struct ofono_modem *modem)
+{
+       struct huawei_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct huawei_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void huawei_remove(struct ofono_modem *modem)
+{
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup after potential enable polling */
+       if (data->sysinfo_poll_source > 0)
+               g_source_remove(data->sysinfo_poll_source);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->pcui);
+
+       g_free(data);
+}
+
+static void huawei_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static void ussdmode_query_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct huawei_data *data = user_data;
+       GAtResultIter iter;
+       gint ussdmode;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^USSDMODE:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &ussdmode))
+               return;
+
+       if (ussdmode == 1)
+               data->have_ussdmode = TRUE;
+}
+
+static void ussdmode_support_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct huawei_data *data = user_data;
+       GAtResultIter iter;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^USSDMODE:"))
+               return;
+
+       /* Query current USSD mode */
+       g_at_chat_send(data->pcui, "AT^USSDMODE?", ussdmode_prefix,
+                                       ussdmode_query_cb, data, NULL);
+}
+
+static void dialmode_query_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct huawei_data *data = user_data;
+       GAtResultIter iter;
+       gint dialmode, cdc_spec;
+       const char *str = "unknown";
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^DIALMODE:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &dialmode))
+               return;
+
+       if (g_at_result_iter_next_number(&iter, &cdc_spec)) {
+               switch (cdc_spec) {
+               case 0:
+                       str = "none";
+                       break;
+               case 1:
+                       str = "Modem port";
+                       break;
+               case 2:
+                       str = "NDIS port";
+                       break;
+               case 3:
+                       str = "Modem and NDIS port";
+                       break;
+               }
+       }
+
+       switch (dialmode) {
+       case 0:
+               ofono_info("Modem support (CDC support: %s)", str);
+               data->have_ndis = FALSE;
+               break;
+       case 1:
+               ofono_info("NDIS support (CDC support: %s)", str);
+               data->have_ndis = TRUE;
+               break;
+       case 2:
+               ofono_info("Modem and NDIS support (CDC support: %s)", str);
+               data->have_ndis = TRUE;
+               break;
+       }
+}
+
+static void dialmode_support_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct huawei_data *data = user_data;
+       GAtResultIter iter;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^DIALMODE:"))
+               return;
+
+       /* Query current NDIS mode */
+       g_at_chat_send(data->pcui, "AT^DIALMODE?", dialmode_prefix,
+                                       dialmode_query_cb, data, NULL);
+}
+
+static void cvoice_query_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       gint mode, rate, bits, period;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^CVOICE:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &mode))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &rate))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &bits))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &period))
+               return;
+
+       data->have_voice = TRUE;
+
+       ofono_info("Voice channel: %d Hz, %d bits, %dms period",
+                                               rate, bits, period);
+
+       /* Check available voice ports */
+       g_at_chat_send(data->pcui, "AT^DDSETEX=?", none_prefix,
+                                               NULL, NULL, NULL);
+}
+
+static void cvoice_support_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^CVOICE:"))
+               return;
+
+       /* Query current voice setting */
+       g_at_chat_send(data->pcui, "AT^CVOICE?", cvoice_prefix,
+                                       cvoice_query_cb, modem, NULL);
+}
+
+static void simst_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       int sim_state;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^SIMST:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &sim_state))
+               return;
+
+       DBG("%d -> %d", data->sim_state, sim_state);
+
+       data->sim_state = sim_state;
+}
+
+static gboolean parse_sysinfo_result(GAtResult *result, int *srv_status,
+                                       int *srv_domain, int *sim_state)
+{
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "^SYSINFO:"))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, srv_status))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, srv_domain))
+               return FALSE;
+
+       if (!g_at_result_iter_skip_next(&iter))
+               return FALSE;
+
+       if (!g_at_result_iter_skip_next(&iter))
+               return FALSE;
+
+       if (!g_at_result_iter_next_number(&iter, sim_state))
+               return FALSE;
+
+       return TRUE;
+}
+
+static void shutdown_device(struct huawei_data *data)
+{
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->pcui);
+       g_at_chat_unregister_all(data->pcui);
+
+       g_at_chat_unref(data->pcui);
+       data->pcui = NULL;
+}
+
+static void cfun_offline(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               shutdown_device(data);
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       ofono_modem_set_powered(modem, TRUE);
+}
+
+static gboolean sysinfo_enable_check(gpointer user_data);
+
+static void sysinfo_enable_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+       int srv_status, srv_domain, sim_state;
+
+       if (!ok)
+               goto failure;
+
+       if (parse_sysinfo_result(result, &srv_status, &srv_domain,
+                                               &sim_state) == FALSE)
+               goto failure;
+
+       DBG("%d -> %d", data->sim_state, sim_state);
+
+       data->sim_state = sim_state;
+
+       if (sim_state == SIM_STATE_NOT_EXISTENT) {
+               data->sysinfo_poll_count++;
+
+               if (data->sysinfo_poll_count > 5)
+                       goto failure;
+
+               data->sysinfo_poll_source = g_timeout_add_seconds(1,
+                                               sysinfo_enable_check, modem);
+               return;
+       }
+
+       data->have_sim = TRUE;
+
+       /* Switch data carrier detect signal off */
+       g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->pcui, "AT&C0", NULL, NULL, NULL, NULL);
+
+       /*
+        * Ensure that the modem is using GSM character set and not IRA,
+        * otherwise weirdness with umlauts and other non-ASCII characters
+        * can result
+        */
+       g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+       g_at_chat_send(data->pcui, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+
+       /* Query current device settings */
+       g_at_chat_send(data->pcui, "AT^U2DIAG?", none_prefix,
+                                               NULL, NULL, NULL);
+
+       /* Query current port settings */
+       g_at_chat_send(data->pcui, "AT^GETPORTMODE", none_prefix,
+                                               NULL, NULL, NULL);
+
+       /* Check USSD mode support */
+       g_at_chat_send(data->pcui, "AT^USSDMODE=?", ussdmode_prefix,
+                                       ussdmode_support_cb, data, NULL);
+
+       /* Check NDIS mode support */
+       g_at_chat_send(data->pcui, "AT^DIALMODE=?", dialmode_prefix,
+                                       dialmode_support_cb, data, NULL);
+
+       /* Check for voice support */
+       g_at_chat_send(data->pcui, "AT^CVOICE=?", cvoice_prefix,
+                                       cvoice_support_cb, modem, NULL);
+
+       if (g_at_chat_send(data->pcui, data->offline_command, none_prefix,
+                                       cfun_offline, modem, NULL) > 0)
+               return;
+
+failure:
+       shutdown_device(data);
+       ofono_modem_set_powered(modem, FALSE);
+}
+
+static gboolean sysinfo_enable_check(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       data->sysinfo_poll_source = 0;
+
+       g_at_chat_send(data->pcui, "AT^SYSINFO", sysinfo_prefix,
+                                       sysinfo_enable_cb, modem, NULL);
+
+       return FALSE;
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               shutdown_device(data);
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       /* Follow sim state changes */
+       g_at_chat_register(data->pcui, "^SIMST:", simst_notify,
+                                               FALSE, modem, NULL);
+
+       data->sysinfo_poll_count = 0;
+
+       sysinfo_enable_check(modem);
+}
+
+static void rfswitch_support(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       if (data->have_gsm == FALSE && data->have_cdma == TRUE) {
+               data->offline_command = "AT+CFUN=5";
+               goto done;
+       }
+
+       if (!ok)
+               data->offline_command = "AT+CFUN=5";
+       else
+               data->offline_command = "AT+CFUN=7";
+
+done:
+       g_at_chat_send(data->pcui, "AT+CFUN=1", none_prefix,
+                                       cfun_enable, modem, NULL);
+}
+
+static void gcap_support(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       const char *gcap;
+
+       if (!ok)
+               goto done;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+GCAP:"))
+               goto done;
+
+       while (g_at_result_iter_next_unquoted_string(&iter, &gcap)) {
+               if (*gcap == '\0')
+                       break;
+
+               if (!strcmp(gcap, "+CGSM"))
+                       data->have_gsm = TRUE;
+               else if (!strcmp(gcap, "+CIS707-A"))
+                       data->have_cdma = TRUE;
+       }
+
+done:
+       g_at_chat_send(data->pcui, "AT^RFSWITCH=?", rfswitch_prefix,
+                                       rfswitch_support, modem, NULL);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       g_at_chat_add_terminator(chat, "COMMAND NOT SUPPORT", -1, FALSE);
+       g_at_chat_add_terminator(chat, "TOO MANY PARAMETERS", -1, FALSE);
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, huawei_debug, debug);
+
+       return chat;
+}
+
+static int huawei_enable(struct ofono_modem *modem)
+{
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               return -EINVAL;
+
+       data->pcui = open_device(modem, "Pcui", "PCUI: ");
+       if (data->pcui == NULL) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+               return -EIO;
+       }
+
+       g_at_chat_set_slave(data->modem, data->pcui);
+
+       g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->pcui, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       data->sim_state = SIM_STATE_NOT_EXISTENT;
+
+       /* Check for GSM capabilities */
+       g_at_chat_send(data->pcui, "ATI", gcap_prefix,
+                                       gcap_support, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->pcui);
+       data->pcui = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int huawei_disable(struct ofono_modem *modem)
+{
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->pcui);
+       g_at_chat_unregister_all(data->pcui);
+
+       /* Cleanup potential online enable polling */
+       if (data->sysinfo_poll_source > 0) {
+               g_source_remove(data->sysinfo_poll_source);
+               data->sysinfo_poll_source = 0;
+
+               g_free(data->online_cbd);
+               data->online_cbd = NULL;
+       }
+
+       g_at_chat_send(data->pcui, "AT+CFUN=0", none_prefix,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static gboolean sysinfo_online_check(gpointer user_data);
+
+static void sysinfo_online_cb(gboolean ok, GAtResult *result,
+                                               gpointer user_data)
+{
+       struct huawei_data *data = user_data;
+       ofono_modem_online_cb_t cb = data->online_cbd->cb;
+       int srv_status, srv_domain, sim_state;
+
+       if (!ok)
+               goto failure;
+
+       if (parse_sysinfo_result(result, &srv_status, &srv_domain,
+                                                       &sim_state) == FALSE)
+               goto failure;
+
+       DBG("%d -> %d", data->sim_state, sim_state);
+
+       data->sim_state = sim_state;
+
+       /* Valid service status and at minimum PS domain */
+       if (srv_status > 0 && srv_domain > 1) {
+               CALLBACK_WITH_SUCCESS(cb, data->online_cbd->data);
+               goto done;
+       }
+
+       switch (sim_state) {
+       case SIM_STATE_VALID:
+       case SIM_STATE_INVALID_CS:
+       case SIM_STATE_INVALID_PS:
+       case SIM_STATE_INVALID_PS_AND_CS:
+       case SIM_STATE_ROMSIM:
+               CALLBACK_WITH_SUCCESS(cb, data->online_cbd->data);
+               goto done;
+       }
+
+       data->sysinfo_poll_count++;
+
+       if (data->sysinfo_poll_count > 15)
+               goto failure;
+
+       data->sysinfo_poll_source = g_timeout_add_seconds(2,
+                                               sysinfo_online_check, data);
+       return;
+
+failure:
+       CALLBACK_WITH_FAILURE(cb, data->online_cbd->data);
+
+done:
+       g_free(data->online_cbd);
+       data->online_cbd = NULL;
+}
+
+static gboolean sysinfo_online_check(gpointer user_data)
+{
+       struct huawei_data *data = user_data;
+
+       data->sysinfo_poll_source = 0;
+
+       g_at_chat_send(data->pcui, "AT^SYSINFO", sysinfo_prefix,
+                                       sysinfo_online_cb, data, NULL);
+
+       return FALSE;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       if (!ok) {
+               ofono_modem_online_cb_t cb = data->online_cbd->cb;
+
+               CALLBACK_WITH_FAILURE(cb, data->online_cbd->data);
+
+               g_free(data->online_cbd);
+               data->online_cbd = NULL;
+               return;
+       }
+
+       data->sysinfo_poll_count = 0;
+
+       sysinfo_online_check(data);
+}
+
+static void set_offline_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void huawei_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (online == TRUE) {
+               data->online_cbd = cb_data_new(cb, user_data);
+
+               if (g_at_chat_send(data->pcui, "AT+CFUN=1", none_prefix,
+                                       set_online_cb, modem, NULL) > 0)
+                       return;
+
+               g_free(data->online_cbd);
+               data->online_cbd = NULL;
+       } else {
+               struct cb_data *cbd = cb_data_new(cb, user_data);
+
+               if (g_at_chat_send(data->pcui, data->offline_command,
+                               none_prefix, set_offline_cb, cbd, g_free) > 0)
+                       return;
+
+               g_free(cbd);
+       }
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void huawei_pre_sim(struct ofono_modem *modem)
+{
+       struct huawei_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim = NULL;
+
+       DBG("%p", modem);
+
+       if (data->have_gsm == TRUE) {
+               ofono_devinfo_create(modem, 0, "atmodem", data->pcui);
+               sim = ofono_sim_create(modem, OFONO_VENDOR_HUAWEI,
+                                               "atmodem", data->pcui);
+       } else if (data->have_cdma == TRUE) {
+               ofono_devinfo_create(modem, 0, "cdmamodem", data->pcui);
+
+               /* Create SIM atom only if SIM is not embedded */
+               if (data->sim_state != SIM_STATE_ROMSIM)
+                       sim = ofono_sim_create(modem, OFONO_VENDOR_HUAWEI,
+                                               "atmodem-noef", data->pcui);
+       }
+
+       if (sim && data->have_sim == TRUE)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void huawei_post_sim(struct ofono_modem *modem)
+{
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (data->have_voice == TRUE) {
+               ofono_voicecall_create(modem, 0, "huaweimodem", data->pcui);
+               ofono_audio_settings_create(modem, 0,
+                                               "huaweimodem", data->pcui);
+       }
+
+       if (data->have_gsm == TRUE) {
+               struct ofono_gprs *gprs;
+               struct ofono_gprs_context *gc;
+
+               ofono_phonebook_create(modem, 0, "atmodem", data->pcui);
+               ofono_radio_settings_create(modem, 0,
+                                               "huaweimodem", data->pcui);
+
+               ofono_sms_create(modem, OFONO_VENDOR_HUAWEI,
+                                               "atmodem", data->pcui);
+
+               gprs = ofono_gprs_create(modem, OFONO_VENDOR_HUAWEI,
+                                               "atmodem", data->pcui);
+               gc = ofono_gprs_context_create(modem, 0,
+                                               "atmodem", data->modem);
+
+               if (gprs && gc)
+                       ofono_gprs_add_context(gprs, gc);
+       }
+}
+
+static void huawei_post_online(struct ofono_modem *modem)
+{
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (data->have_gsm == TRUE) {
+               ofono_netreg_create(modem, OFONO_VENDOR_HUAWEI,
+                                               "atmodem", data->pcui);
+
+               ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                               "atmodem", data->pcui);
+               if (data->have_ussdmode == TRUE)
+                       ofono_ussd_create(modem, 0, "huaweimodem", data->pcui);
+               else
+                       ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                                       "atmodem", data->pcui);
+       } else if (data->have_cdma == TRUE) {
+               ofono_cdma_netreg_create(modem, 0, "huaweimodem", data->pcui);
+
+               ofono_cdma_connman_create(modem, OFONO_VENDOR_HUAWEI,
+                                               "cdmamodem", data->modem);
+       }
+
+       if (data->have_voice == TRUE) {
+               struct ofono_message_waiting *mw;
+
+               ofono_call_forwarding_create(modem, 0, "atmodem", data->pcui);
+               ofono_call_settings_create(modem, 0, "atmodem", data->pcui);
+               ofono_call_barring_create(modem, 0, "atmodem", data->pcui);
+
+               mw = ofono_message_waiting_create(modem);
+               if (mw)
+                       ofono_message_waiting_register(mw);
+       }
+}
+
+static struct ofono_modem_driver huawei_driver = {
+       .name           = "huawei",
+       .probe          = huawei_probe,
+       .remove         = huawei_remove,
+       .enable         = huawei_enable,
+       .disable        = huawei_disable,
+       .set_online     = huawei_set_online,
+       .pre_sim        = huawei_pre_sim,
+       .post_sim       = huawei_post_sim,
+       .post_online    = huawei_post_online,
+};
+
+static int huawei_init(void)
+{
+       return ofono_modem_driver_register(&huawei_driver);
+}
+
+static void huawei_exit(void)
+{
+       ofono_modem_driver_unregister(&huawei_driver);
+}
+
+OFONO_PLUGIN_DEFINE(huawei, "HUAWEI Mobile modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, huawei_init, huawei_exit)
diff --git a/plugins/ifx.c b/plugins/ifx.c
new file mode 100644 (file)
index 0000000..d1b622c
--- /dev/null
@@ -0,0 +1,751 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gatmux.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/voicecall.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-volume.h>
+#include <ofono/message-waiting.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/radio-settings.h>
+#include <ofono/audio-settings.h>
+#include <ofono/stk.h>
+#include <ofono/ctm.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+#define NUM_DLC  6
+
+#define VOICE_DLC   0
+#define NETREG_DLC  1
+#define GPRS1_DLC   2
+#define GPRS2_DLC   3
+#define GPRS3_DLC   4
+#define AUX_DLC     5
+
+static char *dlc_prefixes[NUM_DLC] = { "Voice: ", "Net: ", "GPRS1: ",
+                                       "GPRS2: ", "GPRS3: ", "Aux: " };
+
+static const char *dlc_nodes[NUM_DLC] = { "/dev/ttyGSM1", "/dev/ttyGSM2",
+                                       "/dev/ttyGSM3", "/dev/ttyGSM4",
+                                       "/dev/ttyGSM5", "/dev/ttyGSM6" };
+
+static const char *none_prefix[] = { NULL };
+static const char *xgendata_prefix[] = { "+XGENDATA:", NULL };
+static const char *xsimstate_prefix[] = { "+XSIMSTATE:", NULL };
+
+struct ifx_data {
+       GIOChannel *device;
+       GAtMux *mux;
+       GAtChat *dlcs[NUM_DLC];
+       guint dlc_poll_count;
+       guint dlc_poll_source;
+       guint dlc_init_source;
+       guint mux_init_timeout;
+       guint frame_size;
+       int mux_ldisc;
+       int saved_ldisc;
+       struct ofono_sim *sim;
+       gboolean have_sim;
+};
+
+static void ifx_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int ifx_probe(struct ofono_modem *modem)
+{
+       struct ifx_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct ifx_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->mux_ldisc = -1;
+       data->saved_ldisc = -1;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void ifx_remove(struct ofono_modem *modem)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_free(data);
+}
+
+static void ifx_set_sim_state(struct ifx_data *data, int state)
+{
+       DBG("state %d", state);
+
+       switch (state) {
+       case 0: /* SIM not present */
+       case 9: /* SIM Removed */
+               if (data->have_sim == TRUE) {
+                       ofono_sim_inserted_notify(data->sim, FALSE);
+                       data->have_sim = FALSE;
+               }
+               break;
+       case 1: /* PIN verification needed */
+       case 2: /* PIN verification not needed – Ready */
+       case 3: /* PIN verified – Ready */
+       case 4: /* PUK verification needed */
+       case 5: /* SIM permanently blocked */
+       case 6: /* SIM Error */
+       case 7: /* ready for attach (+COPS) */
+       case 8: /* SIM Technical Problem */
+               if (data->have_sim == FALSE) {
+                       ofono_sim_inserted_notify(data->sim, TRUE);
+                       data->have_sim = TRUE;
+               }
+               break;
+       default:
+               ofono_warn("Unknown SIM state %d received", state);
+               break;
+       }
+}
+
+static void xsim_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       GAtResultIter iter;
+       int state;
+
+       if (data->sim == NULL)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XSIM:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &state))
+               return;
+
+       ifx_set_sim_state(data, state);
+}
+
+static void xsimstate_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       int mode;
+       int state;
+
+       DBG("");
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XSIMSTATE:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &mode))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &state))
+               return;
+
+       ifx_set_sim_state(data, state);
+}
+
+static void shutdown_device(struct ifx_data *data)
+{
+       int i, fd;
+
+       DBG("");
+
+       if (data->dlc_init_source > 0) {
+               g_source_remove(data->dlc_init_source);
+               data->dlc_init_source = 0;
+       }
+
+       for (i = 0; i < NUM_DLC; i++) {
+               if (data->dlcs[i] == NULL)
+                       continue;
+
+               g_at_chat_unref(data->dlcs[i]);
+               data->dlcs[i] = NULL;
+       }
+
+       if (data->mux) {
+               g_at_mux_shutdown(data->mux);
+               g_at_mux_unref(data->mux);
+               data->mux = NULL;
+               goto done;
+       }
+
+       fd = g_io_channel_unix_get_fd(data->device);
+
+       if (ioctl(fd, TIOCSETD, &data->saved_ldisc) < 0)
+               ofono_warn("Failed to restore line discipline");
+
+done:
+       g_io_channel_unref(data->device);
+       data->device = NULL;
+}
+
+static void dlc_disconnect(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       ofono_warn("Disconnect of modem channel");
+
+       shutdown_device(data);
+}
+
+static GAtChat *create_chat(GIOChannel *channel, struct ofono_modem *modem,
+                                                               char *debug)
+{
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsmv1();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, ifx_debug, debug);
+
+       g_at_chat_set_disconnect_function(chat, dlc_disconnect, modem);
+
+       return chat;
+}
+
+static void xgendata_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       const char *gendata;
+
+       DBG("");
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+XGENDATA:"))
+               goto error;
+
+       if (!g_at_result_iter_next_string(&iter, &gendata))
+               goto error;
+
+       DBG("\n%s", gendata);
+
+       /* disable UART for power saving */
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+XPOW=0,0,0", none_prefix,
+                                                       NULL, NULL, NULL);
+
+       data->have_sim = FALSE;
+
+       /* notify that the modem is ready so that pre_sim gets called */
+       ofono_modem_set_powered(modem, TRUE);
+
+       g_at_chat_register(data->dlcs[AUX_DLC], "+XSIM:", xsim_notify,
+                                               FALSE, modem, NULL);
+
+       /* enable XSIM and XLOCK notifications */
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+XSIMSTATE=1", none_prefix,
+                                               NULL, NULL, NULL);
+
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+XSIMSTATE?", xsimstate_prefix,
+                                       xsimstate_query, modem, NULL);
+
+       return;
+
+error:
+       shutdown_device(data);
+       ofono_modem_set_powered(modem, FALSE);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               shutdown_device(data);
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+XGENDATA", xgendata_prefix,
+                                       xgendata_query, modem, NULL);
+}
+
+static gboolean dlc_setup(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("");
+
+       for (i = 0; i < NUM_DLC; i++)
+               g_at_chat_send(data->dlcs[i], "ATE0 +CMEE=1", NULL,
+                                               NULL, NULL, NULL);
+
+       g_at_chat_set_slave(data->dlcs[GPRS1_DLC], data->dlcs[NETREG_DLC]);
+       g_at_chat_set_slave(data->dlcs[GPRS2_DLC], data->dlcs[NETREG_DLC]);
+       g_at_chat_set_slave(data->dlcs[GPRS3_DLC], data->dlcs[NETREG_DLC]);
+
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=4", NULL,
+                                       cfun_enable, modem, NULL);
+
+       data->dlc_init_source = 0;
+
+       return FALSE;
+}
+
+static gboolean dlc_ready_check(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       struct stat st;
+       int i;
+
+       DBG("");
+
+       data->dlc_poll_count++;
+
+       if (stat(dlc_nodes[AUX_DLC], &st) < 0) {
+               /* only possible error is ENOENT */
+               if (data->dlc_poll_count > 6)
+                       goto error;
+
+               return TRUE;
+       }
+
+       for (i = 0; i < NUM_DLC; i++) {
+               GIOChannel *channel = g_at_tty_open(dlc_nodes[i], NULL);
+
+               data->dlcs[i] = create_chat(channel, modem, dlc_prefixes[i]);
+               if (data->dlcs[i] == NULL) {
+                       ofono_error("Failed to open %s", dlc_nodes[i]);
+                       goto error;
+               }
+       }
+
+       data->dlc_poll_source = 0;
+
+       /* iterate through mainloop */
+       data->dlc_init_source = g_timeout_add_seconds(0, dlc_setup, modem);
+
+       return FALSE;
+
+error:
+       data->dlc_poll_source = 0;
+
+       shutdown_device(data);
+       ofono_modem_set_powered(modem, FALSE);
+
+       return FALSE;
+}
+
+static void setup_internal_mux(struct ofono_modem *modem)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("");
+
+       data->mux = g_at_mux_new_gsm0710_basic(data->device, data->frame_size);
+       if (data->mux == NULL)
+               goto error;
+
+       if (getenv("OFONO_MUX_DEBUG"))
+               g_at_mux_set_debug(data->mux, ifx_debug, "MUX: ");
+
+       g_at_mux_start(data->mux);
+
+       for (i = 0; i < NUM_DLC; i++) {
+               GIOChannel *channel = g_at_mux_create_channel(data->mux);
+
+               data->dlcs[i] = create_chat(channel, modem, dlc_prefixes[i]);
+               if (data->dlcs[i] == NULL) {
+                       ofono_error("Failed to create channel");
+                       goto error;
+               }
+       }
+
+       /* wait for DLC creation to settle */
+       data->dlc_init_source = g_timeout_add(10, dlc_setup, modem);
+
+       return;
+
+error:
+       shutdown_device(data);
+       ofono_modem_set_powered(modem, FALSE);
+}
+
+static void mux_setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       int fd;
+
+       DBG("");
+
+       if (data->mux_init_timeout > 0) {
+               g_source_remove(data->mux_init_timeout);
+               data->mux_init_timeout = 0;
+       }
+
+       g_at_chat_unref(data->dlcs[AUX_DLC]);
+       data->dlcs[AUX_DLC] = NULL;
+
+       if (!ok)
+               goto error;
+
+       if (data->mux_ldisc < 0) {
+               ofono_info("Using internal multiplexer");
+               setup_internal_mux(modem);
+               return;
+       }
+
+       fd = g_io_channel_unix_get_fd(data->device);
+
+       if (ioctl(fd, TIOCGETD, &data->saved_ldisc) < 0) {
+               ofono_error("Failed to get current line discipline");
+               goto error;
+       }
+
+       if (ioctl(fd, TIOCSETD, &data->mux_ldisc) < 0) {
+               ofono_error("Failed to set multiplexer line discipline");
+               goto error;
+       }
+
+       data->dlc_poll_count = 0;
+       data->dlc_poll_source = g_timeout_add_seconds(1, dlc_ready_check,
+                                                               modem);
+
+       return;
+
+error:
+       data->saved_ldisc = -1;
+
+       g_io_channel_unref(data->device);
+       data->device = NULL;
+
+       ofono_modem_set_powered(modem, FALSE);
+}
+
+static gboolean mux_timeout_cb(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       ofono_error("Timeout with multiplexer setup");
+
+       data->mux_init_timeout = 0;
+
+       g_at_chat_unref(data->dlcs[AUX_DLC]);
+       data->dlcs[AUX_DLC] = NULL;
+
+       g_io_channel_unref(data->device);
+       data->device = NULL;
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       return FALSE;
+}
+
+static int ifx_enable(struct ofono_modem *modem)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       const char *device, *ldisc;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       DBG("%p", modem);
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL)
+               return -EINVAL;
+
+       DBG("%s", device);
+
+       ldisc = ofono_modem_get_string(modem, "LineDiscipline");
+       if (ldisc != NULL) {
+               data->mux_ldisc = atoi(ldisc);
+               ofono_info("Using multiplexer line discipline %d",
+                                                       data->mux_ldisc);
+       }
+
+       data->device = g_at_tty_open(device, NULL);
+       if (data->device == NULL)
+               return -EIO;
+
+       syntax = g_at_syntax_new_gsmv1();
+       chat = g_at_chat_new(data->device, syntax);
+       g_at_syntax_unref(syntax);
+
+       if (chat == NULL) {
+               g_io_channel_unref(data->device);
+               return -EIO;
+       }
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, ifx_debug, "Master: ");
+
+       g_at_chat_send(chat, "ATE0 +CMEE=1", NULL,
+                                       NULL, NULL, NULL);
+
+       /* Enable multiplexer */
+       data->frame_size = 1509;
+
+       g_at_chat_send(chat, "AT+CMUX=0,0,,1509,10,3,30,,", NULL,
+                                       mux_setup_cb, modem, NULL);
+
+       data->mux_init_timeout = g_timeout_add_seconds(5, mux_timeout_cb,
+                                                               modem);
+
+       data->dlcs[AUX_DLC] = chat;
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (data->dlc_poll_source > 0) {
+               g_source_remove(data->dlc_poll_source);
+               data->dlc_poll_source = 0;
+       }
+
+       shutdown_device(data);
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int ifx_disable(struct ofono_modem *modem)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("%p", modem);
+
+       for (i = 0; i < NUM_DLC; i++) {
+               g_at_chat_cancel_all(data->dlcs[i]);
+               g_at_chat_unregister_all(data->dlcs[i]);
+       }
+
+       g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=0", NULL,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void ifx_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("%p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(data->dlcs[AUX_DLC], command, none_prefix,
+                                       set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void ifx_pre_sim(struct ofono_modem *modem)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       data->sim = ofono_sim_create(modem, OFONO_VENDOR_IFX,
+                                       "atmodem", data->dlcs[AUX_DLC]);
+       ofono_voicecall_create(modem, 0, "ifxmodem", data->dlcs[VOICE_DLC]);
+       ofono_audio_settings_create(modem, 0,
+                                       "ifxmodem", data->dlcs[VOICE_DLC]);
+       ofono_ctm_create(modem, 0, "ifxmodem", data->dlcs[AUX_DLC]);
+}
+
+static void ifx_post_sim(struct ofono_modem *modem)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_stk_create(modem, 0, "ifxmodem", data->dlcs[AUX_DLC]);
+       ofono_phonebook_create(modem, OFONO_VENDOR_IFX,
+                                       "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_forwarding_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_radio_settings_create(modem, 0, "ifxmodem", data->dlcs[AUX_DLC]);
+
+       ofono_sms_create(modem, OFONO_VENDOR_IFX,
+                                       "atmodem", data->dlcs[AUX_DLC]);
+}
+
+static void ifx_post_online(struct ofono_modem *modem)
+{
+       struct ifx_data *data = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_IFX,
+                                       "atmodem", data->dlcs[NETREG_DLC]);
+
+       ofono_cbs_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_ussd_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+
+       ofono_call_settings_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_meter_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_barring_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+       ofono_call_volume_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_IFX,
+                                       "atmodem", data->dlcs[NETREG_DLC]);
+       if (gprs == NULL)
+               return;
+
+       if (data->mux_ldisc < 0) {
+               gc = ofono_gprs_context_create(modem, 0,
+                                       "ifxmodem", data->dlcs[GPRS1_DLC]);
+               if (gc)
+                       ofono_gprs_add_context(gprs, gc);
+
+               gc = ofono_gprs_context_create(modem, 0,
+                                       "ifxmodem", data->dlcs[GPRS2_DLC]);
+               if (gc)
+                       ofono_gprs_add_context(gprs, gc);
+
+               gc = ofono_gprs_context_create(modem, 0,
+                                       "ifxmodem", data->dlcs[GPRS3_DLC]);
+               if (gc)
+                       ofono_gprs_add_context(gprs, gc);
+       }
+}
+
+static struct ofono_modem_driver ifx_driver = {
+       .name           = "ifx",
+       .probe          = ifx_probe,
+       .remove         = ifx_remove,
+       .enable         = ifx_enable,
+       .disable        = ifx_disable,
+       .set_online     = ifx_set_online,
+       .pre_sim        = ifx_pre_sim,
+       .post_sim       = ifx_post_sim,
+       .post_online    = ifx_post_online,
+};
+
+static int ifx_init(void)
+{
+       return ofono_modem_driver_register(&ifx_driver);
+}
+
+static void ifx_exit(void)
+{
+       ofono_modem_driver_unregister(&ifx_driver);
+}
+
+OFONO_PLUGIN_DEFINE(ifx, "Infineon modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, ifx_init, ifx_exit)
diff --git a/plugins/isiusb.c b/plugins/isiusb.c
new file mode 100644 (file)
index 0000000..82d2cc5
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <gisi/netlink.h>
+#include <gisi/modem.h>
+#include <gisi/client.h>
+#include <gisi/message.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/phonebook.h>
+#include <ofono/netreg.h>
+#include <ofono/voicecall.h>
+#include <ofono/sms.h>
+#include <ofono/cbs.h>
+#include <ofono/sim.h>
+#include <ofono/ussd.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-meter.h>
+#include <ofono/radio-settings.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/message-waiting.h>
+
+#include "drivers/isimodem/isimodem.h"
+#include "drivers/isimodem/isiutil.h"
+#include "drivers/isimodem/mtc.h"
+#include "drivers/isimodem/debug.h"
+
+struct isi_data {
+       char const *ifname;
+       GIsiModem *modem;
+       GIsiClient *client;
+       GIsiPhonetNetlink *link;
+       enum GIsiPhonetLinkState linkstate;
+       unsigned interval;
+       int reported;
+       ofono_bool_t online;
+       struct isi_cb_data *online_cbd;
+};
+
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
+{
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       mtc_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static void report_powered(struct ofono_modem *modem, struct isi_data *isi,
+                               ofono_bool_t powered)
+{
+       if (powered == isi->reported)
+               return;
+
+       isi->reported = powered;
+       ofono_modem_set_powered(modem, powered);
+}
+
+static void report_online(struct isi_data *isi, ofono_bool_t online)
+{
+       struct isi_cb_data *cbd = isi->online_cbd;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       isi->online_cbd = NULL;
+
+       if (isi->online == online)
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void set_power_by_mtc_state(struct ofono_modem *modem,
+                                       struct isi_data *isi, int mtc_state)
+{
+       if (isi->online_cbd)
+               report_online(isi, mtc_state == MTC_NORMAL);
+
+       switch (mtc_state) {
+       case MTC_STATE_NONE:
+       case MTC_POWER_OFF:
+       case MTC_CHARGING:
+       case MTC_SELFTEST_FAIL:
+               report_powered(modem, isi, FALSE);
+               break;
+
+       case MTC_RF_INACTIVE:
+       case MTC_NORMAL:
+       default:
+               report_powered(modem, isi, TRUE);
+       }
+}
+
+static void mtc_state_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t state;
+       uint8_t action;
+
+       if (!isi || g_isi_msg_id(msg) != MTC_STATE_INFO_IND)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &state) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &action))
+               return;
+
+       switch (action) {
+       case MTC_START:
+               DBG("target modem state: %s (0x%02X)",
+                       mtc_modem_state_name(state), state);
+               break;
+
+       case MTC_READY:
+               DBG("current modem state: %s (0x%02X)",
+                       mtc_modem_state_name(state), state);
+               set_power_by_mtc_state(modem, isi, state);
+               break;
+       }
+}
+
+static void mtc_query_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t current;
+       uint8_t target;
+
+       if (!check_response_status(msg, MTC_STATE_QUERY_RESP))
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &current) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &target))
+               return;
+
+       DBG("Modem state: current=%s (0x%02X) target=%s (0x%02X)",
+               mtc_modem_state_name(current), current,
+               mtc_modem_state_name(target), target);
+
+       if (current == target)
+               set_power_by_mtc_state(modem, isi, current);
+}
+
+static gboolean bootstrap_current_state(gpointer user)
+{
+       struct ofono_modem *om = user;
+       struct isi_data *isi = ofono_modem_get_data(om);
+
+       const uint8_t req[] = {
+               MTC_STATE_QUERY_REQ,
+               0x00, 0x00 /* Filler */
+       };
+       size_t len = sizeof(req);
+
+       g_isi_client_send(isi->client, req, len, mtc_query_cb, om, NULL);
+
+       return FALSE;
+}
+
+static void reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *om = data;
+       struct isi_data *isi = ofono_modem_get_data(om);
+
+       if (!g_isi_msg_error(msg) < 0)
+               return;
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_ind_subscribe(isi->client, MTC_STATE_INFO_IND,
+                                       mtc_state_ind_cb, om);
+
+       /*
+        * FIXME: There is a theoretical race condition here:
+        * g_isi_client_ind_subscribe() adds the actual message
+        * sending for committing changes to subscriptions in idle
+        * loop, which may or may not preserve ordering.  Thus, we
+        * might miss a state indication if the bootstrap request ends
+        * up being sent first.
+        */
+       g_idle_add(bootstrap_current_state, om);
+}
+
+static void phonet_status_cb(GIsiModem *modem, enum GIsiPhonetLinkState st,
+                               char const *ifname, void *data)
+{
+       struct ofono_modem *om = data;
+       struct isi_data *isi = ofono_modem_get_data(om);
+
+       DBG("Link %s (%u) is %s", isi->ifname, g_isi_modem_index(isi->modem),
+               st == PN_LINK_REMOVED ? "removed" :
+               st == PN_LINK_DOWN ? "down" : "up");
+
+       isi->linkstate = st;
+
+       if (st == PN_LINK_UP)
+               g_isi_client_verify(isi->client, reachable_cb, om, NULL);
+       else if (st == PN_LINK_DOWN)
+               set_power_by_mtc_state(om, isi, MTC_STATE_NONE);
+}
+
+static int isiusb_probe(struct ofono_modem *modem)
+{
+       const char *ifname = ofono_modem_get_string(modem, "Interface");
+       unsigned address = ofono_modem_get_integer(modem, "Address");
+       GIsiModem *isimodem;
+       GIsiClient *client = NULL;
+       GIsiPhonetNetlink *link = NULL;
+       struct isi_data *isi = NULL;
+
+       if (!ifname)
+               return -EINVAL;
+
+       DBG("(%p) with %s", modem, ifname);
+
+       isimodem = g_isi_modem_create_by_name(ifname);
+       if (!isimodem) {
+               DBG("Interface=%s: %s", ifname, strerror(errno));
+               return -errno;
+       }
+
+       g_isi_modem_set_userdata(isimodem, modem);
+       g_isi_modem_set_flags(isimodem, GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE);
+
+       if (getenv("OFONO_ISI_DEBUG"))
+               g_isi_modem_set_debug(isimodem, ofono_debug);
+
+       if (getenv("OFONO_ISI_TRACE"))
+               g_isi_modem_set_trace(isimodem, isi_trace);
+
+       if (g_isi_pn_netlink_by_modem(isimodem)) {
+               DBG("%s: %s", ifname, strerror(EBUSY));
+               errno = EBUSY;
+               goto error;
+       }
+
+       link = g_isi_pn_netlink_start(isimodem, phonet_status_cb, modem);
+       if (link == NULL) {
+               DBG("%s: %s", ifname, strerror(errno));
+               goto error;
+       }
+
+       if (address) {
+               int error = g_isi_pn_netlink_set_address(isimodem, address);
+               if (error && error != -EEXIST) {
+                       DBG("g_isi_pn_netlink_set_address(): %s\n",
+                               strerror(-error));
+                       errno = -error;
+                       goto error;
+               }
+       }
+
+       isi = g_try_new0(struct isi_data, 1);
+       if (!isi) {
+               errno = ENOMEM;
+               goto error;
+       }
+
+       client = g_isi_client_create(isimodem, PN_MTC);
+       if (!client)
+               goto error;
+
+       isi->modem = isimodem;
+       isi->ifname = ifname;
+       isi->link = link;
+       isi->reported = -1;
+       isi->client = client;
+
+       ofono_modem_set_data(modem, isi);
+       return 0;
+
+error:
+       g_isi_pn_netlink_stop(link);
+       g_isi_client_destroy(client);
+       g_isi_modem_destroy(isimodem);
+       g_free(isi);
+
+       return -errno;
+}
+
+static void isiusb_remove(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       if (isi == NULL)
+               return;
+
+       g_isi_pn_netlink_stop(isi->link);
+       g_isi_client_destroy(isi->client);
+       g_isi_modem_destroy(isi->modem);
+       g_free(isi);
+}
+
+static void mtc_state_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_modem *modem = cbd->user;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t cause;
+
+       if (!check_response_status(msg, MTC_STATE_RESP))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &cause))
+               goto error;
+
+       DBG("MTC cause: %s (0x%02X)", mtc_isi_cause_name(cause), cause);
+
+       if (cause == MTC_OK || cause == MTC_STATE_TRANSITION_GOING_ON) {
+               isi->online_cbd = cbd;
+               return;
+       }
+
+       if (cause == MTC_ALREADY_ACTIVE) {
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+               g_free(cbd);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+       g_free(cbd);
+}
+
+static void isiusb_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *data)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       struct isi_cb_data *cbd = isi_cb_data_new(modem, cb, data);
+       const uint8_t req[] = {
+               MTC_STATE_REQ,
+               online ? MTC_NORMAL : MTC_RF_INACTIVE,
+               0x00
+       };
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       if (cbd == NULL || isi == NULL)
+               goto error;
+
+       if (g_isi_client_send_with_timeout(isi->client, req, sizeof(req),
+                               MTC_STATE_REQ_TIMEOUT,
+                               mtc_state_cb, cbd, NULL)) {
+               isi->online = online;
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void isiusb_pre_sim(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_sim_create(modem, 0, "isimodem", isi->modem);
+       ofono_devinfo_create(modem, 0, "isimodem", isi->modem);
+       ofono_voicecall_create(modem, 0, "isimodem", isi->modem);
+}
+
+static void isiusb_post_sim(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_phonebook_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_forwarding_create(modem, 0, "isimodem", isi->modem);
+       ofono_radio_settings_create(modem, 0, "isimodem", isi->modem);
+}
+
+static void isiusb_post_online(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_netreg_create(modem, 0, "isimodem", isi->modem);
+       ofono_sms_create(modem, 0, "isimodem", isi->modem);
+       ofono_cbs_create(modem, 0, "isimodem", isi->modem);
+       ofono_ussd_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_settings_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_barring_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_meter_create(modem, 0, "isimodem", isi->modem);
+       ofono_gprs_create(modem, 0, "isimodem", isi->modem);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static int isiusb_enable(struct ofono_modem *modem)
+{
+       return 0;
+}
+
+static int isiusb_disable(struct ofono_modem *modem)
+{
+       return 0;
+}
+
+static struct ofono_modem_driver driver = {
+       .name = "isiusb",
+       .probe = isiusb_probe,
+       .remove = isiusb_remove,
+       .set_online = isiusb_online,
+       .pre_sim = isiusb_pre_sim,
+       .post_sim = isiusb_post_sim,
+       .post_online = isiusb_post_online,
+       .enable = isiusb_enable,
+       .disable = isiusb_disable,
+};
+
+static int isiusb_init(void)
+{
+       return ofono_modem_driver_register(&driver);
+}
+
+static void isiusb_exit(void)
+{
+       ofono_modem_driver_unregister(&driver);
+}
+
+OFONO_PLUGIN_DEFINE(isiusb, "Generic modem driver for isi",
+                       VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       isiusb_init, isiusb_exit)
diff --git a/plugins/linktop.c b/plugins/linktop.c
new file mode 100644 (file)
index 0000000..bb0d7b8
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/radio-settings.h>
+#include <ofono/phonebook.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct linktop_data {
+       GAtChat *modem;
+       GAtChat *aux;
+};
+
+static int linktop_probe(struct ofono_modem *modem)
+{
+       struct linktop_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct linktop_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void linktop_remove(struct ofono_modem *modem)
+{
+       struct linktop_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_free(data);
+}
+
+static void linktop_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, linktop_debug, debug);
+
+       return chat;
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct linktop_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+       }
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static int linktop_enable(struct ofono_modem *modem)
+{
+       struct linktop_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               return -EINVAL;
+
+       data->aux = open_device(modem, "Aux", "Aux: ");
+       if (data->aux == NULL) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+               return -EIO;
+       }
+
+       g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "AT+CFUN=4", NULL,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct linktop_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int linktop_disable(struct ofono_modem *modem)
+{
+       struct linktop_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       g_at_chat_send(data->aux, "AT+CFUN=4", NULL,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void linktop_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct linktop_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(data->aux, command, none_prefix,
+                                       set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void linktop_pre_sim(struct ofono_modem *modem)
+{
+       struct linktop_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->aux);
+       sim = ofono_sim_create(modem, 0, "atmodem", data->aux);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void linktop_post_sim(struct ofono_modem *modem)
+{
+       struct linktop_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->aux);
+       ofono_sms_create(modem, 0, "atmodem", data->aux);
+
+       ofono_radio_settings_create(modem, 0, "stemodem", data->aux);
+
+       gprs = ofono_gprs_create(modem, 0, "atmodem", data->aux);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+}
+
+static void linktop_post_online(struct ofono_modem *modem)
+{
+       struct linktop_data *data = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, 0, "atmodem", data->aux);
+
+       ofono_cbs_create(modem, 0, "atmodem", data->aux);
+       ofono_ussd_create(modem, 0, "atmodem", data->aux);
+
+       mw = ofono_message_waiting_create(modem);
+
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver linktop_driver = {
+       .name           = "linktop",
+       .probe          = linktop_probe,
+       .remove         = linktop_remove,
+       .enable         = linktop_enable,
+       .disable        = linktop_disable,
+       .set_online     = linktop_set_online,
+       .pre_sim        = linktop_pre_sim,
+       .post_sim       = linktop_post_sim,
+       .post_online    = linktop_post_online,
+};
+
+static int linktop_init(void)
+{
+       return ofono_modem_driver_register(&linktop_driver);
+}
+
+static void linktop_exit(void)
+{
+       ofono_modem_driver_unregister(&linktop_driver);
+}
+
+OFONO_PLUGIN_DEFINE(linktop, "Linktop Datacard modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, linktop_init, linktop_exit)
diff --git a/plugins/mbm.c b/plugins/mbm.c
new file mode 100644 (file)
index 0000000..32c7665
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/stk.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/radio-settings.h>
+#include <ofono/log.h>
+#include <ofono/location-reporting.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *cfun_prefix[] = { "+CFUN:", NULL };
+static const char *none_prefix[] = { NULL };
+
+enum mbm_variant {
+       MBM_GENERIC,
+       MBM_DELL_D5530,         /* OEM of F3507g */
+};
+
+struct mbm_data {
+       GAtChat *modem_port;
+       GAtChat *data_port;
+       gboolean have_sim;
+       struct ofono_location_reporting *lr;
+       enum mbm_variant variant;
+       struct at_util_sim_state_query *sim_state_query;
+};
+
+static int mbm_probe(struct ofono_modem *modem)
+{
+       struct mbm_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct mbm_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void mbm_remove(struct ofono_modem *modem)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup potential SIM state polling */
+       at_util_sim_state_query_free(data->sim_state_query);
+
+       g_at_chat_unref(data->data_port);
+       g_at_chat_unref(data->modem_port);
+
+       g_free(data);
+}
+
+static void mbm_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static void d5530_notify(GAtResult *result, gpointer user_data)
+{
+       DBG("D5530");
+}
+
+static void mbm_quirk_d5530(struct ofono_modem *modem)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       data->variant = MBM_DELL_D5530;
+
+       /* This Dell modem sends some unsolicated messages when it boots. */
+       /* Try to ignore them. */
+       g_at_chat_register(data->modem_port, "D5530", d5530_notify,
+                               FALSE, NULL, NULL);
+       g_at_chat_register(data->modem_port, "+GCAP:", d5530_notify,
+                               FALSE, NULL, NULL);
+}
+
+static void sim_state_cb(gboolean present, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       at_util_sim_state_query_free(data->sim_state_query);
+       data->sim_state_query = NULL;
+
+       data->have_sim = present;
+       ofono_modem_set_powered(modem, TRUE);
+}
+
+static void check_model(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct mbm_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       char const *model;
+
+       DBG("");
+
+       if (!ok)
+               goto done;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, NULL)) {
+               if (!g_at_result_iter_next_unquoted_string(&iter, &model))
+                       continue;
+
+               if (g_str_equal(model, "D5530"))
+                       mbm_quirk_d5530(modem);
+       }
+
+done:
+       data->sim_state_query = at_util_sim_state_query_new(data->modem_port,
+                                                               1, 5,
+                                                               sim_state_cb,
+                                                               modem);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       g_at_chat_send(data->modem_port, "AT+CGMM", NULL,
+                                       check_model, modem, NULL);
+}
+
+static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct mbm_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       int status;
+
+       DBG("%d", ok);
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE)
+               return;
+
+       g_at_result_iter_next_number(&iter, &status);
+
+       if (status != 4) {
+               g_at_chat_send(data->modem_port, "AT+CFUN=4", none_prefix,
+                               cfun_enable, modem, NULL);
+               return;
+       }
+
+       cfun_enable(TRUE, NULL, modem);
+}
+
+static void emrdy_notifier(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct mbm_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       int status;
+
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "*EMRDY:") == FALSE)
+               return;
+
+       g_at_result_iter_next_number(&iter, &status);
+
+       if (status != 1)
+               return;
+
+       g_at_chat_send(data->modem_port, "AT+CFUN?", cfun_prefix,
+                                       cfun_query, modem, NULL);
+}
+
+static void emrdy_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       DBG("%d", ok);
+
+       if (ok)
+               return;
+
+       /* On some MBM hardware the EMRDY cannot be queried, so if this fails
+        * we try to run CFUN? to check the state.  CFUN? will fail unless
+        * EMRDY: 1 has been sent, in which case the emrdy_notifier should be
+        * triggered eventually and we send CFUN? again.
+        */
+       g_at_chat_send(data->modem_port, "AT+CFUN?", cfun_prefix,
+                                       cfun_query, modem, NULL);
+};
+
+static GAtChat *create_port(const char *device)
+{
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GAtChat *chat;
+       GHashTable *options;
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               return NULL;
+
+       g_hash_table_insert(options, "Baud", "115200");
+
+       channel = g_at_tty_open(device, options);
+
+       g_hash_table_destroy(options);
+
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       return chat;
+}
+
+static int mbm_enable(struct ofono_modem *modem)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+       const char *modem_dev;
+       const char *data_dev;
+
+       DBG("%p", modem);
+
+       modem_dev = ofono_modem_get_string(modem, "ModemDevice");
+       data_dev = ofono_modem_get_string(modem, "DataDevice");
+
+       DBG("%s, %s", modem_dev, data_dev);
+
+       if (modem_dev == NULL || data_dev == NULL)
+               return -EINVAL;
+
+       data->modem_port = create_port(modem_dev);
+       if (data->modem_port == NULL)
+               return -EIO;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->modem_port, mbm_debug, "Modem: ");
+
+       data->data_port = create_port(data_dev);
+       if (data->data_port == NULL) {
+               g_at_chat_unref(data->modem_port);
+               data->modem_port = NULL;
+
+               return -EIO;
+       }
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->data_port, mbm_debug, "Data: ");
+
+       g_at_chat_register(data->modem_port, "*EMRDY:", emrdy_notifier,
+                                       FALSE, modem, NULL);
+
+       g_at_chat_send(data->modem_port, "AT&F E0 V1 X4 &C0 +CMEE=1", NULL,
+                                       NULL, NULL, NULL);
+       g_at_chat_send(data->data_port, "AT&F E0 V1 X4 &C0 +CMEE=1", NULL,
+                                       NULL, NULL, NULL);
+
+       g_at_chat_send(data->modem_port, "AT*E2CFUN=1", none_prefix,
+                                       NULL, NULL, NULL);
+       g_at_chat_send(data->modem_port, "AT*EMRDY?", none_prefix,
+                               emrdy_query, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->modem_port);
+       data->modem_port = NULL;
+
+       g_at_chat_unref(data->data_port);
+       data->data_port = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int mbm_disable(struct ofono_modem *modem)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (data->modem_port == NULL)
+               return 0;
+
+       g_at_chat_cancel_all(data->modem_port);
+       g_at_chat_unregister_all(data->modem_port);
+
+       g_at_chat_send(data->modem_port, "AT+CFUN=4", NULL,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void mbm_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+       GAtChat *chat = data->modem_port;
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(chat, command, none_prefix,
+                               set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void mbm_pre_sim(struct ofono_modem *modem)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->modem_port);
+       sim = ofono_sim_create(modem, OFONO_VENDOR_MBM,
+                                       "atmodem", data->modem_port);
+
+       if (data->have_sim && sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void mbm_post_sim(struct ofono_modem *modem)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_stk_create(modem, 0, "mbmmodem", data->modem_port);
+       ofono_radio_settings_create(modem, 0, "stemodem", data->modem_port);
+
+       ofono_sms_create(modem, 0, "atmodem", data->modem_port);
+}
+
+static void mbm_post_online(struct ofono_modem *modem)
+{
+       struct mbm_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+       const char *gps_dev;
+
+       DBG("%p", modem);
+
+       gps_dev = ofono_modem_get_string(modem, "GPSDevice");
+       if (gps_dev)
+               data->lr = ofono_location_reporting_create(modem, 0,
+                                       "mbmmodem", data->modem_port);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_MBM,
+                                       "atmodem", data->modem_port);
+
+       switch (data->variant) {
+       case MBM_GENERIC:
+               ofono_cbs_create(modem, 0, "atmodem", data->modem_port);
+               break;
+       case MBM_DELL_D5530:
+               /* DELL D5530 crashes when it processes CBSs */
+               break;
+       }
+
+       ofono_ussd_create(modem, 0, "atmodem", data->modem_port);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_MBM,
+                                       "atmodem", data->modem_port);
+       if (gprs == NULL)
+               return;
+
+       gc = ofono_gprs_context_create(modem, 0,
+                                       "mbmmodem", data->modem_port);
+       if (gc) {
+               ofono_gprs_context_set_type(gc,
+                                       OFONO_GPRS_CONTEXT_TYPE_INTERNET);
+               ofono_gprs_add_context(gprs, gc);
+       }
+
+       gc = ofono_gprs_context_create(modem, 0,
+                                       "atmodem", data->data_port);
+       if (gc) {
+               ofono_gprs_context_set_type(gc,
+                                       OFONO_GPRS_CONTEXT_TYPE_MMS);
+               ofono_gprs_add_context(gprs, gc);
+       }
+}
+
+static struct ofono_modem_driver mbm_driver = {
+       .name           = "mbm",
+       .probe          = mbm_probe,
+       .remove         = mbm_remove,
+       .enable         = mbm_enable,
+       .disable        = mbm_disable,
+       .set_online     = mbm_set_online,
+       .pre_sim        = mbm_pre_sim,
+       .post_sim       = mbm_post_sim,
+       .post_online    = mbm_post_online,
+};
+
+static int mbm_init(void)
+{
+       return ofono_modem_driver_register(&mbm_driver);
+}
+
+static void mbm_exit(void)
+{
+       ofono_modem_driver_unregister(&mbm_driver);
+}
+
+OFONO_PLUGIN_DEFINE(mbm, "Ericsson MBM modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, mbm_init, mbm_exit)
diff --git a/plugins/mbpi.c b/plugins/mbpi.c
new file mode 100644 (file)
index 0000000..309e1ed
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/modem.h>
+#include <ofono/gprs-provision.h>
+
+#ifndef MBPI_DATABASE
+#define MBPI_DATABASE  "/usr/share/mobile-broadband-provider-info/" \
+                                                       "serviceproviders.xml"
+#endif
+
+#include "mbpi.h"
+
+#define _(x) case x: return (#x)
+
+enum MBPI_ERROR {
+       MBPI_ERROR_DUPLICATE,
+};
+
+struct gsm_data {
+       const char *match_mcc;
+       const char *match_mnc;
+       GSList *apns;
+       gboolean match_found;
+       gboolean allow_duplicates;
+};
+
+struct cdma_data {
+       const char *match_sid;
+       char *provider_name;
+       gboolean match_found;
+};
+
+const char *mbpi_ap_type(enum ofono_gprs_context_type type)
+{
+       switch (type) {
+               _(OFONO_GPRS_CONTEXT_TYPE_ANY);
+               _(OFONO_GPRS_CONTEXT_TYPE_INTERNET);
+               _(OFONO_GPRS_CONTEXT_TYPE_MMS);
+               _(OFONO_GPRS_CONTEXT_TYPE_WAP);
+               _(OFONO_GPRS_CONTEXT_TYPE_IMS);
+       }
+
+       return "OFONO_GPRS_CONTEXT_TYPE_<UNKNOWN>";
+}
+
+static GQuark mbpi_error_quark(void)
+{
+       return g_quark_from_static_string("ofono-mbpi-error-quark");
+}
+
+void mbpi_ap_free(struct ofono_gprs_provision_data *ap)
+{
+       g_free(ap->name);
+       g_free(ap->apn);
+       g_free(ap->username);
+       g_free(ap->password);
+       g_free(ap->message_proxy);
+       g_free(ap->message_center);
+
+       g_free(ap);
+}
+
+static void mbpi_g_set_error(GMarkupParseContext *context, GError **error,
+                               GQuark domain, gint code, const gchar *fmt, ...)
+{
+       va_list ap;
+       gint line_number, char_number;
+
+       g_markup_parse_context_get_position(context, &line_number,
+                                               &char_number);
+       va_start(ap, fmt);
+
+       *error = g_error_new_valist(domain, code, fmt, ap);
+
+       va_end(ap);
+
+       g_prefix_error(error, "%s:%d ", MBPI_DATABASE, line_number);
+}
+
+static void text_handler(GMarkupParseContext *context,
+                               const gchar *text, gsize text_len,
+                               gpointer userdata, GError **error)
+{
+       char **string = userdata;
+
+       *string = g_strndup(text, text_len);
+}
+
+static const GMarkupParser text_parser = {
+       NULL,
+       NULL,
+       text_handler,
+       NULL,
+       NULL,
+};
+
+static void usage_start(GMarkupParseContext *context,
+                       const gchar **attribute_names,
+                       const gchar **attribute_values,
+                       enum ofono_gprs_context_type *type, GError **error)
+{
+       const char *text = NULL;
+       int i;
+
+       for (i = 0; attribute_names[i]; i++)
+               if (g_str_equal(attribute_names[i], "type") == TRUE)
+                       text = attribute_values[i];
+
+       if (text == NULL) {
+               mbpi_g_set_error(context, error, G_MARKUP_ERROR,
+                                       G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+                                       "Missing attribute: type");
+               return;
+       }
+
+       if (strcmp(text, "internet") == 0)
+               *type = OFONO_GPRS_CONTEXT_TYPE_INTERNET;
+       else if (strcmp(text, "mms") == 0)
+               *type = OFONO_GPRS_CONTEXT_TYPE_MMS;
+       else if (strcmp(text, "wap") == 0)
+               *type = OFONO_GPRS_CONTEXT_TYPE_WAP;
+       else
+               mbpi_g_set_error(context, error, G_MARKUP_ERROR,
+                                       G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                                       "Unknown usage attribute: %s", text);
+}
+
+static void apn_start(GMarkupParseContext *context, const gchar *element_name,
+                       const gchar **attribute_names,
+                       const gchar **attribute_values,
+                       gpointer userdata, GError **error)
+{
+       struct ofono_gprs_provision_data *apn = userdata;
+
+       if (g_str_equal(element_name, "name"))
+               g_markup_parse_context_push(context, &text_parser, &apn->name);
+       else if (g_str_equal(element_name, "username"))
+               g_markup_parse_context_push(context, &text_parser,
+                                               &apn->username);
+       else if (g_str_equal(element_name, "password"))
+               g_markup_parse_context_push(context, &text_parser,
+                                               &apn->password);
+       else if (g_str_equal(element_name, "usage"))
+               usage_start(context, attribute_names, attribute_values,
+                               &apn->type, error);
+}
+
+static void apn_end(GMarkupParseContext *context, const gchar *element_name,
+                       gpointer userdata, GError **error)
+{
+       if (g_str_equal(element_name, "name") ||
+                       g_str_equal(element_name, "username") ||
+                       g_str_equal(element_name, "password"))
+               g_markup_parse_context_pop(context);
+}
+
+static void apn_error(GMarkupParseContext *context, GError *error,
+                       gpointer userdata)
+{
+       /*
+        * Note that even if the error happened in a subparser, this will
+        * be called.  So we always perform cleanup of the allocated
+        * provision data
+        */
+       mbpi_ap_free(userdata);
+}
+
+static const GMarkupParser apn_parser = {
+       apn_start,
+       apn_end,
+       NULL,
+       NULL,
+       apn_error,
+};
+
+static const GMarkupParser skip_parser = {
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+};
+
+static void network_id_handler(GMarkupParseContext *context,
+                               struct gsm_data *gsm,
+                               const gchar **attribute_names,
+                               const gchar **attribute_values,
+                               GError **error)
+{
+       const char *mcc = NULL, *mnc = NULL;
+       int i;
+
+       for (i = 0; attribute_names[i]; i++) {
+               if (g_str_equal(attribute_names[i], "mcc") == TRUE)
+                       mcc = attribute_values[i];
+               if (g_str_equal(attribute_names[i], "mnc") == TRUE)
+                       mnc = attribute_values[i];
+       }
+
+       if (mcc == NULL) {
+               mbpi_g_set_error(context, error, G_MARKUP_ERROR,
+                                       G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+                                       "Missing attribute: mcc");
+               return;
+       }
+
+       if (mnc == NULL) {
+               mbpi_g_set_error(context, error, G_MARKUP_ERROR,
+                                       G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+                                       "Missing attribute: mnc");
+               return;
+       }
+
+       if (g_str_equal(mcc, gsm->match_mcc) &&
+                       g_str_equal(mnc, gsm->match_mnc))
+               gsm->match_found = TRUE;
+}
+
+static void apn_handler(GMarkupParseContext *context, struct gsm_data *gsm,
+                       const gchar **attribute_names,
+                       const gchar **attribute_values,
+                       GError **error)
+{
+       struct ofono_gprs_provision_data *ap;
+       const char *apn;
+       int i;
+
+       if (gsm->match_found == FALSE) {
+               g_markup_parse_context_push(context, &skip_parser, NULL);
+               return;
+       }
+
+       for (i = 0, apn = NULL; attribute_names[i]; i++) {
+               if (g_str_equal(attribute_names[i], "value") == FALSE)
+                       continue;
+
+               apn = attribute_values[i];
+               break;
+       }
+
+       if (apn == NULL) {
+               mbpi_g_set_error(context, error, G_MARKUP_ERROR,
+                                       G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+                                       "APN attribute missing");
+               return;
+       }
+
+       ap = g_new0(struct ofono_gprs_provision_data, 1);
+       ap->apn = g_strdup(apn);
+       ap->type = OFONO_GPRS_CONTEXT_TYPE_INTERNET;
+       ap->proto = OFONO_GPRS_PROTO_IP;
+
+       g_markup_parse_context_push(context, &apn_parser, ap);
+}
+
+static void sid_handler(GMarkupParseContext *context,
+                               struct cdma_data *cdma,
+                               const gchar **attribute_names,
+                               const gchar **attribute_values,
+                               GError **error)
+{
+       const char *sid = NULL;
+       int i;
+
+       for (i = 0; attribute_names[i]; i++) {
+               if (g_str_equal(attribute_names[i], "value") == FALSE)
+                       continue;
+
+               sid = attribute_values[i];
+               break;
+       }
+
+       if (sid == NULL) {
+               mbpi_g_set_error(context, error, G_MARKUP_ERROR,
+                                       G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+                                       "Missing attribute: sid");
+               return;
+       }
+
+       if (g_str_equal(sid, cdma->match_sid))
+               cdma->match_found = TRUE;
+}
+
+static void gsm_start(GMarkupParseContext *context, const gchar *element_name,
+                       const gchar **attribute_names,
+                       const gchar **attribute_values,
+                       gpointer userdata, GError **error)
+{
+       if (g_str_equal(element_name, "network-id")) {
+               struct gsm_data *gsm = userdata;
+
+               /*
+                * For entries with multiple network-id elements, don't bother
+                * searching if we already have a match
+                */
+               if (gsm->match_found == TRUE)
+                       return;
+
+               network_id_handler(context, userdata, attribute_names,
+                                       attribute_values, error);
+       } else if (g_str_equal(element_name, "apn"))
+               apn_handler(context, userdata, attribute_names,
+                               attribute_values, error);
+}
+
+static void gsm_end(GMarkupParseContext *context, const gchar *element_name,
+                       gpointer userdata, GError **error)
+{
+       struct gsm_data *gsm;
+       struct ofono_gprs_provision_data *ap;
+
+       if (!g_str_equal(element_name, "apn"))
+               return;
+
+       gsm = userdata;
+
+       ap = g_markup_parse_context_pop(context);
+       if (ap == NULL)
+               return;
+
+       if (gsm->allow_duplicates == FALSE) {
+               GSList *l;
+
+               for (l = gsm->apns; l; l = l->next) {
+                       struct ofono_gprs_provision_data *pd = l->data;
+
+                       if (pd->type != ap->type)
+                               continue;
+
+                       mbpi_g_set_error(context, error, mbpi_error_quark(),
+                                               MBPI_ERROR_DUPLICATE,
+                                               "Duplicate context detected");
+
+                       mbpi_ap_free(ap);
+                       return;
+               }
+       }
+
+       gsm->apns = g_slist_append(gsm->apns, ap);
+}
+
+static const GMarkupParser gsm_parser = {
+       gsm_start,
+       gsm_end,
+       NULL,
+       NULL,
+       NULL,
+};
+
+static void cdma_start(GMarkupParseContext *context, const gchar *element_name,
+                       const gchar **attribute_names,
+                       const gchar **attribute_values,
+                       gpointer userdata, GError **error)
+{
+       if (g_str_equal(element_name, "sid")) {
+               struct cdma_data *cdma = userdata;
+               /*
+                * For entries with multiple sid elements, don't bother
+                * searching if we already have a match
+                */
+               if (cdma->match_found == TRUE)
+                       return;
+
+               sid_handler(context, cdma, attribute_names, attribute_values,
+                               error);
+       }
+}
+
+static const GMarkupParser cdma_parser = {
+       cdma_start,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+};
+
+static void provider_start(GMarkupParseContext *context,
+                               const gchar *element_name,
+                               const gchar **attribute_names,
+                               const gchar **attribute_values,
+                               gpointer userdata, GError **error)
+{
+       if (g_str_equal(element_name, "name")) {
+               struct cdma_data *cdma = userdata;
+
+               g_free(cdma->provider_name);
+               cdma->provider_name = NULL;
+               g_markup_parse_context_push(context, &text_parser,
+                                               &cdma->provider_name);
+       } else if (g_str_equal(element_name, "gsm"))
+               g_markup_parse_context_push(context, &skip_parser, NULL);
+       else if (g_str_equal(element_name, "cdma"))
+               g_markup_parse_context_push(context, &cdma_parser, userdata);
+}
+
+static void provider_end(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       gpointer userdata, GError **error)
+{
+       if (g_str_equal(element_name, "name") ||
+                               g_str_equal(element_name, "gsm") ||
+                               g_str_equal(element_name, "cdma"))
+               g_markup_parse_context_pop(context);
+
+}
+
+static const GMarkupParser provider_parser = {
+       provider_start,
+       provider_end,
+       NULL,
+       NULL,
+       NULL,
+};
+
+static void toplevel_gsm_start(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       const gchar **atribute_names,
+                                       const gchar **attribute_values,
+                                       gpointer userdata, GError **error)
+{
+       struct gsm_data *gsm = userdata;
+
+       if (g_str_equal(element_name, "gsm")) {
+               gsm->match_found = FALSE;
+               g_markup_parse_context_push(context, &gsm_parser, gsm);
+       } else if (g_str_equal(element_name, "cdma"))
+               g_markup_parse_context_push(context, &skip_parser, NULL);
+}
+
+static void toplevel_gsm_end(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       gpointer userdata, GError **error)
+{
+       if (g_str_equal(element_name, "gsm") ||
+                       g_str_equal(element_name, "cdma"))
+               g_markup_parse_context_pop(context);
+}
+
+static const GMarkupParser toplevel_gsm_parser = {
+       toplevel_gsm_start,
+       toplevel_gsm_end,
+       NULL,
+       NULL,
+       NULL,
+};
+
+static void toplevel_cdma_start(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       const gchar **atribute_names,
+                                       const gchar **attribute_values,
+                                       gpointer userdata, GError **error)
+{
+       struct cdma_data *cdma = userdata;
+
+       if (g_str_equal(element_name, "provider") == FALSE)
+               return;
+
+       if (cdma->match_found == TRUE)
+               g_markup_parse_context_push(context, &skip_parser, NULL);
+       else
+               g_markup_parse_context_push(context, &provider_parser, cdma);
+}
+
+static void toplevel_cdma_end(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       gpointer userdata, GError **error)
+{
+       if (g_str_equal(element_name, "provider"))
+               g_markup_parse_context_pop(context);
+}
+
+static const GMarkupParser toplevel_cdma_parser = {
+       toplevel_cdma_start,
+       toplevel_cdma_end,
+       NULL,
+       NULL,
+       NULL,
+};
+
+static gboolean mbpi_parse(const GMarkupParser *parser, gpointer userdata,
+                               GError **error)
+{
+       struct stat st;
+       char *db;
+       int fd;
+       GMarkupParseContext *context;
+       gboolean ret;
+
+       fd = open(MBPI_DATABASE, O_RDONLY);
+       if (fd < 0) {
+               g_set_error(error, G_FILE_ERROR,
+                               g_file_error_from_errno(errno),
+                               "open(%s) failed: %s", MBPI_DATABASE,
+                               g_strerror(errno));
+               return FALSE;
+       }
+
+       if (fstat(fd, &st) < 0) {
+               close(fd);
+               g_set_error(error, G_FILE_ERROR,
+                               g_file_error_from_errno(errno),
+                               "fstat(%s) failed: %s", MBPI_DATABASE,
+                               g_strerror(errno));
+               return FALSE;
+       }
+
+       db = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+       if (db == MAP_FAILED) {
+               close(fd);
+               g_set_error(error, G_FILE_ERROR,
+                               g_file_error_from_errno(errno),
+                               "mmap(%s) failed: %s", MBPI_DATABASE,
+                               g_strerror(errno));
+               return FALSE;
+       }
+
+       context = g_markup_parse_context_new(parser,
+                                               G_MARKUP_TREAT_CDATA_AS_TEXT,
+                                               userdata, NULL);
+
+       ret = g_markup_parse_context_parse(context, db, st.st_size, error);
+
+       if (ret == TRUE)
+               g_markup_parse_context_end_parse(context, error);
+
+       munmap(db, st.st_size);
+       close(fd);
+       g_markup_parse_context_free(context);
+
+       return ret;
+}
+
+GSList *mbpi_lookup_apn(const char *mcc, const char *mnc,
+                       gboolean allow_duplicates, GError **error)
+{
+       struct gsm_data gsm;
+       GSList *l;
+
+       memset(&gsm, 0, sizeof(gsm));
+       gsm.match_mcc = mcc;
+       gsm.match_mnc = mnc;
+       gsm.allow_duplicates = allow_duplicates;
+
+       if (mbpi_parse(&toplevel_gsm_parser, &gsm, error) == FALSE) {
+               for (l = gsm.apns; l; l = l->next)
+                       mbpi_ap_free(l->data);
+
+               g_slist_free(gsm.apns);
+               gsm.apns = NULL;
+       }
+
+       return gsm.apns;
+}
+
+char *mbpi_lookup_cdma_provider_name(const char *sid, GError **error)
+{
+       struct cdma_data cdma;
+
+       memset(&cdma, 0, sizeof(cdma));
+       cdma.match_sid = sid;
+
+       if (mbpi_parse(&toplevel_cdma_parser, &cdma, error) == FALSE) {
+               g_free(cdma.provider_name);
+               cdma.provider_name = NULL;
+       }
+
+       return cdma.provider_name;
+}
diff --git a/plugins/mbpi.h b/plugins/mbpi.h
new file mode 100644 (file)
index 0000000..64b7ea5
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+const char *mbpi_ap_type(enum ofono_gprs_context_type type);
+
+void mbpi_ap_free(struct ofono_gprs_provision_data *data);
+
+GSList *mbpi_lookup_apn(const char *mcc, const char *mnc,
+                       gboolean allow_duplicates, GError **error);
+
+char *mbpi_lookup_cdma_provider_name(const char *sid, GError **error);
diff --git a/plugins/n900.c b/plugins/n900.c
new file mode 100644 (file)
index 0000000..44e2e75
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <gisi/modem.h>
+#include <gisi/netlink.h>
+#include <gisi/client.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/phonebook.h>
+#include <ofono/netreg.h>
+#include <ofono/voicecall.h>
+#include <ofono/sms.h>
+#include <ofono/cbs.h>
+#include <ofono/sim.h>
+#include <ofono/ussd.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-meter.h>
+#include <ofono/radio-settings.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/audio-settings.h>
+
+#include "drivers/isimodem/isimodem.h"
+#include "drivers/isimodem/isiutil.h"
+#include "drivers/isimodem/infoserver.h"
+#include "drivers/isimodem/mtc.h"
+#include "drivers/isimodem/debug.h"
+
+#include "nokia-gpio.h"
+
+struct isi_data {
+       const char *ifname;
+       GIsiModem *modem;
+       GIsiClient *client;
+       struct isi_infoserver *infoserver;
+       ofono_bool_t enabled;
+       ofono_bool_t online;
+       ofono_bool_t reported;
+       enum power_state power_state;
+       int mtc_state;
+       guint timeout;
+       struct isi_cb_data *online_cbd;
+};
+
+static void mtc_power_off(struct isi_data *isi);
+static gboolean mtc_power_off_poll(gpointer user);
+
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
+{
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       mtc_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static void report_powered(struct ofono_modem *modem, struct isi_data *isi,
+                               ofono_bool_t powered)
+{
+       if (powered == isi->reported)
+               return;
+
+       DBG("%s", powered ? "Powered on"
+               : isi->enabled ? "Reset"
+               : "Powered off");
+
+       isi->reported = powered;
+       ofono_modem_set_powered(modem, powered);
+}
+
+static void report_online(struct isi_data *isi, ofono_bool_t online)
+{
+       struct isi_cb_data *cbd = isi->online_cbd;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       isi->online_cbd = NULL;
+
+       if (isi->online == online)
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void set_power_by_mtc_state(struct ofono_modem *modem,
+                                       struct isi_data *isi, int mtc_state)
+{
+       isi->mtc_state = mtc_state;
+
+       if (isi->online_cbd)
+               report_online(isi, mtc_state == MTC_NORMAL);
+
+       switch (mtc_state) {
+       case MTC_STATE_NONE:
+       case MTC_POWER_OFF:
+       case MTC_CHARGING:
+       case MTC_SELFTEST_FAIL:
+               report_powered(modem, isi, FALSE);
+               break;
+
+       case MTC_RF_INACTIVE:
+       case MTC_NORMAL:
+       default:
+               report_powered(modem, isi, TRUE);
+       }
+}
+
+static void mtc_state_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t action;
+       uint8_t state;
+
+       if (g_isi_msg_error(msg) < 0)
+               return;
+
+       if (g_isi_msg_id(msg) != MTC_STATE_INFO_IND)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &state) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &action))
+               return;
+
+       if (action == MTC_START) {
+               DBG("target modem state: %s (0x%02X)",
+                       mtc_modem_state_name(state), state);
+
+               if (state == MTC_POWER_OFF) {
+                       isi->power_state = POWER_STATE_OFF_STARTED;
+                       mtc_power_off_poll(isi);
+               }
+       } else if (action == MTC_READY) {
+               DBG("current modem state: %s (0x%02X)",
+                       mtc_modem_state_name(state), state);
+
+               set_power_by_mtc_state(modem, isi, state);
+       }
+}
+
+static void mtc_startup_synq_cb(const GIsiMessage *msg, void *data)
+{
+       check_response_status(msg, MTC_STARTUP_SYNQ_RESP);
+}
+
+static void mtc_startup_synq(struct isi_data *isi)
+{
+       const uint8_t msg[] = {
+               MTC_STARTUP_SYNQ_REQ,
+               0, 0,
+       };
+
+       g_isi_client_send(isi->client, msg, sizeof(msg),
+                               mtc_startup_synq_cb, NULL, NULL);
+}
+
+static void mtc_query_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t current;
+       uint8_t target;
+
+       if (!check_response_status(msg, MTC_STATE_QUERY_RESP))
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &current) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &target))
+               return;
+
+       DBG("Modem state: current=%s (0x%02X) target=%s (0x%02X)",
+               mtc_modem_state_name(current), current,
+               mtc_modem_state_name(target), target);
+
+       set_power_by_mtc_state(modem, isi, current);
+
+       mtc_startup_synq(isi);
+}
+
+static void mtc_state_query(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       const uint8_t msg[] = {
+               MTC_STATE_QUERY_REQ,
+               0, 0,
+       };
+
+       if (!isi)
+               return;
+
+       g_isi_client_send(isi->client, msg, sizeof(msg),
+                               mtc_query_cb, modem, NULL);
+}
+
+static void mtc_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       if (!g_isi_msg_error(msg) < 0)
+               return;
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_ind_subscribe(isi->client, MTC_STATE_INFO_IND,
+                                       mtc_state_ind_cb, modem);
+
+       mtc_state_query(modem);
+}
+
+static void mtc_shutdown_sync(struct isi_data *isi)
+{
+       const uint8_t msg[] = {
+               MTC_SHUTDOWN_SYNC_REQ,
+               0, 0,
+       };
+
+       g_isi_client_send(isi->client, msg, sizeof(msg), NULL, NULL, NULL);
+}
+
+
+static gboolean mtc_power_off_poll(gpointer user)
+{
+       struct isi_data *isi = user;
+
+       isi->timeout = 0;
+
+       if (isi->power_state == POWER_STATE_ON_STARTED
+                       || isi->power_state == POWER_STATE_OFF
+                       || isi->power_state == POWER_STATE_OFF_WAITING)
+               return FALSE;
+
+       mtc_shutdown_sync(isi);
+
+       isi->timeout = g_timeout_add(200, mtc_power_off_poll, user);
+
+       return FALSE;
+}
+
+static void mtc_power_off_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_data *isi = data;
+
+       if (!check_response_status(msg, MTC_POWER_OFF_RESP)) {
+
+               if (isi->power_state == POWER_STATE_OFF_STARTED)
+                       mtc_power_off(isi);
+               return;
+       }
+
+       /* power off poll is started by mtc_state_ind_cb() */
+}
+
+static void mtc_power_off(struct isi_data *isi)
+{
+       const uint8_t msg[] = {
+               MTC_POWER_OFF_REQ,
+               0, 0,
+       };
+
+       g_isi_client_send(isi->client, msg, sizeof(msg),
+                               mtc_power_off_cb, isi, NULL);
+}
+
+static void n900_power_cb(enum power_state state, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("power state %s", gpio_power_state_name(state));
+
+       isi->power_state = state;
+
+       if (state == POWER_STATE_OFF_STARTED)
+               mtc_power_off(isi);
+       else if (isi->timeout)
+               g_source_remove(isi->timeout);
+
+       if (state == POWER_STATE_ON)
+               g_isi_client_verify(isi->client, mtc_reachable_cb, modem, NULL);
+       else if (isi->enabled)
+               /* If enabled, report modem crash */
+               set_power_by_mtc_state(modem, isi, MTC_STATE_NONE);
+       else if (state == POWER_STATE_OFF || state == POWER_STATE_ON_FAILED)
+               /* If being disabled, report powered off only when safe */
+               report_powered(modem, isi, 0);
+       else
+               isi->mtc_state = MTC_STATE_NONE;
+}
+
+static int n900_probe(struct ofono_modem *modem)
+{
+       char const *ifname = ofono_modem_get_string(modem, "Interface");
+       unsigned address = ofono_modem_get_integer(modem, "Address");
+
+       struct isi_data *isi = NULL;
+       GIsiModem *isimodem;
+       GIsiClient *client;
+
+       if (!ifname)
+               return -EINVAL;
+
+       DBG("(%p) with %s", modem, ifname);
+
+       isimodem = g_isi_modem_create_by_name(ifname);
+       if (isimodem == NULL) {
+               DBG("Interface=%s: %s", ifname, strerror(errno));
+               return -errno;
+       }
+
+       g_isi_modem_set_userdata(isimodem, modem);
+       g_isi_modem_set_flags(isimodem, GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE);
+
+       if (getenv("OFONO_ISI_DEBUG"))
+               g_isi_modem_set_debug(isimodem, ofono_debug);
+
+       if (getenv("OFONO_ISI_TRACE"))
+               g_isi_modem_set_trace(isimodem, isi_trace);
+
+       if (gpio_probe(isimodem, address, n900_power_cb, modem) != 0) {
+               DBG("gpio for %s: %s", ifname, strerror(errno));
+               goto error;
+       }
+
+       isi = g_try_new0(struct isi_data, 1);
+       if (isi == NULL) {
+               errno = ENOMEM;
+               goto error;
+       }
+
+       client = g_isi_client_create(isimodem, PN_MTC);
+       if (!client)
+               goto error;
+
+       isi->modem = isimodem;
+       isi->ifname = ifname;
+       isi->client = client;
+
+       ofono_modem_set_data(modem, isi);
+       return 0;
+
+error:
+       g_isi_modem_destroy(isimodem);
+       gpio_remove(modem);
+       g_free(isi);
+
+       return -errno;
+}
+
+static void n900_remove(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       if (!isi)
+               return;
+
+       gpio_remove(modem);
+
+       if (isi->timeout)
+               g_source_remove(isi->timeout);
+
+       g_isi_client_destroy(isi->client);
+       g_isi_modem_destroy(isi->modem);
+       g_free(isi);
+}
+
+static void mtc_state_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_modem *modem = cbd->user;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t cause;
+
+       if (!check_response_status(msg, MTC_STATE_RESP))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &cause))
+               goto error;
+
+       DBG("MTC cause: %s (0x%02X)", mtc_isi_cause_name(cause), cause);
+
+       if (cause == MTC_OK) {
+               isi->online_cbd = cbd;
+               return;
+       }
+
+       if (cause == MTC_ALREADY_ACTIVE) {
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+               g_free(cbd);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+       g_free(cbd);
+}
+
+static void n900_set_online(struct ofono_modem *modem,
+                                       ofono_bool_t online,
+                                       ofono_modem_online_cb_t cb, void *data)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       struct isi_cb_data *cbd = isi_cb_data_new(modem, cb, data);
+       const uint8_t req[] = {
+               MTC_STATE_REQ,
+               online ? MTC_NORMAL : MTC_RF_INACTIVE, 0
+       };
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       if (cbd == NULL || isi == NULL)
+               goto error;
+
+       if (isi->power_state != POWER_STATE_ON)
+               goto error;
+
+       if (isi->mtc_state == MTC_SELFTEST_FAIL)
+               goto error;
+
+       if (g_isi_client_send_with_timeout(isi->client, req, sizeof(req),
+                               MTC_STATE_REQ_TIMEOUT,
+                               mtc_state_cb, cbd, NULL)) {
+               isi->online = online;
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void n900_pre_sim(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       isi->infoserver = isi_infoserver_create(modem, isi->modem);
+
+       ofono_sim_create(modem, 0, "isimodem", isi->modem);
+       ofono_devinfo_create(modem, 0, "isimodem", isi->modem);
+       ofono_voicecall_create(modem, 0, "isimodem", isi->modem);
+       ofono_audio_settings_create(modem, 0, "isimodem", isi->modem);
+}
+
+static void n900_post_sim(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_phonebook_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_forwarding_create(modem, 0, "isimodem", isi->modem);
+       ofono_radio_settings_create(modem, 0, "isimodem", isi->modem);
+}
+
+static void n900_post_online(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_netreg_create(modem, 0, "isimodem", isi->modem);
+       ofono_sms_create(modem, 0, "isimodem", isi->modem);
+       ofono_cbs_create(modem, 0, "isimodem", isi->modem);
+       ofono_ussd_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_settings_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_barring_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_meter_create(modem, 0, "isimodem", isi->modem);
+       ofono_gprs_create(modem, 0, "isimodem", isi->modem);
+}
+
+static int n900_enable(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("modem=%p with %p", modem, isi ? isi->ifname : NULL);
+
+       isi->enabled = TRUE;
+
+       return gpio_enable(modem);
+}
+
+static int n900_disable(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("modem=%p with %p", modem, isi ? isi->ifname : NULL);
+
+       isi->enabled = FALSE;
+
+       return gpio_disable(modem);
+}
+
+static struct ofono_modem_driver n900_driver = {
+       .name = "n900",
+       .probe = n900_probe,
+       .remove = n900_remove,
+       .enable = n900_enable,
+       .disable = n900_disable,
+       .set_online = n900_set_online,
+       .pre_sim = n900_pre_sim,
+       .post_sim = n900_post_sim,
+       .post_online = n900_post_online,
+};
+
+static int n900_init(void)
+{
+       return ofono_modem_driver_register(&n900_driver);
+}
+
+static void n900_exit(void)
+{
+       ofono_modem_driver_unregister(&n900_driver);
+}
+
+OFONO_PLUGIN_DEFINE(n900, "Nokia N900 modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, n900_init, n900_exit)
diff --git a/plugins/nokia-gpio.c b/plugins/nokia-gpio.c
new file mode 100644 (file)
index 0000000..57aad8d
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <gisi/netlink.h>
+#include <glib.h>
+
+#include <ofono/log.h>
+
+#include <drivers/isimodem/debug.h>
+#include "nokia-gpio.h"
+
+#define GPIO_SWITCH    "/sys/devices/platform/gpio-switch"
+#define DEV_CMT                "/dev/cmt"
+
+enum rapu_type {
+       RAPU_TYPE_1,
+       RAPU_TYPE_2,
+};
+
+enum retry_count {
+       RETRY_COUNT_RESET = 5,
+       RETRY_COUNT_POWER_ON = 10,
+};
+
+enum phonet_link {
+       PHONET_LINK_NONE = 0,
+       PHONET_LINK_DOWN,
+       PHONET_LINK_UP,
+};
+
+enum power_event {
+       POWER_EVENT_PHONET_LINK_UP = 1,
+       POWER_EVENT_PHONET_LINK_DOWN,
+       POWER_EVENT_ON,
+       POWER_EVENT_ON_TIMEOUT,
+       POWER_EVENT_REBOOT_TIMEOUT,
+       POWER_EVENT_OFF,
+       POWER_EVENT_OFF_IMMEDIATELY,
+       POWER_EVENT_OFF_TIMEOUT,
+       POWER_EVENT_OFF_COMPLETE,
+};
+
+struct gpio_data {
+       GIsiPhonetNetlink *link;
+       gpio_finished_cb_t callback;
+       void *data;
+
+       enum power_state state;
+       enum phonet_link current;
+       enum phonet_link target;
+       enum power_event timer_event;
+       enum rapu_type rapu;
+
+       guint timeout_source;
+
+       unsigned retry_count;
+       unsigned have_gpio_switch:1;
+       unsigned have_cmt_en:1;
+       unsigned have_cmt_rst_rq:1;
+       unsigned have_cmt_rst:1;
+       unsigned have_cmt_bsi:1;
+       unsigned have_cmt_apeslpx:1;
+       unsigned reset_in_progress:1;
+       unsigned startup_in_progress:1;
+};
+
+static struct gpio_data self;
+
+#define _(X) case X: return #X
+
+static inline char const *gpio_power_event_name(enum power_event value)
+{
+       switch (value) {
+               _(POWER_EVENT_PHONET_LINK_UP);
+               _(POWER_EVENT_PHONET_LINK_DOWN);
+               _(POWER_EVENT_ON);
+               _(POWER_EVENT_ON_TIMEOUT);
+               _(POWER_EVENT_REBOOT_TIMEOUT);
+               _(POWER_EVENT_OFF);
+               _(POWER_EVENT_OFF_IMMEDIATELY);
+               _(POWER_EVENT_OFF_TIMEOUT);
+               _(POWER_EVENT_OFF_COMPLETE);
+       }
+       return "<UNKNOWN>";
+}
+
+char const *gpio_power_state_name(enum power_state value)
+{
+       switch (value) {
+               _(POWER_STATE_NONE);
+               _(POWER_STATE_ON_STARTED);
+               _(POWER_STATE_ON);
+               _(POWER_STATE_ON_RESET);
+               _(POWER_STATE_ON_FAILED);
+               _(POWER_STATE_OFF_STARTED);
+               _(POWER_STATE_OFF_WAITING);
+               _(POWER_STATE_OFF);
+       }
+       return "<UNKNOWN>";
+}
+
+#undef _
+
+static void gpio_power_state_machine(enum power_event event);
+static void gpio_power_set_state(enum power_state new_state);
+
+static int file_exists(char const *filename)
+{
+       struct stat st;
+
+       return stat(filename, &st) == 0;
+}
+
+static int dir_exists(char const *filename)
+{
+       struct stat st;
+
+       return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+static int file_write(char const *filename, char const *output)
+{
+       FILE *f;
+
+       f = fopen(filename, "r+");
+       if (f == NULL) {
+               DBG("%s: %s (%d)", filename, strerror(errno), errno);
+               return -1;
+       }
+
+       fputs(output, f);
+
+       return fclose(f);
+}
+
+static int gpio_write(char *line, int value)
+{
+       char filename[256];
+
+       DBG("(\"%s\", \"%s\")", line, value ? "active" : "inactive");
+
+       if (self.have_gpio_switch) {
+               snprintf(filename, sizeof filename, "%s/%s/%s",
+                               GPIO_SWITCH, line, "state");
+               return file_write(filename, value ? "active" : "inactive");
+       } else {
+               snprintf(filename, sizeof filename, "%s/%s/%s",
+                               DEV_CMT, line, "value");
+               return file_write(filename, value ? "1" : "0");
+       }
+}
+
+#define GPIO_WRITE(line, value) \
+       (self.have_ ## line ? gpio_write(#line, value) : 0)
+
+static int gpio_line_probe(char const *line)
+{
+       char filename[256];
+       int result;
+
+       if (self.have_gpio_switch)
+               snprintf(filename, sizeof filename,
+                               "%s/%s/state", GPIO_SWITCH, line);
+       else
+               snprintf(filename, sizeof filename,
+                               "%s/%s/value", DEV_CMT, line);
+
+       result = file_exists(filename);
+
+       DBG("%s: %s", line, result ? "found" : "not found");
+
+       return result;
+}
+
+/*
+ * Modem start up function
+ *
+ * Sets all lines down and leaves "power key" pressed (power key must
+ * be released after some time)
+ */
+static void gpio_start_modem_power_on(void)
+{
+       DBG("");
+
+       if (self.startup_in_progress)
+               return;
+       self.startup_in_progress = 1;
+
+       GPIO_WRITE(cmt_apeslpx, 0);     /* skip flash mode */
+       GPIO_WRITE(cmt_rst_rq, 0);      /* prevent current drain */
+
+       switch (self.rapu) {
+       case RAPU_TYPE_2:
+               GPIO_WRITE(cmt_en, 0);
+               /* 15 ms needed for ASIC poweroff */
+               usleep(20000);
+               GPIO_WRITE(cmt_en, 1);
+               break;
+
+       case RAPU_TYPE_1:
+               GPIO_WRITE(cmt_en, 0);
+               GPIO_WRITE(cmt_bsi, 0); /* toggle BSI visible to modem */
+               GPIO_WRITE(cmt_rst, 0); /* Assert PURX */
+               GPIO_WRITE(cmt_en, 1);  /* Press "power key" */
+               GPIO_WRITE(cmt_rst, 1); /* Release CMT to boot */
+               break;
+       }
+
+       GPIO_WRITE(cmt_rst_rq, 1);
+}
+
+static void gpio_finish_modem_power_on(void)
+{
+       DBG("");
+
+       if (!self.startup_in_progress)
+               return;
+
+       self.startup_in_progress = 0;
+
+       switch (self.rapu) {
+       case RAPU_TYPE_2:
+               break;
+
+       case RAPU_TYPE_1:
+               GPIO_WRITE(cmt_en, 0);  /* release "power key" */
+               break;
+       }
+}
+
+static void gpio_start_modem_reset(void)
+{
+       DBG("");
+
+       if (self.reset_in_progress)
+               return;
+       self.reset_in_progress = 1;
+
+       if (self.have_cmt_rst_rq) {
+               GPIO_WRITE(cmt_rst_rq, 0); /* Just in case */
+               GPIO_WRITE(cmt_rst_rq, 1);
+       } else {
+               gpio_start_modem_power_on();
+       }
+}
+
+static void gpio_finish_modem_reset(void)
+{
+       DBG("");
+
+       if (!self.reset_in_progress)
+               return;
+
+       self.reset_in_progress = 0;
+       gpio_finish_modem_power_on();
+}
+
+static void gpio_finish_modem_power_off(void)
+{
+       DBG("");
+
+       if (self.reset_in_progress)
+               gpio_finish_modem_reset();
+
+       if (self.startup_in_progress)
+               gpio_finish_modem_power_on();
+
+       GPIO_WRITE(cmt_apeslpx, 0);     /* skip flash mode */
+       GPIO_WRITE(cmt_rst_rq, 0);      /* prevent current drain */
+
+       switch (self.rapu) {
+       case RAPU_TYPE_2:
+               GPIO_WRITE(cmt_en, 0);  /* Power off */
+               break;
+
+       case RAPU_TYPE_1:
+               GPIO_WRITE(cmt_en, 0);  /* release "power key" */
+               GPIO_WRITE(cmt_rst, 0); /* force modem to reset state */
+               GPIO_WRITE(cmt_rst, 1); /* release modem to be powered
+                                                       off by bootloader */
+               break;
+       }
+}
+
+static gboolean gpio_power_timer_cb(gpointer user)
+{
+       self.timeout_source = 0;
+
+       if (self.timer_event)
+               gpio_power_state_machine(self.timer_event);
+
+       return FALSE;
+}
+
+
+static void gpio_power_state_machine(enum power_event event)
+{
+       enum power_state new_state;
+
+       DBG("(%s) @ state %s",
+               gpio_power_event_name(event),
+               gpio_power_state_name(self.state));
+
+       switch (event) {
+       case POWER_EVENT_ON:
+               self.target = PHONET_LINK_UP;
+
+               if (self.current == PHONET_LINK_NONE)
+                       return;
+
+               switch (self.state) {
+               case POWER_STATE_ON_STARTED:
+               case POWER_STATE_ON_RESET:
+               case POWER_STATE_ON:
+                       /* Do nothing */
+                       break;
+
+               case POWER_STATE_OFF_STARTED:
+                       /* Do nothing */
+                       break;
+
+               case POWER_STATE_NONE:
+               case POWER_STATE_OFF_WAITING:
+               case POWER_STATE_OFF:
+               case POWER_STATE_ON_FAILED:
+                       gpio_power_set_state(POWER_STATE_ON_STARTED);
+                       break;
+               }
+               return;
+
+       case POWER_EVENT_PHONET_LINK_DOWN:
+
+               switch (self.target) {
+               case PHONET_LINK_UP:
+                       break;
+
+               case PHONET_LINK_DOWN:
+               case PHONET_LINK_NONE:
+               default:
+                       if (self.state == POWER_STATE_OFF ||
+                                       self.state == POWER_STATE_NONE)
+                               new_state = POWER_STATE_OFF;
+                       else
+                               new_state = POWER_STATE_OFF_WAITING;
+
+                       gpio_power_set_state(new_state);
+                       return;
+               }
+
+               switch (self.state) {
+               case POWER_STATE_NONE:
+                       /* first connection down event => start modem */
+                       gpio_power_set_state(POWER_STATE_ON_STARTED);
+                       break;
+
+               case POWER_STATE_ON_STARTED:
+               case POWER_STATE_ON_RESET:
+                       break;
+
+               default:
+                       self.retry_count = 0;
+                       gpio_power_set_state(POWER_STATE_ON_RESET);
+                       break;
+               }
+               return;
+
+       case POWER_EVENT_ON_TIMEOUT:
+
+               if (self.target == PHONET_LINK_DOWN)
+                       new_state = POWER_STATE_OFF_STARTED;
+               else if (self.retry_count <= RETRY_COUNT_POWER_ON)
+                       new_state = POWER_STATE_ON_STARTED;
+               else
+                       new_state = POWER_STATE_ON_FAILED;
+
+               gpio_power_set_state(new_state);
+               return;
+
+       case POWER_EVENT_REBOOT_TIMEOUT:
+               /* Modem not rebooting - try to powercycle */
+               if (self.target == PHONET_LINK_DOWN)
+                       new_state = POWER_STATE_OFF_STARTED;
+               else if (self.retry_count <= RETRY_COUNT_RESET)
+                       new_state = POWER_STATE_ON_RESET;
+               else
+                       new_state = POWER_STATE_ON_STARTED;
+
+               gpio_power_set_state(new_state);
+               return;
+
+       case POWER_EVENT_PHONET_LINK_UP:
+
+               switch (self.state) {
+               case POWER_STATE_NONE:
+                       return;
+
+               case POWER_STATE_ON_STARTED:
+               case POWER_STATE_ON_RESET:
+                       break;
+
+               case POWER_STATE_ON:
+                       return;
+
+               case POWER_STATE_OFF_STARTED:
+               case POWER_STATE_OFF_WAITING:
+               case POWER_STATE_OFF:
+               case POWER_STATE_ON_FAILED:
+                       DBG("LINK_UP event while modem should be powered off");
+                       /* should never come here */
+                       break;
+               }
+
+               if (self.target == PHONET_LINK_DOWN)
+                       gpio_power_set_state(POWER_STATE_OFF_STARTED);
+               else
+                       gpio_power_set_state(POWER_STATE_ON);
+               return;
+
+       case POWER_EVENT_OFF:
+               self.target = PHONET_LINK_DOWN;
+
+               switch (self.state) {
+               case POWER_STATE_ON_STARTED:
+               case POWER_STATE_ON_RESET:
+                       /* Do nothing until a timer expires */
+                       break;
+
+               case POWER_STATE_ON:
+                       gpio_power_set_state(POWER_STATE_OFF_STARTED);
+                       break;
+
+               case POWER_STATE_OFF_STARTED:
+               case POWER_STATE_OFF_WAITING:
+               case POWER_STATE_OFF:
+                       /* Do nothing */
+                       break;
+
+               case POWER_STATE_NONE:
+               case POWER_STATE_ON_FAILED:
+                       gpio_power_set_state(POWER_STATE_OFF);
+                       break;
+               }
+               return;
+
+       case POWER_EVENT_OFF_IMMEDIATELY:
+               gpio_power_set_state(POWER_STATE_OFF);
+               return;
+
+       case POWER_EVENT_OFF_TIMEOUT:
+               DBG("Modem power off timed out");
+               gpio_power_set_state(POWER_STATE_OFF);
+               return;
+
+       case POWER_EVENT_OFF_COMPLETE:
+               if (self.state == POWER_STATE_OFF_WAITING) {
+                       DBG("Modem shutdown complete");
+                       gpio_power_set_state(POWER_STATE_OFF);
+               }
+               return;
+       }
+
+       DBG("Event %s (%d) not handled", gpio_power_event_name(event), event);
+}
+
+
+static void gpio_power_set_state(enum power_state new_state)
+{
+       enum power_state old_state = self.state;
+       unsigned timeout = 0;
+       enum power_event timer_event;
+
+       DBG("(%s) at (%s)%s",
+               gpio_power_state_name(new_state),
+               gpio_power_state_name(old_state),
+               new_state == old_state ? " - already" : "");
+
+       switch (old_state) {
+       case POWER_STATE_ON_STARTED:
+               gpio_finish_modem_power_on();
+               break;
+
+       case POWER_STATE_ON_RESET:
+               gpio_finish_modem_reset();
+               break;
+
+       default:
+               break;
+       }
+
+       if (self.timeout_source) {
+               g_source_remove(self.timeout_source);
+               self.timeout_source = 0;
+               self.timer_event = 0;
+       }
+
+       if (old_state == new_state
+                       && new_state != POWER_STATE_ON_STARTED
+                       && new_state != POWER_STATE_ON_RESET)
+               return;
+
+       self.state = new_state;
+
+       switch (self.state) {
+       case POWER_STATE_NONE:
+               break;
+
+       case POWER_STATE_ON_STARTED:
+               self.retry_count++;
+
+               /* Maximum time modem power on procedure on can take */
+               timeout = 5000;
+               timer_event = POWER_EVENT_ON_TIMEOUT;
+               gpio_start_modem_power_on();
+               break;
+
+       case POWER_STATE_ON_RESET:
+               DBG("Starting modem restart timeout");
+
+               /* Time allowed for modem to restart after crash */
+               timeout = 5000;
+               timer_event = POWER_EVENT_REBOOT_TIMEOUT;
+
+               if (self.retry_count++ > 0)
+                       gpio_start_modem_reset();
+               break;
+
+       case POWER_STATE_ON:
+               DBG("Power on");
+               self.retry_count = 0;
+               break;
+
+       case POWER_STATE_OFF_STARTED:
+               DBG("Starting power off");
+
+               /* Maximum time modem power_off can take */
+               timeout = 6150;
+               timer_event = POWER_EVENT_OFF_TIMEOUT;
+               break;
+
+       case POWER_STATE_OFF_WAITING:
+               gpio_finish_modem_power_off();
+               DBG("Waiting for modem to settle down");
+
+               /* Cooling time after power off */
+               timeout = 1000;
+               timer_event = POWER_EVENT_OFF_COMPLETE;
+               break;
+
+       case POWER_STATE_OFF:
+
+               if (old_state != POWER_STATE_OFF_WAITING
+                               && old_state != POWER_STATE_ON_FAILED)
+                       gpio_finish_modem_power_off();
+               break;
+
+       case POWER_STATE_ON_FAILED:
+               DBG("Link to modem cannot be established, giving up");
+               gpio_finish_modem_power_off();
+               break;
+       }
+
+       if (timeout) {
+               self.timer_event = timer_event;
+               self.timeout_source = g_timeout_add(timeout,
+                                       gpio_power_timer_cb, NULL);
+       }
+
+       self.callback(new_state, self.data);
+}
+
+static void phonet_status_cb(GIsiModem *idx, enum GIsiPhonetLinkState state,
+                               char const *ifname, void *dummy)
+{
+       DBG("Link %s (%u) is %s",
+               ifname, g_isi_modem_index(idx),
+               state == PN_LINK_REMOVED ? "removed" :
+               state == PN_LINK_DOWN ? "down" : "up");
+
+       if (state == PN_LINK_UP) {
+
+               if (self.current == PHONET_LINK_UP)
+                       return;
+
+               self.current = PHONET_LINK_UP;
+
+               /* link is up - we can lower cmt_rst_rq */
+               GPIO_WRITE(cmt_rst_rq, 0);
+
+               gpio_power_state_machine(POWER_EVENT_PHONET_LINK_UP);
+       } else {
+
+               if (self.current == PHONET_LINK_DOWN)
+                       return;
+
+               self.current = PHONET_LINK_DOWN;
+
+               gpio_power_state_machine(POWER_EVENT_PHONET_LINK_DOWN);
+       }
+}
+
+static int gpio_probe_links(void)
+{
+       char const *gpiodir = "/sys/class/gpio";
+       char const *cmtdir = "/dev/cmt";
+       DIR *gpio;
+       struct dirent *d, entry[1];
+
+       if (file_exists(cmtdir)) {
+               DBG("Using %s", cmtdir);
+               return 0;
+       }
+
+       DBG("Using %s: trying to make links to %s", gpiodir, cmtdir);
+
+       if (!dir_exists(cmtdir)) {
+               if (mkdir(cmtdir, 0755) == -1) {
+                       DBG("%s: %s", cmtdir, strerror(errno));
+                       return -(errno = ENODEV);
+               }
+       }
+
+       gpio = opendir(gpiodir);
+       if (gpio == NULL) {
+               DBG("%s: %s", "gpiodir", strerror(errno));
+               return -(errno = ENODEV);
+       }
+
+       while (readdir_r(gpio, entry, &d) == 0) {
+               char nn[PATH_MAX], name[PATH_MAX], from[PATH_MAX], to[PATH_MAX];
+               FILE *nf;
+               size_t len;
+
+               if (d == NULL) {
+                       (void) closedir(gpio);
+                       return 0;
+               }
+
+               snprintf(nn, sizeof nn, "%s/%s/name", gpiodir, d->d_name);
+
+               nf = fopen(nn, "rb");
+               if (nf == NULL) {
+                       DBG("%s: %s", nn, strerror(errno));
+                       continue;
+               }
+
+               len = fread(name, sizeof name, 1, nf);
+
+               if (ferror(nf)) {
+                       DBG("read from %s: %s", nn, strerror(errno));
+                       fclose(nf);
+                       continue;
+               }
+
+               fclose(nf);
+
+               if (len < 4)
+                       continue;
+
+               name[--len] = '\0';
+
+               if (strncmp(name, "cmt_", 4))
+                       continue;
+
+               snprintf(from, sizeof from, "%s/%s", gpiodir, d->d_name);
+               snprintf(to, sizeof to, "%s/%s", cmtdir, name);
+
+               if (symlink(from, to) == -1)
+                       DBG("%s: %s", to, strerror(errno));
+       }
+
+       DBG("%s: %s", "/sys/class/gpio", strerror(errno));
+
+       (void) closedir(gpio);
+
+       return -(errno = ENODEV);
+}
+
+
+int gpio_probe(GIsiModem *idx, unsigned addr, gpio_finished_cb_t cb, void *data)
+{
+       int error;
+
+       if (cb == NULL) {
+               DBG("gpio: No callback given");
+               return -(errno = EFAULT);
+       }
+
+       if (self.callback) {
+               DBG("gpio: %s", strerror(EBUSY));
+               return -(errno = EBUSY);
+       }
+
+       if (g_isi_pn_netlink_by_modem(idx)) {
+               DBG("Phonet link %p: %s", idx, strerror(EBUSY));
+               return -(errno = EBUSY);
+       }
+
+       self.target = PHONET_LINK_NONE;
+       self.have_gpio_switch = file_exists(GPIO_SWITCH);
+
+       if (self.have_gpio_switch) {
+               DBG("Using GPIO switch");
+       } else {
+               error = gpio_probe_links();
+               if (error)
+                       return error;
+       }
+
+       /* GPIO lines availability depends on HW and SW versions */
+       self.have_cmt_en = gpio_line_probe("cmt_en");
+       self.have_cmt_rst_rq = gpio_line_probe("cmt_rst_rq");
+       self.have_cmt_rst = gpio_line_probe("cmt_rst");
+       self.have_cmt_bsi = gpio_line_probe("cmt_bsi");
+       self.have_cmt_apeslpx = gpio_line_probe("cmt_apeslpx");
+
+       if (!self.have_cmt_en) {
+               DBG("Modem control GPIO lines are not available");
+               memset(&self, 0, sizeof self);
+               return -(errno = ENODEV);
+       }
+
+       if (self.have_cmt_bsi)
+               self.rapu = RAPU_TYPE_1;
+       else
+               self.rapu = RAPU_TYPE_2;
+
+       self.link = g_isi_pn_netlink_start(idx, phonet_status_cb, NULL);
+       if (self.link == NULL) {
+               memset(&self, 0, sizeof self);
+               return -errno;
+       }
+
+       self.callback = cb;
+       self.data = data;
+
+       if (addr) {
+               error = g_isi_pn_netlink_set_address(idx, addr);
+               if (error && error != -EEXIST)
+                       DBG("g_isi_netlink_set_address: %s", strerror(-error));
+       }
+
+       return 0;
+}
+
+int gpio_remove(void *data)
+{
+       if (self.data != data)
+               return -EINVAL;
+
+       if (self.link)
+               g_isi_pn_netlink_stop(self.link);
+
+       if (self.timeout_source) {
+               g_source_remove(self.timeout_source);
+               self.timeout_source = 0;
+       }
+
+       memset(&self, 0, sizeof self);
+
+       return 0;
+}
+
+int gpio_enable(void *data)
+{
+       if (self.data != data)
+               return -EINVAL;
+
+       if (self.state == POWER_STATE_ON)
+               return 0;
+
+       gpio_power_state_machine(POWER_EVENT_ON);
+
+       return -EINPROGRESS;
+}
+
+int gpio_disable(void *data)
+{
+       if (self.data != data)
+               return -EINVAL;
+
+       if (self.state == POWER_STATE_OFF
+                       || self.state == POWER_STATE_ON_FAILED)
+               return 0;
+
+       gpio_power_state_machine(POWER_EVENT_OFF);
+
+       return -EINPROGRESS;
+}
diff --git a/plugins/nokia-gpio.h b/plugins/nokia-gpio.h
new file mode 100644 (file)
index 0000000..b653bb8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum power_state {
+       POWER_STATE_NONE,
+       POWER_STATE_ON_STARTED,
+       POWER_STATE_ON,
+       POWER_STATE_ON_RESET,
+       POWER_STATE_ON_FAILED,
+       POWER_STATE_OFF_STARTED,
+       POWER_STATE_OFF_WAITING,
+       POWER_STATE_OFF,
+};
+
+typedef void (*gpio_finished_cb_t)(enum power_state value, void *opaque);
+
+int gpio_probe(GIsiModem *idx, unsigned addr, gpio_finished_cb_t cb, void *data);
+int gpio_enable(void *opaque);
+int gpio_disable(void *opaque);
+int gpio_remove(void *opaque);
+
+char const *gpio_power_state_name(enum power_state value);
diff --git a/plugins/nokia.c b/plugins/nokia.c
new file mode 100644 (file)
index 0000000..ef598fa
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/phonebook.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct nokia_data {
+       GAtChat *modem;
+       GAtChat *aux;
+};
+
+static int nokia_probe(struct ofono_modem *modem)
+{
+       struct nokia_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct nokia_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void nokia_remove(struct ofono_modem *modem)
+{
+       struct nokia_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->aux);
+
+       g_free(data);
+}
+
+static void nokia_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, nokia_debug, debug);
+
+       return chat;
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct nokia_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+       }
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static int nokia_enable(struct ofono_modem *modem)
+{
+       struct nokia_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               return -EINVAL;
+
+       data->aux = open_device(modem, "Aux", "Aux: ");
+       if (data->aux == NULL) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+               return -EIO;
+       }
+
+       g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL,
+                                               NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL,
+                                               NULL, NULL, NULL);
+
+       /*
+        * Ensure that the modem is using GSM character set and not IRA,
+        * otherwise weirdness with umlauts and other non-ASCII characters
+        * can result
+        */
+       g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "AT+CFUN=1", none_prefix,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct nokia_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int nokia_disable(struct ofono_modem *modem)
+{
+       struct nokia_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void nokia_pre_sim(struct ofono_modem *modem)
+{
+       struct nokia_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->aux);
+       sim = ofono_sim_create(modem, 0, "atmodem", data->aux);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void nokia_post_sim(struct ofono_modem *modem)
+{
+       struct nokia_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->aux);
+
+       ofono_sms_create(modem, OFONO_VENDOR_OPTION_HSO,
+                                       "atmodem", data->aux);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_NOKIA,
+                                       "atmodem", data->aux);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+}
+
+static void nokia_post_online(struct ofono_modem *modem)
+{
+       struct nokia_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_NOKIA,
+                                       "atmodem", data->aux);
+
+       ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                       "atmodem", data->aux);
+}
+
+static struct ofono_modem_driver nokia_driver = {
+       .name           = "nokia",
+       .probe          = nokia_probe,
+       .remove         = nokia_remove,
+       .enable         = nokia_enable,
+       .disable        = nokia_disable,
+       .pre_sim        = nokia_pre_sim,
+       .post_sim       = nokia_post_sim,
+       .post_online    = nokia_post_online,
+};
+
+static int nokia_init(void)
+{
+       return ofono_modem_driver_register(&nokia_driver);
+}
+
+static void nokia_exit(void)
+{
+       ofono_modem_driver_unregister(&nokia_driver);
+}
+
+OFONO_PLUGIN_DEFINE(nokia, "Nokia Datacard modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, nokia_init, nokia_exit)
diff --git a/plugins/nokiacdma.c b/plugins/nokiacdma.c
new file mode 100644 (file)
index 0000000..a25e2a0
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <termios.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <ofono/cdma-voicecall.h>
+#include <ofono/devinfo.h>
+#include <ofono/cdma-connman.h>
+
+#include "common.h"
+
+struct nokiacdma_data {
+       GAtChat *chat;
+};
+
+static void nokiacdma_debug(const char *str, void *data)
+{
+       const char *prefix = data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int nokiacdma_probe(struct ofono_modem *modem)
+{
+       struct nokiacdma_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct nokiacdma_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void nokiacdma_remove(struct ofono_modem *modem)
+{
+       struct nokiacdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_at_chat_unref(data->chat);
+
+       g_free(data);
+}
+
+static int nokiacdma_enable(struct ofono_modem *modem)
+{
+       struct nokiacdma_data *data = ofono_modem_get_data(modem);
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       const char *device;
+
+       DBG("%p", modem);
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL)
+               return -EINVAL;
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return -EIO;
+
+       /*
+        * TODO: Will need a CDMA AT syntax parser later.
+        * Using GSM V1 for now.
+        */
+       syntax = g_at_syntax_new_gsmv1();
+
+       data->chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (data->chat == NULL)
+               return -ENOMEM;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->chat, nokiacdma_debug,
+                                       "CDMA Device: ");
+
+       return 0;
+}
+
+static int nokiacdma_disable(struct ofono_modem *modem)
+{
+       struct nokiacdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       return 0;
+}
+
+static void nokiacdma_pre_sim(struct ofono_modem *modem)
+{
+       struct nokiacdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_cdma_voicecall_create(modem, 0, "cdmamodem", data->chat);
+       ofono_devinfo_create(modem, 0, "cdmamodem", data->chat);
+}
+
+static void nokiacdma_post_sim(struct ofono_modem *modem)
+{
+       DBG("%p", modem);
+}
+
+static void nokiacdma_post_online(struct ofono_modem *modem)
+{
+       struct nokiacdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_cdma_connman_create(modem, 0, "cdmamodem", data->chat);
+}
+
+static struct ofono_modem_driver nokiacdma_driver = {
+       .name           = "nokiacdma",
+       .probe          = nokiacdma_probe,
+       .remove         = nokiacdma_remove,
+       .enable         = nokiacdma_enable,
+       .disable        = nokiacdma_disable,
+       .pre_sim        = nokiacdma_pre_sim,
+       .post_sim       = nokiacdma_post_sim,
+       .post_online    = nokiacdma_post_online,
+};
+
+static int nokiacdma_init(void)
+{
+       return ofono_modem_driver_register(&nokiacdma_driver);
+}
+
+static void nokiacdma_exit(void)
+{
+       ofono_modem_driver_unregister(&nokiacdma_driver);
+}
+
+OFONO_PLUGIN_DEFINE(nokiacdma, "Nokia CDMA AT Modem", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       nokiacdma_init, nokiacdma_exit)
diff --git a/plugins/novatel.c b/plugins/novatel.c
new file mode 100644 (file)
index 0000000..a64364d
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/radio-settings.h>
+#include <ofono/phonebook.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+static const char *nwdmat_prefix[] = { "$NWDMAT:", NULL };
+
+struct novatel_data {
+       GAtChat *modem;
+       GAtChat *aux;
+       gint dmat_mode;
+};
+
+static int novatel_probe(struct ofono_modem *modem)
+{
+       struct novatel_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct novatel_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void novatel_remove(struct ofono_modem *modem)
+{
+       struct novatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->aux);
+
+       g_free(data);
+}
+
+static void novatel_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                                               const char *key, char *debug)
+{
+       GAtChat *chat;
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       const char *device;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, novatel_debug, debug);
+
+       return chat;
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct novatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+       }
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static void nwdmat_action(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct novatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok)
+               goto error;
+
+       data->dmat_mode = 1;
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               goto error;
+
+       g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL,
+                                                       NULL, NULL, NULL);
+
+       /* Check for all supported technologies */
+       g_at_chat_send(data->aux, "AT$CNTI=2", none_prefix,
+                                                       NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix,
+                                               cfun_enable, modem, NULL);
+
+       return;
+
+error:
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       ofono_modem_set_powered(modem, FALSE);
+}
+
+static void nwdmat_query(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct novatel_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       gint dmat_mode;
+
+       DBG("");
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "$NWDMAT:"))
+               goto error;
+
+       if (!g_at_result_iter_next_number(&iter, &dmat_mode))
+               goto error;
+
+       if (dmat_mode == 1) {
+               nwdmat_action(TRUE, result, user_data);
+               return;
+       }
+
+       g_at_chat_send(data->aux, "AT$NWDMAT=1", nwdmat_prefix,
+                                               nwdmat_action, modem, NULL);
+
+       return;
+
+error:
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       ofono_modem_set_powered(modem, FALSE);
+}
+
+static int novatel_enable(struct ofono_modem *modem)
+{
+       struct novatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->aux = open_device(modem, "Aux", "Aux: ");
+       if (data->aux == NULL)
+               return -EIO;
+
+       g_at_chat_blacklist_terminator(data->aux,
+                                       G_AT_CHAT_TERMINATOR_NO_CARRIER);
+
+       g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL,
+                                                       NULL, NULL, NULL);
+
+       /* Check mode of seconday port */
+       g_at_chat_send(data->aux, "AT$NWDMAT?", nwdmat_prefix,
+                                               nwdmat_query, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct novatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int novatel_disable(struct ofono_modem *modem)
+{
+       struct novatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       g_at_chat_send(data->aux, "AT$NWDMAT=0", nwdmat_prefix,
+                                                       NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "AT+CFUN=0", none_prefix,
+                                               cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void novatel_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct novatel_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(data->aux, command, none_prefix,
+                                       set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void novatel_pre_sim(struct ofono_modem *modem)
+{
+       struct novatel_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->aux);
+       sim = ofono_sim_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                       "atmodem", data->aux);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void novatel_post_sim(struct ofono_modem *modem)
+{
+       struct novatel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->aux);
+       ofono_radio_settings_create(modem, 0, "nwmodem", data->aux);
+       ofono_sms_create(modem, OFONO_VENDOR_NOVATEL, "atmodem", data->aux);
+}
+
+static void novatel_post_online(struct ofono_modem *modem)
+{
+       struct novatel_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_NOVATEL, "atmodem",
+                                                       data->aux);
+
+       ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem",
+                                                       data->aux);
+       ofono_ussd_create(modem, 0, "atmodem", data->aux);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_NOVATEL,
+                                               "atmodem", data->aux);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+}
+
+static struct ofono_modem_driver novatel_driver = {
+       .name           = "novatel",
+       .probe          = novatel_probe,
+       .remove         = novatel_remove,
+       .enable         = novatel_enable,
+       .disable        = novatel_disable,
+       .set_online     = novatel_set_online,
+       .pre_sim        = novatel_pre_sim,
+       .post_sim       = novatel_post_sim,
+       .post_online    = novatel_post_online,
+};
+
+static int novatel_init(void)
+{
+       return ofono_modem_driver_register(&novatel_driver);
+}
+
+static void novatel_exit(void)
+{
+       ofono_modem_driver_unregister(&novatel_driver);
+}
+
+OFONO_PLUGIN_DEFINE(novatel, "Novatel Wireless modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, novatel_init, novatel_exit)
diff --git a/plugins/ofono-speedup.rules b/plugins/ofono-speedup.rules
new file mode 100644 (file)
index 0000000..f43fc7b
--- /dev/null
@@ -0,0 +1,23 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="ofono_speedup_end"
+
+SUBSYSTEM!="tty", GOTO="ofono_speedup_end"
+KERNEL!="ttyUSB[0-9]*", GOTO="ofono_speedup_end"
+
+# SpeedUp 7300
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9e00", ENV{ID_USB_INTERFACE_NUM}=="00", ENV{OFONO_LABEL}="modem"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9e00", ENV{ID_USB_INTERFACE_NUM}=="03", ENV{OFONO_LABEL}="aux"
+
+# SpeedUp
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1005", ENV{ID_USB_INTERFACE_NUM}=="03", ENV{OFONO_LABEL}="modem"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1005", ENV{ID_USB_INTERFACE_NUM}=="01", ENV{OFONO_LABEL}="aux"
+
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1008", ENV{ID_USB_INTERFACE_NUM}=="03", ENV{OFONO_LABEL}="modem"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1008", ENV{ID_USB_INTERFACE_NUM}=="01", ENV{OFONO_LABEL}="aux"
+
+# SpeedUp 9800
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9800", ENV{ID_USB_INTERFACE_NUM}=="01", ENV{OFONO_LABEL}="modem"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9800", ENV{ID_USB_INTERFACE_NUM}=="02", ENV{OFONO_LABEL}="aux"
+
+LABEL="ofono_speedup_end"
diff --git a/plugins/ofono.rules b/plugins/ofono.rules
new file mode 100644 (file)
index 0000000..3ed9f16
--- /dev/null
@@ -0,0 +1,29 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="ofono_end"
+
+# ISI/Phonet drivers
+SUBSYSTEM!="net", GOTO="ofono_isi_end"
+ATTRS{type}!="820", GOTO="ofono_isi_end"
+KERNELS=="gadget", GOTO="ofono_isi_end"
+
+# Nokia N900 modem
+SUBSYSTEMS=="hsi", ENV{OFONO_DRIVER}="n900", ENV{OFONO_ISI_ADDRESS}="108"
+KERNEL=="phonet*", ENV{OFONO_DRIVER}="n900", ENV{OFONO_ISI_ADDRESS}="108"
+
+# STE u8500
+KERNEL=="shrm0", ENV{OFONO_DRIVER}="u8500"
+
+LABEL="ofono_isi_end"
+
+SUBSYSTEM!="usb", GOTO="ofono_end"
+ENV{DEVTYPE}!="usb_device", GOTO="ofono_end"
+
+# Ignore fake serial number
+ATTRS{serial}=="1234567890ABCDEF", ENV{ID_SERIAL_SHORT}=""
+
+# Nokia CDMA Device
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="023e", ENV{OFONO_DRIVER}="nokiacdma"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="00b6", ENV{OFONO_DRIVER}="nokiacdma"
+
+LABEL="ofono_end"
diff --git a/plugins/palmpre.c b/plugins/palmpre.c
new file mode 100644 (file)
index 0000000..c495f28
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/phonebook.h>
+#include <ofono/voicecall.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/sms.h>
+
+#include <drivers/atmodem/vendor.h>
+
+struct palmpre_data {
+       GAtChat *chat;
+};
+
+static int palmpre_probe(struct ofono_modem *modem)
+{
+       struct palmpre_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct palmpre_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void palmpre_remove(struct ofono_modem *modem)
+{
+       struct palmpre_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_at_chat_unref(data->chat);
+       g_free(data);
+}
+
+static void palmpre_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+
+       DBG("");
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static int palmpre_enable(struct ofono_modem *modem)
+{
+       struct palmpre_data *data = ofono_modem_get_data(modem);
+       GIOChannel *io;
+       GAtSyntax *syntax;
+       const char *device;
+       GHashTable *options;
+
+       DBG("%p", modem);
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL)
+               device = "/dev/modem0";
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               return -ENOMEM;
+
+       g_hash_table_insert(options, "Baud", "115200");
+
+       io = g_at_tty_open(device, options);
+       g_hash_table_destroy(options);
+
+       if (io == NULL)
+               return -EIO;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       data->chat = g_at_chat_new(io, syntax);
+       g_io_channel_unref(io);
+       g_at_syntax_unref(syntax);
+
+       if (data->chat == NULL)
+               return -ENOMEM;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->chat, palmpre_debug, "");
+
+       /* Ensure terminal is in a known state */
+       g_at_chat_send(data->chat, "ATZ E0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       /* Power modem up */
+       g_at_chat_send(data->chat, "AT+CFUN=1", NULL,
+                       cfun_set_on_cb, modem, NULL);
+
+       return 0;
+}
+
+static void cfun_set_off_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct palmpre_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int palmpre_disable(struct ofono_modem *modem)
+{
+       struct palmpre_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       /* Power modem down */
+       g_at_chat_cancel_all(data->chat);
+       g_at_chat_unregister_all(data->chat);
+       g_at_chat_send(data->chat, "AT+CFUN=0", NULL,
+                       cfun_set_off_cb, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void palmpre_pre_sim(struct ofono_modem *modem)
+{
+       struct palmpre_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+       sim = ofono_sim_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem",
+                               data->chat);
+       ofono_voicecall_create(modem, 0, "atmodem", data->chat);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void palmpre_post_sim(struct ofono_modem *modem)
+{
+       struct palmpre_data *data = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, 0, "atmodem", data->chat);
+       ofono_sms_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem",
+                               data->chat);
+       ofono_phonebook_create(modem, 0, "atmodem", data->chat);
+
+       gprs = ofono_gprs_create(modem, 0, "atmodem", data->chat);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver palmpre_driver = {
+       .name           = "palmpre",
+       .probe          = palmpre_probe,
+       .remove         = palmpre_remove,
+       .enable         = palmpre_enable,
+       .disable        = palmpre_disable,
+       .pre_sim        = palmpre_pre_sim,
+       .post_sim       = palmpre_post_sim
+};
+
+static int palmpre_init(void)
+{
+       return ofono_modem_driver_register(&palmpre_driver);
+}
+
+static void palmpre_exit(void)
+{
+       ofono_modem_driver_unregister(&palmpre_driver);
+}
+
+OFONO_PLUGIN_DEFINE(palmpre, "Palm Pre driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, palmpre_init, palmpre_exit)
diff --git a/plugins/phonesim.c b/plugins/phonesim.c
new file mode 100644 (file)
index 0000000..5f4940f
--- /dev/null
@@ -0,0 +1,1058 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <gatmux.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-volume.h>
+#include <ofono/cbs.h>
+#include <ofono/ctm.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/stk.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/gnss.h>
+#include <ofono/handsfree.h>
+
+#include <drivers/atmodem/vendor.h>
+#include <drivers/atmodem/atutil.h>
+#include <drivers/hfpmodem/slc.h>
+
+#include "ofono.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *ptty_prefix[] = { "+PTTY:", NULL };
+static int next_iface = 0;
+
+struct phonesim_data {
+       GAtMux *mux;
+       GAtChat *chat;
+       gboolean calypso;
+       gboolean use_mux;
+       gboolean hfp;
+       struct hfp_slc_info hfp_info;
+       unsigned int hfp_watch;
+       int batt_level;
+};
+
+struct gprs_context_data {
+       GAtChat *chat;
+       char *interface;
+       enum ofono_gprs_proto proto;
+};
+
+static void at_cgact_up_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_gprs_context *gc = cbd->user;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok == FALSE)
+               goto done;
+
+       ofono_gprs_context_set_interface(gc, gcd->interface);
+
+       if (gcd->proto == OFONO_GPRS_PROTO_IP ||
+                       gcd->proto == OFONO_GPRS_PROTO_IPV4V6)
+               ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE);
+
+       if (gcd->proto == OFONO_GPRS_PROTO_IPV6 ||
+                       gcd->proto == OFONO_GPRS_PROTO_IPV4V6) {
+               ofono_gprs_context_set_ipv6_address(gc, "fe80::1");
+               ofono_gprs_context_set_ipv6_prefix_length(gc, 10);
+       }
+
+done:
+       cb(&error, cbd->data);
+}
+
+static void at_cgact_down_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_context_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void phonesim_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+       int len = 0;
+
+       cbd->user = gc;
+       gcd->proto = ctx->proto;
+
+       switch (ctx->proto) {
+       case OFONO_GPRS_PROTO_IP:
+               len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
+                               ctx->cid);
+               break;
+
+       case OFONO_GPRS_PROTO_IPV6:
+               len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
+                               ctx->cid);
+               break;
+
+       case OFONO_GPRS_PROTO_IPV4V6:
+               len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
+                               ctx->cid);
+               break;
+       }
+
+       if (ctx->apn)
+               snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+                               ctx->apn);
+
+       /* Assume always succeeds */
+       if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+               goto error;
+
+       sprintf(buf, "AT+CGACT=1,%u", ctx->cid);
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_cgact_up_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void phonesim_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int id,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[128];
+
+       cbd->user = gc;
+
+       snprintf(buf, sizeof(buf), "AT+CGACT=0,%u", id);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               at_cgact_down_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static int phonesim_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+
+       gcd = g_try_new0(struct gprs_context_data, 1);
+       if (gcd == NULL)
+               return -ENOMEM;
+
+       gcd->chat = g_at_chat_clone(chat);
+       gcd->interface = g_strdup_printf("dummy%d", next_iface++);
+
+       ofono_gprs_context_set_data(gc, gcd);
+
+       return 0;
+}
+
+static void phonesim_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("");
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd->interface);
+
+       g_free(gcd);
+}
+
+static void phonesim_ctm_support_cb(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_ctm *ctm = user_data;
+
+       if (!ok) {
+               ofono_ctm_remove(ctm);
+               return;
+       }
+
+       ofono_ctm_register(ctm);
+}
+
+static int phonesim_ctm_probe(struct ofono_ctm *ctm,
+                               unsigned int vendor, void *data)
+{
+       GAtChat *chat;
+
+       DBG("");
+
+       chat = g_at_chat_clone(data);
+
+       ofono_ctm_set_data(ctm, chat);
+
+       g_at_chat_send(chat, "AT+PTTY=?", ptty_prefix, phonesim_ctm_support_cb,
+                       ctm, NULL);
+
+       return 0;
+}
+
+static void phonesim_ctm_remove(struct ofono_ctm *ctm)
+{
+       GAtChat *chat = ofono_ctm_get_data(ctm);
+
+       DBG("");
+
+       ofono_ctm_set_data(ctm, NULL);
+
+       g_at_chat_unref(chat);
+}
+
+static void ctm_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_error error;
+       GAtResultIter iter;
+       ofono_ctm_query_cb_t cb = cbd->cb;
+       int value;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               cb(&error, -1, cbd->data);
+               return;
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+PTTY:") == FALSE)
+               goto error;
+
+       if (g_at_result_iter_next_number(&iter, &value) == FALSE)
+               goto error;
+
+       cb(&error, value, cbd->data);
+
+       return;
+
+error:
+
+       CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void phonesim_ctm_query(struct ofono_ctm *ctm,
+                               ofono_ctm_query_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_ctm_get_data(ctm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+
+       DBG("");
+
+       if (g_at_chat_send(chat, "AT+PTTY?", ptty_prefix,
+                               ctm_query_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, 0, data);
+}
+
+static void ctm_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_ctm_set_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void phonesim_ctm_set(struct ofono_ctm *ctm, ofono_bool_t enable,
+                               ofono_ctm_set_cb_t cb, void *data)
+{
+       GAtChat *chat = ofono_ctm_get_data(ctm);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[20];
+
+       DBG("");
+
+       snprintf(buf, sizeof(buf), "AT+PTTY=%d", enable);
+
+       if (g_at_chat_send(chat, buf, none_prefix,
+                               ctm_set_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static struct ofono_gprs_context_driver context_driver = {
+       .name                   = "phonesim",
+       .probe                  = phonesim_context_probe,
+       .remove                 = phonesim_context_remove,
+       .activate_primary       = phonesim_activate_primary,
+       .deactivate_primary     = phonesim_deactivate_primary,
+};
+
+static struct ofono_ctm_driver ctm_driver = {
+       .name                   = "phonesim",
+       .probe                  = phonesim_ctm_probe,
+       .remove                 = phonesim_ctm_remove,
+       .query_tty              = phonesim_ctm_query,
+       .set_tty                = phonesim_ctm_set,
+};
+
+static int phonesim_probe(struct ofono_modem *modem)
+{
+       struct phonesim_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct phonesim_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void phonesim_remove(struct ofono_modem *modem)
+{
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_free(data);
+       ofono_modem_set_data(modem, NULL);
+}
+
+static void phonesim_debug(const char *str, void *prefix)
+{
+       ofono_info("%s%s", (const char *) prefix, str);
+}
+
+static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+
+       DBG("");
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static gboolean phonesim_reset(void *user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (data->mux) {
+               g_at_mux_shutdown(data->mux);
+               g_at_mux_unref(data->mux);
+               data->mux = NULL;
+       }
+
+       ofono_modem_reset(modem);
+
+       return FALSE;
+}
+
+static void crst_notify(GAtResult *result, gpointer user_data)
+{
+       g_idle_add(phonesim_reset, user_data);
+}
+
+static void emulator_battery_cb(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+       int val = 0;
+
+       if (GPOINTER_TO_INT(data) > 0)
+               val = (GPOINTER_TO_INT(data) - 1) / 20 + 1;
+
+       ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_BATTERY, val);
+}
+
+static void cbc_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+       GAtResultIter iter;
+       int status;
+       int level;
+
+       g_at_result_iter_init(&iter, result);
+       if (!g_at_result_iter_next(&iter, "+CBC:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &status))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &level))
+               return;
+
+       data->batt_level = level;
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_battery_cb,
+                                               GUINT_TO_POINTER(level));
+}
+
+static void phonesim_disconnected(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (data->mux) {
+               g_at_mux_shutdown(data->mux);
+               g_at_mux_unref(data->mux);
+               data->mux = NULL;
+       }
+}
+
+static void mux_setup(GAtMux *mux, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+       GIOChannel *io;
+       GAtSyntax *syntax;
+
+       DBG("%p", mux);
+
+       if (mux == NULL) {
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       data->mux = mux;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_mux_set_debug(data->mux, phonesim_debug, "");
+
+       g_at_mux_start(mux);
+       io = g_at_mux_create_channel(mux);
+
+       if (data->calypso)
+               syntax = g_at_syntax_new_gsm_permissive();
+       else
+               syntax = g_at_syntax_new_gsmv1();
+
+       data->chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->chat, phonesim_debug, "");
+
+       if (data->calypso)
+               g_at_chat_set_wakeup_command(data->chat, "AT\r", 500, 5000);
+
+       g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
+                                       cfun_set_on_cb, modem, NULL);
+}
+
+static void emulator_hfp_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *user_data)
+{
+       struct phonesim_data *data = user_data;
+
+       if (cond != OFONO_ATOM_WATCH_CONDITION_REGISTERED)
+               return;
+
+       emulator_battery_cb(atom, GUINT_TO_POINTER(data->batt_level));
+}
+
+static int connect_socket(const char *address, int port)
+{
+       struct sockaddr_in addr;
+       int sk;
+       int err;
+
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0)
+               return -EINVAL;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(address);
+       addr.sin_port = htons(port);
+
+       err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0) {
+               close(sk);
+               return -errno;
+       }
+
+       return sk;
+}
+
+static int phonesim_enable(struct ofono_modem *modem)
+{
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+       GIOChannel *io;
+       GAtSyntax *syntax;
+       const char *address, *value;
+       int sk, port;
+
+       DBG("%p", modem);
+
+       address = ofono_modem_get_string(modem, "Address");
+       if (address == NULL)
+               return -EINVAL;
+
+       port = ofono_modem_get_integer(modem, "Port");
+       if (port < 0)
+               return -EINVAL;
+
+       value = ofono_modem_get_string(modem, "Modem");
+       if (!g_strcmp0(value, "calypso"))
+               data->calypso = TRUE;
+
+       value = ofono_modem_get_string(modem, "Multiplexer");
+       if (!g_strcmp0(value, "internal"))
+               data->use_mux = TRUE;
+
+       sk = connect_socket(address, port);
+       if (sk < 0)
+               return sk;
+
+       io = g_io_channel_unix_new(sk);
+       if (io == NULL) {
+               close(sk);
+               return -ENOMEM;
+       }
+
+       if (data->calypso)
+               syntax = g_at_syntax_new_gsm_permissive();
+       else
+               syntax = g_at_syntax_new_gsmv1();
+
+       data->chat = g_at_chat_new(io, syntax);
+
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       if (data->chat == NULL)
+               return -ENOMEM;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->chat, phonesim_debug, "");
+
+       g_at_chat_set_disconnect_function(data->chat,
+                                               phonesim_disconnected, modem);
+
+       if (data->calypso) {
+               g_at_chat_set_wakeup_command(data->chat, "AT\r", 500, 5000);
+
+               g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL);
+
+               g_at_chat_send(data->chat, "AT%CUNS=0",
+                               NULL, NULL, NULL, NULL);
+       }
+
+       if (data->use_mux) {
+               g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL);
+
+               g_at_mux_setup_gsm0710(data->chat, mux_setup, modem, NULL);
+
+               g_at_chat_unref(data->chat);
+               data->chat = NULL;
+
+               return -EINPROGRESS;
+       }
+
+       g_at_chat_send(data->chat, "AT+CSCS=\"GSM\"", none_prefix,
+                       NULL, NULL, NULL);
+
+       g_at_chat_register(data->chat, "+CRST:",
+                               crst_notify, FALSE, modem, NULL);
+
+       g_at_chat_register(data->chat, "+CBC:",
+                               cbc_notify, FALSE, modem, NULL);
+
+       g_at_chat_send(data->chat, "AT+CBC", none_prefix, NULL, NULL, NULL);
+
+       data->hfp_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                       emulator_hfp_watch, data, NULL);
+
+       return 0;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t callback = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       callback(&error, cbd->data);
+}
+
+static void phonesim_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char buf[64];
+
+       DBG("%p", modem);
+
+       snprintf(buf, sizeof(buf), "AT+CFUN=%d", online ? 1 : 4);
+
+       if (g_at_chat_send(data->chat, buf, none_prefix,
+                               set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static int phonesim_disable(struct ofono_modem *modem)
+{
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       __ofono_modem_remove_atom_watch(modem, data->hfp_watch);
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (data->mux) {
+               g_at_mux_shutdown(data->mux);
+
+               g_at_mux_unref(data->mux);
+               data->mux = NULL;
+       }
+
+       return 0;
+}
+
+static void phonesim_pre_sim(struct ofono_modem *modem)
+{
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+       sim = ofono_sim_create(modem, 0, "atmodem", data->chat);
+
+       if (data->calypso)
+               ofono_voicecall_create(modem, 0, "calypsomodem", data->chat);
+       else
+               ofono_voicecall_create(modem, 0, "atmodem", data->chat);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void phonesim_post_sim(struct ofono_modem *modem)
+{
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_ctm_create(modem, 0, "phonesim", data->chat);
+       ofono_phonebook_create(modem, 0, "atmodem", data->chat);
+
+       if (!data->calypso)
+               ofono_stk_create(modem, OFONO_VENDOR_PHONESIM,
+                                       "atmodem", data->chat);
+
+       ofono_call_forwarding_create(modem, 0, "atmodem", data->chat);
+
+       if (!data->calypso)
+               ofono_sms_create(modem, 0, "atmodem", data->chat);
+}
+
+static void phonesim_post_online(struct ofono_modem *modem)
+{
+       struct phonesim_data *data = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc1, *gc2;
+
+       DBG("%p", modem);
+
+       ofono_ussd_create(modem, 0, "atmodem", data->chat);
+       ofono_call_settings_create(modem, 0, "atmodem", data->chat);
+
+       if (data->calypso)
+               ofono_netreg_create(modem, OFONO_VENDOR_CALYPSO,
+                                                       "atmodem", data->chat);
+       else
+               ofono_netreg_create(modem, OFONO_VENDOR_PHONESIM,
+                                                       "atmodem", data->chat);
+
+       ofono_call_meter_create(modem, 0, "atmodem", data->chat);
+       ofono_call_barring_create(modem, 0, "atmodem", data->chat);
+       ofono_call_volume_create(modem, 0, "atmodem", data->chat);
+
+       if (!data->calypso)
+               ofono_cbs_create(modem, 0, "atmodem", data->chat);
+
+       gc1 = ofono_gprs_context_create(modem, 0, "phonesim", data->chat);
+       gprs = ofono_gprs_create(modem, 0, "atmodem", data->chat);
+       gc2 = ofono_gprs_context_create(modem, 0, "phonesim", data->chat);
+
+       if (gprs && gc1)
+               ofono_gprs_add_context(gprs, gc1);
+
+       if (gprs && gc2)
+               ofono_gprs_add_context(gprs, gc2);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+
+       ofono_gnss_create(modem, 0, "atmodem", data->chat);
+}
+
+static struct ofono_modem_driver phonesim_driver = {
+       .name           = "phonesim",
+       .probe          = phonesim_probe,
+       .remove         = phonesim_remove,
+       .enable         = phonesim_enable,
+       .disable        = phonesim_disable,
+       .set_online     = phonesim_set_online,
+       .pre_sim        = phonesim_pre_sim,
+       .post_sim       = phonesim_post_sim,
+       .post_online    = phonesim_post_online,
+};
+
+static int localhfp_probe(struct ofono_modem *modem)
+{
+       struct hfp_slc_info *info;
+
+       DBG("%p", modem);
+
+       info = g_try_new(struct hfp_slc_info, 1);
+       if (info == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, info);
+
+       return 0;
+}
+
+static void localhfp_remove(struct ofono_modem *modem)
+{
+       struct hfp_slc_info *info = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_free(info);
+       ofono_modem_set_data(modem, NULL);
+}
+
+static void slc_established(gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+
+       ofono_modem_set_powered(modem, TRUE);
+}
+
+static void slc_failed(gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+       struct hfp_slc_info *info = ofono_modem_get_data(modem);
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       g_at_chat_unref(info->chat);
+       info->chat = NULL;
+}
+
+static int localhfp_enable(struct ofono_modem *modem)
+{
+       struct hfp_slc_info *info = ofono_modem_get_data(modem);
+       GIOChannel *io;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+       const char *address;
+       int sk, port;
+
+       address = ofono_modem_get_string(modem, "Address");
+       if (address == NULL)
+               return -EINVAL;
+
+       port = ofono_modem_get_integer(modem, "Port");
+       if (port < 0)
+               return -EINVAL;
+
+       sk = connect_socket(address, port);
+       if (sk < 0)
+               return sk;
+
+       io = g_io_channel_unix_new(sk);
+       if (io == NULL) {
+               close(sk);
+               return -ENOMEM;
+       }
+
+       syntax = g_at_syntax_new_gsmv1();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       if (chat == NULL)
+               return -ENOMEM;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, phonesim_debug, "LocalHfp: ");
+
+       g_at_chat_set_disconnect_function(chat, slc_failed, modem);
+
+       hfp_slc_info_init(info, HFP_VERSION_LATEST);
+       info->chat = chat;
+       hfp_slc_establish(info, slc_established, slc_failed, modem);
+
+       return -EINPROGRESS;
+}
+
+static int localhfp_disable(struct ofono_modem *modem)
+{
+       struct hfp_slc_info *info = ofono_modem_get_data(modem);
+
+       g_at_chat_unref(info->chat);
+       info->chat = NULL;
+
+       return 0;
+}
+
+static void localhfp_pre_sim(struct ofono_modem *modem)
+{
+       struct hfp_slc_info *info = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_voicecall_create(modem, 0, "hfpmodem", info);
+       ofono_netreg_create(modem, 0, "hfpmodem", info);
+       ofono_call_volume_create(modem, 0, "hfpmodem", info);
+       ofono_handsfree_create(modem, 0, "hfpmodem", info);
+}
+
+static struct ofono_modem_driver localhfp_driver = {
+       .name           = "localhfp",
+       .probe          = localhfp_probe,
+       .remove         = localhfp_remove,
+       .enable         = localhfp_enable,
+       .disable        = localhfp_disable,
+       .pre_sim        = localhfp_pre_sim,
+};
+
+static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
+{
+       const char *driver = "phonesim";
+       struct ofono_modem *modem;
+       char *value;
+
+       DBG("group %s", group);
+
+       value = g_key_file_get_string(keyfile, group, "Modem", NULL);
+
+       if (value && g_str_equal(value, "hfp"))
+               driver = "localhfp";
+
+       g_free(value);
+
+       modem = ofono_modem_create(group, driver);
+       if (modem == NULL)
+               return NULL;
+
+       value = g_key_file_get_string(keyfile, group, "Address", NULL);
+       if (value == NULL)
+               goto error;
+
+       ofono_modem_set_string(modem, "Address", value);
+       g_free(value);
+
+       value = g_key_file_get_string(keyfile, group, "Port", NULL);
+       if (value == NULL)
+               goto error;
+
+       ofono_modem_set_integer(modem, "Port", atoi(value));
+       g_free(value);
+
+       value = g_key_file_get_string(keyfile, group, "Modem", NULL);
+       if (value) {
+               ofono_modem_set_string(modem, "Modem", value);
+               g_free(value);
+       }
+
+       value = g_key_file_get_string(keyfile, group, "Multiplexer", NULL);
+       if (value) {
+               ofono_modem_set_string(modem, "Multiplexer", value);
+               g_free(value);
+       }
+
+       DBG("%p", modem);
+
+       return modem;
+
+error:
+       ofono_error("Missing address or port setting for %s", group);
+
+       ofono_modem_remove(modem);
+
+       return NULL;
+}
+
+static GSList *modem_list = NULL;
+
+static void parse_config(const char *filename)
+{
+       GKeyFile *keyfile;
+       GError *err = NULL;
+       char **modems;
+       int i;
+
+       DBG("filename %s", filename);
+
+       keyfile = g_key_file_new();
+
+       g_key_file_set_list_separator(keyfile, ',');
+
+       if (!g_key_file_load_from_file(keyfile, filename, 0, &err)) {
+               ofono_warn("Reading of %s failed: %s", filename, err->message);
+               g_error_free(err);
+               goto done;
+       }
+
+       modems = g_key_file_get_groups(keyfile, NULL);
+
+       for (i = 0; modems[i]; i++) {
+               struct ofono_modem *modem;
+
+               modem = create_modem(keyfile, modems[i]);
+               if (modem == NULL)
+                       continue;
+
+               modem_list = g_slist_prepend(modem_list, modem);
+
+               ofono_modem_register(modem);
+       }
+
+       g_strfreev(modems);
+
+done:
+       g_key_file_free(keyfile);
+}
+
+static int phonesim_init(void)
+{
+       int err;
+
+       err = ofono_modem_driver_register(&phonesim_driver);
+       if (err < 0)
+               return err;
+
+       ofono_modem_driver_register(&localhfp_driver);
+
+       ofono_gprs_context_driver_register(&context_driver);
+       ofono_ctm_driver_register(&ctm_driver);
+
+       parse_config(CONFIGDIR "/phonesim.conf");
+
+       return 0;
+}
+
+static void phonesim_exit(void)
+{
+       GSList *list;
+
+       for (list = modem_list; list; list = list->next) {
+               struct ofono_modem *modem = list->data;
+
+               ofono_modem_remove(modem);
+       }
+
+       g_slist_free(modem_list);
+       modem_list = NULL;
+
+       ofono_ctm_driver_unregister(&ctm_driver);
+
+       ofono_gprs_context_driver_unregister(&context_driver);
+
+       ofono_modem_driver_unregister(&phonesim_driver);
+}
+
+OFONO_PLUGIN_DEFINE(phonesim, "Phone Simulator driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, phonesim_init, phonesim_exit)
diff --git a/plugins/phonesim.conf b/plugins/phonesim.conf
new file mode 100644 (file)
index 0000000..74bb645
--- /dev/null
@@ -0,0 +1,14 @@
+# This is a sample file for the phonesim configuration
+#
+# It should be installed in your oFono system directory,
+# e.g. /etc/ofono/phonesim.conf
+#
+# Each group is parsed as a modem device
+#
+# Each group shall at least define the address and port
+#   Address = <valid IPv4 address format>
+#   Port = <valid TCP port>
+
+#[phonesim]
+#Address=127.0.0.1
+#Port=12345
diff --git a/plugins/provision.c b/plugins/provision.c
new file mode 100644 (file)
index 0000000..99c299e
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/types.h>
+#include <ofono/log.h>
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-provision.h>
+
+#include "mbpi.h"
+
+static int provision_get_settings(const char *mcc, const char *mnc,
+                               const char *spn,
+                               struct ofono_gprs_provision_data **settings,
+                               int *count)
+{
+       GSList *l;
+       GSList *apns;
+       GError *error = NULL;
+       int ap_count;
+       int i;
+
+       DBG("Provisioning for MCC %s, MNC %s, SPN '%s'", mcc, mnc, spn);
+
+       apns = mbpi_lookup_apn(mcc, mnc, FALSE, &error);
+       if (apns == NULL) {
+               if (error != NULL) {
+                       ofono_error("%s", error->message);
+                       g_error_free(error);
+               }
+
+               return -ENOENT;
+       }
+
+       ap_count = g_slist_length(apns);
+
+       DBG("Found %d APs", ap_count);
+
+       *settings = g_try_new0(struct ofono_gprs_provision_data, ap_count);
+       if (*settings == NULL) {
+               ofono_error("Provisioning failed: %s", g_strerror(errno));
+
+               for (l = apns; l; l = l->next)
+                       mbpi_ap_free(l->data);
+
+               g_slist_free(apns);
+
+               return -ENOMEM;
+       }
+
+       *count = ap_count;
+
+       for (l = apns, i = 0; l; l = l->next, i++) {
+               struct ofono_gprs_provision_data *ap = l->data;
+
+               DBG("Name: '%s'", ap->name);
+               DBG("APN: '%s'", ap->apn);
+               DBG("Type: %s", mbpi_ap_type(ap->type));
+               DBG("Username: '%s'", ap->username);
+               DBG("Password: '%s'", ap->password);
+
+               memcpy(*settings + i, ap,
+                       sizeof(struct ofono_gprs_provision_data));
+
+               g_free(ap);
+       }
+
+       g_slist_free(apns);
+
+       return 0;
+}
+
+static struct ofono_gprs_provision_driver provision_driver = {
+       .name           = "Provisioning",
+       .get_settings   = provision_get_settings
+};
+
+static int provision_init(void)
+{
+       return ofono_gprs_provision_driver_register(&provision_driver);
+}
+
+static void provision_exit(void)
+{
+       ofono_gprs_provision_driver_unregister(&provision_driver);
+}
+
+OFONO_PLUGIN_DEFINE(provision, "Provisioning Plugin", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       provision_init, provision_exit)
diff --git a/plugins/push-notification.c b/plugins/push-notification.c
new file mode 100644 (file)
index 0000000..1c19bf2
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/dbus.h>
+
+#include "smsagent.h"
+
+#define PUSH_NOTIFICATION_INTERFACE "org.ofono.PushNotification"
+#define AGENT_INTERFACE "org.ofono.PushNotificationAgent"
+
+#define WAP_PUSH_SRC_PORT 9200
+#define WAP_PUSH_DST_PORT 2948
+
+static unsigned int modemwatch_id;
+
+struct push_notification {
+       struct ofono_modem *modem;
+       struct ofono_sms *sms;
+       struct sms_agent *agent;
+       unsigned int push_watch[2];
+};
+
+static void agent_exited(void *userdata)
+{
+       struct push_notification *pn = userdata;
+
+       if (pn->push_watch[0] > 0) {
+               __ofono_sms_datagram_watch_remove(pn->sms, pn->push_watch[0]);
+               pn->push_watch[0] = 0;
+       }
+
+       if (pn->push_watch[1] > 0) {
+               __ofono_sms_datagram_watch_remove(pn->sms, pn->push_watch[1]);
+               pn->push_watch[1] = 0;
+       }
+
+       pn->agent = NULL;
+}
+
+static void push_received(const char *from, const struct tm *remote,
+                               const struct tm *local, int dst, int src,
+                               const unsigned char *buffer,
+                               unsigned int len, void *data)
+{
+       struct push_notification *pn = data;
+
+       DBG("Received push of size: %u", len);
+
+       if (pn->agent == NULL)
+               return;
+
+       sms_agent_dispatch_datagram(pn->agent, "ReceiveNotification",
+                                       from, remote, local, buffer, len,
+                                       NULL, NULL, NULL);
+}
+
+static DBusMessage *push_notification_register_agent(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct push_notification *pn = data;
+       const char *agent_path;
+
+       if (pn->agent)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_dbus_valid_object_path(agent_path))
+               return __ofono_error_invalid_format(msg);
+
+       pn->agent = sms_agent_new(AGENT_INTERFACE,
+                                       dbus_message_get_sender(msg),
+                                       agent_path);
+
+       if (pn->agent == NULL)
+               return __ofono_error_failed(msg);
+
+       sms_agent_set_removed_notify(pn->agent, agent_exited, pn);
+
+       pn->push_watch[0] = __ofono_sms_datagram_watch_add(pn->sms,
+                                                       push_received,
+                                                       WAP_PUSH_DST_PORT,
+                                                       WAP_PUSH_SRC_PORT,
+                                                       pn, NULL);
+
+       pn->push_watch[1] = __ofono_sms_datagram_watch_add(pn->sms,
+                                                       push_received,
+                                                       WAP_PUSH_DST_PORT,
+                                                       0, pn, NULL);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *push_notification_unregister_agent(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct push_notification *pn = data;
+       const char *agent_path;
+       const char *agent_bus = dbus_message_get_sender(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (pn->agent == NULL)
+               return __ofono_error_failed(msg);
+
+       if (sms_agent_matches(pn->agent, agent_bus, agent_path) == FALSE)
+               return __ofono_error_failed(msg);
+
+       sms_agent_free(pn->agent);
+       pn->agent = NULL;
+
+       return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable push_notification_methods[] = {
+       { "RegisterAgent",    "o",   "",  push_notification_register_agent },
+       { "UnregisterAgent",  "o",   "",  push_notification_unregister_agent },
+       { }
+};
+
+static void push_notification_cleanup(gpointer user)
+{
+       struct push_notification *pn = user;
+
+       DBG("%p", pn);
+
+       /* The push watch was already cleaned up */
+       pn->push_watch[0] = 0;
+       pn->push_watch[1] = 0;
+       pn->sms = NULL;
+
+       sms_agent_free(pn->agent);
+
+       ofono_modem_remove_interface(pn->modem, PUSH_NOTIFICATION_INTERFACE);
+}
+
+static void sms_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct push_notification *pn = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               g_dbus_unregister_interface(conn,
+                                       ofono_modem_get_path(pn->modem),
+                                       PUSH_NOTIFICATION_INTERFACE);
+               return;
+       }
+
+       DBG("registered");
+       pn->sms = __ofono_atom_get_data(atom);
+
+       if (!g_dbus_register_interface(conn, ofono_modem_get_path(pn->modem),
+                                       PUSH_NOTIFICATION_INTERFACE,
+                                       push_notification_methods, NULL, NULL,
+                                       pn, push_notification_cleanup)) {
+               ofono_error("Could not create %s interface",
+                               PUSH_NOTIFICATION_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(pn->modem, PUSH_NOTIFICATION_INTERFACE);
+}
+
+static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
+{
+       struct push_notification *pn;
+       DBG("modem: %p, added: %d", modem, added);
+
+       if (added == FALSE)
+               return;
+
+       pn = g_try_new0(struct push_notification, 1);
+       if (pn == NULL)
+               return;
+
+       pn->modem = modem;
+       __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SMS,
+                                       sms_watch, pn, g_free);
+}
+
+static void call_modemwatch(struct ofono_modem *modem, void *user)
+{
+       modem_watch(modem, TRUE, user);
+}
+
+static int push_notification_init(void)
+{
+       DBG("");
+
+       modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
+
+       __ofono_modem_foreach(call_modemwatch, NULL);
+
+       return 0;
+}
+
+static void push_notification_exit(void)
+{
+       DBG("");
+
+       __ofono_modemwatch_remove(modemwatch_id);
+}
+
+OFONO_PLUGIN_DEFINE(push_notification, "Push Notification Plugin", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       push_notification_init, push_notification_exit)
diff --git a/plugins/samsung.c b/plugins/samsung.c
new file mode 100644 (file)
index 0000000..8e0d360
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/sim.h>
+#include <ofono/netreg.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct samsung_data {
+       GAtChat *chat;
+       gboolean have_sim;
+       struct at_util_sim_state_query *sim_state_query;
+};
+
+static void samsung_debug(const char *str, void *data)
+{
+       const char *prefix = data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int samsung_probe(struct ofono_modem *modem)
+{
+       struct samsung_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct samsung_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void samsung_remove(struct ofono_modem *modem)
+{
+       struct samsung_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup potential SIM state polling */
+       at_util_sim_state_query_free(data->sim_state_query);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->chat);
+
+       g_free(data);
+}
+
+static void mode_select(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct samsung_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (!ok) {
+               g_at_chat_unref(data->chat);
+               data->chat = NULL;
+
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       g_at_chat_send(data->chat, "AT+VERSNAME=1,0", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->chat, "AT+VERSNAME=1,1", NULL, NULL, NULL, NULL);
+
+       ofono_modem_set_powered(modem, TRUE);
+}
+
+static void sim_state_cb(gboolean present, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct samsung_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       at_util_sim_state_query_free(data->sim_state_query);
+       data->sim_state_query = NULL;
+
+       data->have_sim = present;
+
+       g_at_chat_send(data->chat, "AT+MODESELECT=3", none_prefix,
+                                               mode_select, modem, NULL);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct samsung_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (!ok) {
+               g_at_chat_unref(data->chat);
+               data->chat = NULL;
+
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       data->sim_state_query = at_util_sim_state_query_new(data->chat,
+                                               1, 5, sim_state_cb, modem);
+}
+
+static int samsung_enable(struct ofono_modem *modem)
+{
+       struct samsung_data *data = ofono_modem_get_data(modem);
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GHashTable *options;
+       const char *device;
+
+       device = ofono_modem_get_string(modem, "ControlPort");
+       if (device == NULL)
+               return -EINVAL;
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               return -ENOMEM;
+
+       g_hash_table_insert(options, "Baud", "115200");
+       g_hash_table_insert(options, "Parity", "none");
+       g_hash_table_insert(options, "StopBits", "1");
+       g_hash_table_insert(options, "DataBits", "8");
+       g_hash_table_insert(options, "XonXoff", "off");
+       g_hash_table_insert(options, "RtsCts", "on");
+       g_hash_table_insert(options, "Local", "on");
+       g_hash_table_insert(options, "Read", "on");
+
+       channel = g_at_tty_open(device, options);
+
+       g_hash_table_destroy(options);
+
+       if (channel == NULL)
+               return -EIO;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       data->chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (data->chat == NULL)
+               return -ENOMEM;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(data->chat, samsung_debug, "Device: ");
+
+       g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->chat, "AT+CMEE=1", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->chat, "AT+CFUN=?", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(data->chat, "AT+CFUN?", none_prefix, NULL, NULL, NULL);
+
+       g_at_chat_send(data->chat, "AT+CFUN=5", none_prefix,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct samsung_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int samsung_disable(struct ofono_modem *modem)
+{
+       struct samsung_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->chat);
+       g_at_chat_unregister_all(data->chat);
+
+       g_at_chat_send(data->chat, "AT+MODESELECT=2", none_prefix,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void samsung_pre_sim(struct ofono_modem *modem)
+{
+       struct samsung_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+       sim = ofono_sim_create(modem, 0, "atmodem", data->chat);
+
+       if (sim && data->have_sim == TRUE)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void samsung_post_sim(struct ofono_modem *modem)
+{
+       DBG("%p", modem);
+}
+
+static void samsung_post_online(struct ofono_modem *modem)
+{
+       struct samsung_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_SAMSUNG, "atmodem", data->chat);
+}
+
+static struct ofono_modem_driver samsung_driver = {
+       .name           = "samsung",
+       .probe          = samsung_probe,
+       .remove         = samsung_remove,
+       .enable         = samsung_enable,
+       .disable        = samsung_disable,
+       .pre_sim        = samsung_pre_sim,
+       .post_sim       = samsung_post_sim,
+       .post_online    = samsung_post_online,
+};
+
+static int samsung_init(void)
+{
+       return ofono_modem_driver_register(&samsung_driver);
+}
+
+static void samsung_exit(void)
+{
+       ofono_modem_driver_unregister(&samsung_driver);
+}
+
+OFONO_PLUGIN_DEFINE(samsung, "Samsung modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, samsung_init, samsung_exit)
diff --git a/plugins/sap.c b/plugins/sap.c
new file mode 100644 (file)
index 0000000..d893bc1
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010-2011  ProFUSION embedded systems
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+
+#include "bluetooth.h"
+#include "util.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define BLUEZ_SERIAL_INTERFACE BLUEZ_SERVICE ".Serial"
+
+static DBusConnection *connection;
+static GHashTable *modem_hash = NULL;
+static struct ofono_modem *sap_hw_modem = NULL;
+static struct bluetooth_sap_driver *sap_hw_driver = NULL;
+
+struct sap_data {
+       struct ofono_modem *hw_modem;
+       struct bluetooth_sap_driver *sap_driver;
+       DBusPendingCall *call;
+};
+
+int bluetooth_sap_client_register(struct bluetooth_sap_driver *sap,
+                                       struct ofono_modem *modem)
+{
+       if (sap_hw_modem != NULL)
+               return -EPERM;
+
+       sap_hw_modem = modem;
+       sap_hw_driver = sap;
+
+       bluetooth_get_properties();
+
+       return 0;
+}
+
+void bluetooth_sap_client_unregister(struct ofono_modem *modem)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       if (sap_hw_modem == NULL)
+               return;
+
+       g_hash_table_iter_init(&iter, modem_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               g_hash_table_iter_remove(&iter);
+
+               ofono_modem_remove(value);
+       }
+
+       sap_hw_modem = NULL;
+       sap_hw_driver = NULL;
+}
+
+static int sap_probe(struct ofono_modem *modem)
+{
+       struct sap_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct sap_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void sap_remove(struct ofono_modem *modem)
+{
+       struct sap_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (data->call != NULL)
+               dbus_pending_call_cancel(data->call);
+
+       g_free(data);
+
+       ofono_modem_set_data(modem, NULL);
+}
+
+static void sap_connect_reply(DBusPendingCall *call, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct sap_data *data = ofono_modem_get_data(modem);
+       DBusError derr;
+       DBusMessage *reply;
+       int fd, err;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       data->call = NULL;
+
+       if (ofono_modem_get_powered(modem))
+               goto done;
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+
+               DBG("Connect reply: %s", derr.message);
+
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                               DBUS_TYPE_INVALID))
+               goto done;
+
+       data->hw_modem = sap_hw_modem;
+       data->sap_driver = sap_hw_driver;
+
+       err = data->sap_driver->enable(data->hw_modem, modem, fd);
+       if (!err || err == -EINPROGRESS) {
+               dbus_message_unref(reply);
+               return;
+       }
+
+done:
+       ofono_modem_set_powered(modem, FALSE);
+       dbus_message_unref(reply);
+}
+
+/* power up hardware */
+static int sap_enable(struct ofono_modem *modem)
+{
+       struct sap_data *data = ofono_modem_get_data(modem);
+       DBusPendingCall *call;
+       int status;
+       const char *str = "sap";
+       const char *server_path = ofono_modem_get_string(modem, "ServerPath");
+
+       DBG("%p", modem);
+
+       status = bluetooth_send_with_reply(server_path, BLUEZ_SERIAL_INTERFACE,
+                                       "ConnectFD", &call, sap_connect_reply,
+                                       modem, NULL, DBUS_TIMEOUT,
+                                       DBUS_TYPE_STRING, &str,
+                                       DBUS_TYPE_INVALID);
+
+       if (status < 0)
+               return -EINVAL;
+
+       data->call = call;
+
+       return -EINPROGRESS;
+}
+
+static int sap_disable(struct ofono_modem *modem)
+{
+       struct sap_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       return data->sap_driver->disable(data->hw_modem);
+}
+
+static void sap_pre_sim(struct ofono_modem *modem)
+{
+       struct sap_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->sap_driver->pre_sim(data->hw_modem);
+}
+
+static void sap_post_sim(struct ofono_modem *modem)
+{
+       struct sap_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->sap_driver->post_sim(data->hw_modem);
+}
+
+static void sap_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct sap_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->sap_driver->set_online(data->hw_modem, online, cb, user_data);
+}
+
+static void sap_post_online(struct ofono_modem *modem)
+{
+       struct sap_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->sap_driver->post_online(data->hw_modem);
+}
+
+static int bluetooth_sap_probe(const char *device, const char *dev_addr,
+                               const char *adapter_addr, const char *alias)
+{
+       struct ofono_modem *modem;
+       char buf[256];
+
+       if (sap_hw_modem == NULL)
+               return -ENODEV;
+
+       /* We already have this device in our hash, ignore */
+       if (g_hash_table_lookup(modem_hash, device) != NULL)
+               return -EALREADY;
+
+       ofono_info("Using device: %s, devaddr: %s, adapter: %s",
+                       device, dev_addr, adapter_addr);
+
+       strcpy(buf, "sap/");
+       bluetooth_create_path(dev_addr, adapter_addr, buf + 4,
+                                               sizeof(buf) - 4);
+
+       modem = ofono_modem_create(buf, "sap");
+       if (modem == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_string(modem, "ServerPath", device);
+       ofono_modem_set_name(modem, alias);
+       ofono_modem_register(modem);
+
+       g_hash_table_insert(modem_hash, g_strdup(device), modem);
+
+       return 0;
+}
+
+static void bluetooth_sap_remove(const char *prefix)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("%s", prefix);
+
+       if (modem_hash == NULL)
+               return;
+
+       g_hash_table_iter_init(&iter, modem_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               if (prefix && g_str_has_prefix((char *)key, prefix) == FALSE)
+                       continue;
+
+               g_hash_table_iter_remove(&iter);
+
+               ofono_modem_remove(value);
+       }
+}
+
+static void bluetooth_sap_set_alias(const char *device, const char *alias)
+{
+       struct ofono_modem *modem;
+
+       if (device == NULL || alias == NULL)
+               return;
+
+       modem = g_hash_table_lookup(modem_hash, device);
+       if (modem == NULL)
+               return;
+
+       ofono_modem_set_name(modem, alias);
+}
+
+static struct ofono_modem_driver sap_driver = {
+       .name           = "sap",
+       .modem_type     = OFONO_MODEM_TYPE_SAP,
+       .probe          = sap_probe,
+       .remove         = sap_remove,
+       .enable         = sap_enable,
+       .disable        = sap_disable,
+       .pre_sim        = sap_pre_sim,
+       .post_sim       = sap_post_sim,
+       .set_online     = sap_set_online,
+       .post_online    = sap_post_online,
+};
+
+static struct bluetooth_profile sap = {
+       .name           = "sap",
+       .probe          = bluetooth_sap_probe,
+       .remove         = bluetooth_sap_remove,
+       .set_alias      = bluetooth_sap_set_alias,
+};
+
+static int sap_init(void)
+{
+       int err;
+
+       if (DBUS_TYPE_UNIX_FD < 0)
+               return -EBADF;
+
+       connection = ofono_dbus_get_connection();
+
+       err = ofono_modem_driver_register(&sap_driver);
+       if (err < 0)
+               return err;
+
+       err = bluetooth_register_uuid(SAP_UUID, &sap);
+       if (err < 0) {
+               ofono_modem_driver_unregister(&sap_driver);
+               return err;
+       }
+
+       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, NULL);
+
+       return 0;
+}
+
+static void sap_exit(void)
+{
+       DBG("");
+
+       bluetooth_unregister_uuid(SAP_UUID);
+       ofono_modem_driver_unregister(&sap_driver);
+       g_hash_table_destroy(modem_hash);
+       modem_hash = NULL;
+}
+
+OFONO_PLUGIN_DEFINE(sap, "Sim Access Profile Plugins", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, sap_init, sap_exit)
diff --git a/plugins/sierra.c b/plugins/sierra.c
new file mode 100644 (file)
index 0000000..b33382d
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/gprs.h>
+#include <ofono/phonebook.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct sierra_data {
+       GAtChat *chat;
+};
+
+static void sierra_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int sierra_probe(struct ofono_modem *modem)
+{
+       struct sierra_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct sierra_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void sierra_remove(struct ofono_modem *modem)
+{
+       struct sierra_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->chat);
+
+       g_free(data);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsmv1();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, sierra_debug, debug);
+
+       return chat;
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct sierra_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->chat);
+               data->chat = NULL;
+       }
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static int sierra_enable(struct ofono_modem *modem)
+{
+       struct sierra_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->chat = open_device(modem, "Device", "Device: ");
+       if (data->chat == NULL)
+               return -EINVAL;
+
+       g_at_chat_send(data->chat, "ATE0 &C0 +CMEE=1", NULL,
+                                               NULL, NULL, NULL);
+
+       g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct sierra_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int sierra_disable(struct ofono_modem *modem)
+{
+       struct sierra_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->chat);
+       g_at_chat_unregister_all(data->chat);
+
+       g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void sierra_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct sierra_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(data->chat, command, none_prefix,
+                                       set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void sierra_pre_sim(struct ofono_modem *modem)
+{
+       struct sierra_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+       sim = ofono_sim_create(modem, OFONO_VENDOR_SIERRA,
+                                       "atmodem", data->chat);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void sierra_post_sim(struct ofono_modem *modem)
+{
+       struct sierra_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->chat);
+}
+
+static void sierra_post_online(struct ofono_modem *modem)
+{
+       struct sierra_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, 0, "atmodem", data->chat);
+
+       ofono_gprs_create(modem, 0, "atmodem", data->chat);
+}
+
+static struct ofono_modem_driver sierra_driver = {
+       .name           = "sierra",
+       .probe          = sierra_probe,
+       .remove         = sierra_remove,
+       .enable         = sierra_enable,
+       .disable        = sierra_disable,
+       .set_online     = sierra_set_online,
+       .pre_sim        = sierra_pre_sim,
+       .post_sim       = sierra_post_sim,
+       .post_online    = sierra_post_online,
+};
+
+static int sierra_init(void)
+{
+       return ofono_modem_driver_register(&sierra_driver);
+}
+
+static void sierra_exit(void)
+{
+       ofono_modem_driver_unregister(&sierra_driver);
+}
+
+OFONO_PLUGIN_DEFINE(sierra, "Sierra Wireless modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, sierra_init, sierra_exit)
diff --git a/plugins/sim900.c b/plugins/sim900.c
new file mode 100644 (file)
index 0000000..bc5f9c5
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/phonebook.h>
+#include <ofono/history.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct sim900_data {
+       GAtChat *modem;
+};
+
+static int sim900_probe(struct ofono_modem *modem)
+{
+       struct sim900_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct sim900_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void sim900_remove(struct ofono_modem *modem)
+{
+       struct sim900_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_at_chat_unref(data->modem);
+
+       g_free(data);
+}
+
+static void sim900_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GAtChat *chat;
+       GHashTable *options;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               return NULL;
+
+       g_hash_table_insert(options, "Baud", "115200");
+       g_hash_table_insert(options, "Parity", "none");
+       g_hash_table_insert(options, "StopBits", "1");
+       g_hash_table_insert(options, "DataBits", "8");
+       g_hash_table_insert(options, "XonXoff", "off");
+       g_hash_table_insert(options, "Local", "off");
+       g_hash_table_insert(options, "RtsCts", "off");
+
+       channel = g_at_tty_open(device, options);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, sim900_debug, debug);
+
+       return chat;
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct sim900_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+       }
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static int sim900_enable(struct ofono_modem *modem)
+{
+       struct sim900_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->modem = open_device(modem, "Device", "Device: ");
+       if (data->modem == NULL) {
+               DBG("return -EINVAL");
+               return -EINVAL;
+       }
+
+       g_at_chat_send(data->modem, "ATE0", NULL, NULL, NULL, NULL);
+
+       /* For obtain correct sms service number */
+       g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", NULL,
+                                       NULL, NULL, NULL);
+
+       g_at_chat_send(data->modem, "AT+CFUN=1", none_prefix,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct sim900_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int sim900_disable(struct ofono_modem *modem)
+{
+       struct sim900_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_send(data->modem, "AT+CFUN=4", none_prefix,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void sim900_pre_sim(struct ofono_modem *modem)
+{
+       struct sim900_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->modem);
+       sim = ofono_sim_create(modem, 0, "atmodem", data->modem);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void sim900_post_sim(struct ofono_modem *modem)
+{
+       struct sim900_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->modem);
+       ofono_sms_create(modem, 0, "atmodem", data->modem);
+}
+
+static void sim900_post_online(struct ofono_modem *modem)
+{
+       struct sim900_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_SIMCOM, "atmodem", data->modem);
+}
+
+static struct ofono_modem_driver sim900_driver = {
+       .name           = "sim900",
+       .probe          = sim900_probe,
+       .remove         = sim900_remove,
+       .enable         = sim900_enable,
+       .disable        = sim900_disable,
+       .pre_sim        = sim900_pre_sim,
+       .post_sim       = sim900_post_sim,
+       .post_online    = sim900_post_online,
+};
+
+static int sim900_init(void)
+{
+       return ofono_modem_driver_register(&sim900_driver);
+}
+
+static void sim900_exit(void)
+{
+       ofono_modem_driver_unregister(&sim900_driver);
+}
+
+OFONO_PLUGIN_DEFINE(sim900, "SIM900 modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, sim900_init, sim900_exit)
diff --git a/plugins/smart-messaging.c b/plugins/smart-messaging.c
new file mode 100644 (file)
index 0000000..d6d77cf
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/dbus.h>
+#include "smsagent.h"
+#include "smsutil.h"
+#include "common.h"
+
+#define SMART_MESSAGING_INTERFACE "org.ofono.SmartMessaging"
+#define AGENT_INTERFACE "org.ofono.SmartMessagingAgent"
+
+#define VCARD_SRC_PORT -1
+#define VCARD_DST_PORT 9204
+
+#define VCAL_SRC_PORT -1
+#define VCAL_DST_PORT 9205
+
+static unsigned int modemwatch_id;
+
+struct smart_messaging {
+       struct ofono_modem *modem;
+       struct ofono_sms *sms;
+       struct sms_agent *agent;
+       unsigned int vcard_watch;
+       unsigned int vcal_watch;
+};
+
+static void agent_exited(void *userdata)
+{
+       struct smart_messaging *sm = userdata;
+
+       if (sm->vcard_watch > 0) {
+               __ofono_sms_datagram_watch_remove(sm->sms, sm->vcard_watch);
+               sm->vcard_watch = 0;
+       }
+
+       if (sm->vcal_watch > 0) {
+               __ofono_sms_datagram_watch_remove(sm->sms, sm->vcal_watch);
+               sm->vcal_watch = 0;
+       }
+
+       sm->agent = NULL;
+}
+
+static void vcard_received(const char *from, const struct tm *remote,
+                               const struct tm *local, int dst, int src,
+                               const unsigned char *buffer,
+                               unsigned int len, void *data)
+{
+       struct smart_messaging *sm = data;
+
+       if (sm->agent == NULL)
+               return;
+
+       sms_agent_dispatch_datagram(sm->agent, "ReceiveBusinessCard",
+                                       from, remote, local, buffer, len,
+                                       NULL, NULL, NULL);
+}
+
+static void vcal_received(const char *from, const struct tm *remote,
+                               const struct tm *local, int dst, int src,
+                               const unsigned char *buffer,
+                               unsigned int len, void *data)
+{
+       struct smart_messaging *sm = data;
+
+       if (sm->agent == NULL)
+               return;
+
+       sms_agent_dispatch_datagram(sm->agent, "ReceiveAppointment",
+                                       from, remote, local, buffer, len,
+                                       NULL, NULL, NULL);
+}
+
+static DBusMessage *smart_messaging_register_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct smart_messaging *sm = data;
+       const char *agent_path;
+
+       if (sm->agent)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_dbus_valid_object_path(agent_path))
+               return __ofono_error_invalid_format(msg);
+
+       sm->agent = sms_agent_new(AGENT_INTERFACE,
+                                       dbus_message_get_sender(msg),
+                                       agent_path);
+
+       if (sm->agent == NULL)
+               return __ofono_error_failed(msg);
+
+       sms_agent_set_removed_notify(sm->agent, agent_exited, sm);
+
+       sm->vcard_watch = __ofono_sms_datagram_watch_add(sm->sms,
+                                                       vcard_received,
+                                                       VCARD_DST_PORT,
+                                                       VCARD_SRC_PORT,
+                                                       sm, NULL);
+
+       sm->vcal_watch = __ofono_sms_datagram_watch_add(sm->sms,
+                                                       vcal_received,
+                                                       VCAL_DST_PORT,
+                                                       VCAL_SRC_PORT,
+                                                       sm, NULL);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *smart_messaging_unregister_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct smart_messaging *sm = data;
+       const char *agent_path;
+       const char *agent_bus = dbus_message_get_sender(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (sm->agent == NULL)
+               return __ofono_error_failed(msg);
+
+       if (sms_agent_matches(sm->agent, agent_bus, agent_path) == FALSE)
+               return __ofono_error_failed(msg);
+
+       sms_agent_free(sm->agent);
+       sm->agent = NULL;
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void message_queued(struct ofono_sms *sms,
+                               const struct ofono_uuid *uuid, void *data)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *msg = data;
+       const char *path;
+
+       path = __ofono_sms_message_path_from_uuid(sms, uuid);
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *smart_messaging_send_vcard(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct smart_messaging *sm = data;
+       const char *to;
+       unsigned char *bytes;
+       int len;
+       GSList *msg_list;
+       unsigned int flags;
+       gboolean use_16bit_ref = FALSE;
+       int err;
+       struct ofono_uuid uuid;
+       unsigned short ref;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to,
+                                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                       &bytes, &len, DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       if (valid_phone_number_format(to) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       ref = __ofono_sms_get_next_ref(sm->sms);
+       msg_list = sms_datagram_prepare(to, bytes, len, ref, use_16bit_ref,
+                                               0, VCARD_DST_PORT, TRUE, FALSE);
+
+       if (msg_list == NULL)
+               return __ofono_error_invalid_format(msg);
+
+       flags = OFONO_SMS_SUBMIT_FLAG_RETRY | OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS;
+
+       err = __ofono_sms_txq_submit(sm->sms, msg_list, flags, &uuid,
+                                       message_queued, msg);
+
+       g_slist_foreach(msg_list, (GFunc)g_free, NULL);
+       g_slist_free(msg_list);
+
+       if (err < 0)
+               return __ofono_error_failed(msg);
+
+       return NULL;
+}
+
+static DBusMessage *smart_messaging_send_vcal(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct smart_messaging *sm = data;
+       const char *to;
+       unsigned char *bytes;
+       int len;
+       GSList *msg_list;
+       unsigned int flags;
+       gboolean use_16bit_ref = FALSE;
+       int err;
+       struct ofono_uuid uuid;
+       unsigned short ref;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to,
+                                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                       &bytes, &len, DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       if (valid_phone_number_format(to) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       ref = __ofono_sms_get_next_ref(sm->sms);
+       msg_list = sms_datagram_prepare(to, bytes, len, ref, use_16bit_ref,
+                                               0, VCAL_DST_PORT, TRUE, FALSE);
+
+       if (msg_list == NULL)
+               return __ofono_error_invalid_format(msg);
+
+       flags = OFONO_SMS_SUBMIT_FLAG_RETRY | OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS;
+
+       err = __ofono_sms_txq_submit(sm->sms, msg_list, flags, &uuid,
+                                       message_queued, msg);
+
+       g_slist_foreach(msg_list, (GFunc)g_free, NULL);
+       g_slist_free(msg_list);
+
+       if (err < 0)
+               return __ofono_error_failed(msg);
+
+       return NULL;
+}
+
+static GDBusMethodTable smart_messaging_methods[] = {
+       { "RegisterAgent",    "o",     "",  smart_messaging_register_agent },
+       { "UnregisterAgent",  "o",     "",  smart_messaging_unregister_agent },
+       { "SendBusinessCard", "say",   "o", smart_messaging_send_vcard,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SendAppointment",  "say",   "o", smart_messaging_send_vcal,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static void smart_messaging_cleanup(gpointer user)
+{
+       struct smart_messaging *sm = user;
+
+       DBG("%p", sm);
+
+       sm->vcard_watch = 0;
+       sm->vcal_watch = 0;
+       sm->sms = NULL;
+
+       sms_agent_free(sm->agent);
+
+       ofono_modem_remove_interface(sm->modem, SMART_MESSAGING_INTERFACE);
+}
+
+static void sms_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct smart_messaging *sm = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               g_dbus_unregister_interface(conn,
+                                       ofono_modem_get_path(sm->modem),
+                                       SMART_MESSAGING_INTERFACE);
+
+               return;
+       }
+
+       DBG("registered");
+       sm->sms = __ofono_atom_get_data(atom);
+
+       if (!g_dbus_register_interface(conn, ofono_modem_get_path(sm->modem),
+                                       SMART_MESSAGING_INTERFACE,
+                                       smart_messaging_methods, NULL, NULL,
+                                       sm, smart_messaging_cleanup)) {
+               ofono_error("Could not create %s interface",
+                               SMART_MESSAGING_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(sm->modem, SMART_MESSAGING_INTERFACE);
+}
+
+static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
+{
+       struct smart_messaging *sm;
+       DBG("modem: %p, added: %d", modem, added);
+
+       if (added == FALSE)
+               return;
+
+       sm = g_try_new0(struct smart_messaging, 1);
+       if (sm == NULL)
+               return;
+
+       sm->modem = modem;
+       __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SMS,
+                                       sms_watch, sm, g_free);
+}
+
+static void call_modemwatch(struct ofono_modem *modem, void *user)
+{
+       modem_watch(modem, TRUE, user);
+}
+
+static int smart_messaging_init(void)
+{
+       DBG("");
+
+       modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
+
+       __ofono_modem_foreach(call_modemwatch, NULL);
+
+       return 0;
+}
+
+static void smart_messaging_exit(void)
+{
+       DBG("");
+
+       __ofono_modemwatch_remove(modemwatch_id);
+}
+
+OFONO_PLUGIN_DEFINE(smart_messaging, "Smart Messaging Plugin", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       smart_messaging_init, smart_messaging_exit)
diff --git a/plugins/speedup.c b/plugins/speedup.c
new file mode 100644 (file)
index 0000000..ca6ed13
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/phonebook.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct speedup_data {
+       GAtChat *modem;
+       GAtChat *aux;
+       gboolean have_sim;
+       struct at_util_sim_state_query *sim_state_query;
+};
+
+static int speedup_probe(struct ofono_modem *modem)
+{
+       struct speedup_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct speedup_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void speedup_remove(struct ofono_modem *modem)
+{
+       struct speedup_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup potential SIM state polling */
+       at_util_sim_state_query_free(data->sim_state_query);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->aux);
+
+       g_free(data);
+}
+
+static void speedup_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, speedup_debug, debug);
+
+       return chat;
+}
+
+static void sim_state_cb(gboolean present, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct speedup_data *data = ofono_modem_get_data(modem);
+
+       at_util_sim_state_query_free(data->sim_state_query);
+       data->sim_state_query = NULL;
+
+       data->have_sim = present;
+
+       ofono_modem_set_powered(modem, TRUE);
+
+       /* AT&C0 needs to be send separate and on both channel */
+       g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "AT&C0", NULL, NULL, NULL, NULL);
+
+       /*
+        * Ensure that the modem is using GSM character set and not IRA,
+        * otherwise weirdness with umlauts and other non-ASCII characters
+        * can result
+        */
+       g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct speedup_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       data->sim_state_query = at_util_sim_state_query_new(data->aux,
+                                               2, 20, sim_state_cb, modem);
+}
+
+static int speedup_enable(struct ofono_modem *modem)
+{
+       struct speedup_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               return -EINVAL;
+
+       data->aux = open_device(modem, "Aux", "Aux: ");
+       if (data->aux == NULL) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+               return -EIO;
+       }
+
+       g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "AT+CFUN=1", NULL,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct speedup_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int speedup_disable(struct ofono_modem *modem)
+{
+       struct speedup_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       g_at_chat_send(data->aux, "AT+CFUN=0", NULL,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void speedup_pre_sim(struct ofono_modem *modem)
+{
+       struct speedup_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->aux);
+       sim = ofono_sim_create(modem, OFONO_VENDOR_SPEEDUP,
+                                               "atmodem", data->aux);
+
+       if (sim && data->have_sim == TRUE)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void speedup_post_sim(struct ofono_modem *modem)
+{
+       struct speedup_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->aux);
+
+       ofono_sms_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                               "atmodem", data->aux);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_SPEEDUP,
+                                               "atmodem", data->aux);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+}
+
+static void speedup_post_online(struct ofono_modem *modem)
+{
+       struct speedup_data *data = ofono_modem_get_data(modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_SPEEDUP, "atmodem", data->aux);
+
+       ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                               "atmodem", data->aux);
+       ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                               "atmodem", data->aux);
+}
+
+static struct ofono_modem_driver speedup_driver = {
+       .name           = "speedup",
+       .probe          = speedup_probe,
+       .remove         = speedup_remove,
+       .enable         = speedup_enable,
+       .disable        = speedup_disable,
+       .pre_sim        = speedup_pre_sim,
+       .post_sim       = speedup_post_sim,
+       .post_online    = speedup_post_online,
+};
+
+static int speedup_init(void)
+{
+       return ofono_modem_driver_register(&speedup_driver);
+}
+
+static void speedup_exit(void)
+{
+       ofono_modem_driver_unregister(&speedup_driver);
+}
+
+OFONO_PLUGIN_DEFINE(speedup, "Speed Up modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, speedup_init, speedup_exit)
diff --git a/plugins/speedupcdma.c b/plugins/speedupcdma.c
new file mode 100644 (file)
index 0000000..8e5f324
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/cdma-netreg.h>
+#include <ofono/cdma-connman.h>
+#include <ofono/log.h>
+
+#include "drivers/atmodem/vendor.h"
+
+struct speedupcdma_data {
+       GAtChat *modem;
+       GAtChat *aux;
+};
+
+static void speedupcdma_debug(const char *str, void *data)
+{
+       const char *prefix = data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int speedupcdma_probe(struct ofono_modem *modem)
+{
+       struct speedupcdma_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct speedupcdma_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void speedupcdma_remove(struct ofono_modem *modem)
+{
+       struct speedupcdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->aux);
+
+       g_free(data);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct speedupcdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+       }
+
+       ofono_modem_set_powered(modem, ok);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, speedupcdma_debug, debug);
+
+       return chat;
+}
+
+static int speedupcdma_enable(struct ofono_modem *modem)
+{
+       struct speedupcdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               return -EINVAL;
+
+       data->aux = open_device(modem, "Aux", "Aux: ");
+       if (data->aux == NULL) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+               return -EIO;
+       }
+
+       g_at_chat_set_slave(data->modem, data->aux);
+
+       g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(data->aux, "AT+CFUN=1", NULL,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct speedupcdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int speedupcdma_disable(struct ofono_modem *modem)
+{
+       struct speedupcdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       g_at_chat_send(data->aux, "AT+CFUN=0", NULL,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void speedupcdma_pre_sim(struct ofono_modem *modem)
+{
+       struct speedupcdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "cdmamodem", data->aux);
+}
+
+static void speedupcdma_post_sim(struct ofono_modem *modem)
+{
+       DBG("%p", modem);
+}
+
+static void speedupcdma_post_online(struct ofono_modem *modem)
+{
+       struct speedupcdma_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_cdma_netreg_create(modem, 0, "huaweicdmamodem", data->aux);
+
+       ofono_cdma_connman_create(modem, OFONO_VENDOR_HUAWEI, "cdmamodem",
+                                       data->modem);
+}
+
+static struct ofono_modem_driver speedupcdma_driver = {
+       .name           = "speedupcdma",
+       .probe          = speedupcdma_probe,
+       .remove         = speedupcdma_remove,
+       .enable         = speedupcdma_enable,
+       .disable        = speedupcdma_disable,
+       .pre_sim        = speedupcdma_pre_sim,
+       .post_sim       = speedupcdma_post_sim,
+       .post_online    = speedupcdma_post_online,
+};
+
+static int speedupcdma_init(void)
+{
+       return ofono_modem_driver_register(&speedupcdma_driver);
+}
+
+static void speedupcdma_exit(void)
+{
+       ofono_modem_driver_unregister(&speedupcdma_driver);
+}
+
+OFONO_PLUGIN_DEFINE(speedupcdma, "Speed Up CDMA modem driver", VERSION,
+                               OFONO_PLUGIN_PRIORITY_DEFAULT,
+                               speedupcdma_init, speedupcdma_exit)
diff --git a/plugins/ste.c b/plugins/ste.c
new file mode 100644 (file)
index 0000000..900e20d
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <net/if.h>
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/call-volume.h>
+#include <ofono/voicecall.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/radio-settings.h>
+#include <ofono/stk.h>
+#include <ofono/gnss.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+#include <drivers/stemodem/caif_socket.h>
+#include <drivers/stemodem/if_caif.h>
+
+#define NUM_CHAT       6
+#define AT_DEFAULT     0
+#define AT_NET         1
+#define AT_VOICE       2
+#define AT_GPRS        3
+#define AT_SIM         4
+#define AT_GNSS        5
+
+#define MAX_PDP_CONTEXTS       4
+
+static char *chat_prefixes[NUM_CHAT] = { "Default: ", "Net: ", "Voice: ",
+                                        "GPRS: ", "SIM: ", "GNSS:" };
+
+struct ste_data {
+       GAtChat *chat[NUM_CHAT];
+       gboolean have_sim;
+       struct ofono_sim *sim;
+};
+
+enum ste_sim_state {
+       SIM_STATE_NULL = 0,
+       SIM_STATE_AWAITING_APP,
+       SIM_STATE_BLOCKED,
+       SIM_STATE_BLOCKED_FOREVER,
+       SIM_STATE_WAIT_FOR_PIN,
+       SIM_STATE_ACTIVE,
+       SIM_STATE_TERMINATING,
+       SIM_STATE_POWER_OFF
+};
+
+static int ste_probe(struct ofono_modem *modem)
+{
+       struct ste_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct ste_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void ste_remove(struct ofono_modem *modem)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       for (i = 0; i < NUM_CHAT; i++)
+               g_at_chat_unref(data->chat[i]);
+
+       g_free(data);
+}
+
+static void ste_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static void handle_sim_status(int status, struct ofono_modem *modem)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+       DBG("SIM status:%d\n", status);
+
+       switch (status) {
+       case SIM_STATE_WAIT_FOR_PIN:
+       case SIM_STATE_ACTIVE:
+       case SIM_STATE_NULL:
+       case SIM_STATE_AWAITING_APP:
+       case SIM_STATE_BLOCKED:
+       case SIM_STATE_BLOCKED_FOREVER:
+       case SIM_STATE_TERMINATING:
+               if (data->have_sim == FALSE) {
+                       if (data->sim)
+                               ofono_sim_inserted_notify(data->sim, TRUE);
+
+                       data->have_sim = TRUE;
+               }
+               break;
+       case SIM_STATE_POWER_OFF:
+               if (data->have_sim == TRUE) {
+                       if (data->sim)
+                               ofono_sim_inserted_notify(data->sim, FALSE);
+
+                       data->have_sim = FALSE;
+               }
+               break;
+       }
+}
+
+static void handle_sim_state(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       int simnr, status;
+       GAtResultIter iter;
+
+       DBG("ok:%d", ok);
+
+       if (!ok)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       ofono_modem_set_powered(modem, TRUE);
+
+       if (!g_at_result_iter_next(&iter, "*ESIMSR:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &simnr))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &status))
+               return;
+
+       handle_sim_status(status, modem);
+}
+
+static gboolean init_sim_reporting(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ste_data *data = ofono_modem_get_data(modem);
+
+       data->have_sim = FALSE;
+
+       g_at_chat_send(data->chat[AT_SIM], "AT*ESIMSR=1;*ESIMSR?", NULL,
+                       handle_sim_state, modem, NULL);
+
+       return FALSE;
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ste_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("");
+
+       if (!ok) {
+               ofono_modem_set_powered(modem, FALSE);
+
+               for (i = 0; i < NUM_CHAT; i++) {
+                       g_at_chat_cancel_all(data->chat[i]);
+                       g_at_chat_unregister_all(data->chat[i]);
+                       g_at_chat_unref(data->chat[i]);
+                       data->chat[i] = NULL;
+               }
+
+               return;
+       }
+
+       init_sim_reporting(modem);
+}
+
+static GIOChannel *ste_create_channel(struct ofono_modem *modem)
+{
+       GIOChannel *channel;
+       const char *device;
+       int fd;
+
+       DBG("%p", modem);
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL) {
+               struct sockaddr_caif addr;
+               int err;
+               const char *interface;
+
+               /* Create a CAIF socket for AT Service */
+               fd = socket(AF_CAIF, SOCK_STREAM, CAIFPROTO_AT);
+               if (fd < 0) {
+                       ofono_error("Failed to create CAIF socket for AT");
+                       return NULL;
+               }
+
+               /* Bind CAIF socket to specified interface */
+               interface = ofono_modem_get_string(modem, "Interface");
+               if (interface) {
+                       struct ifreq ifreq;
+
+                       memset(&ifreq, 0, sizeof(ifreq));
+                       strcpy(ifreq.ifr_name, interface);
+                       err = setsockopt(fd, SOL_SOCKET,
+                                       SO_BINDTODEVICE, &ifreq, sizeof(ifreq));
+                       if (err < 0) {
+                               ofono_error("Failed to bind caif socket "
+                                       "to interface");
+                               close(fd);
+                               return NULL;
+                       }
+               }
+
+               memset(&addr, 0, sizeof(addr));
+               addr.family = AF_CAIF;
+               addr.u.at.type = CAIF_ATTYPE_PLAIN;
+
+               /* Connect to the AT Service at the modem */
+               err = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+               if (err < 0) {
+                       ofono_error("Failed to connect CAIF socket for AT");
+                       close(fd);
+                       return NULL;
+               }
+       } else {
+               fd = open(device, O_RDWR);
+               if (fd < 0) {
+                       ofono_error("Failed to open device %s", device);
+                       return NULL;
+               }
+       }
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL)  {
+               close(fd);
+               return NULL;
+       }
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       return channel;
+}
+
+static void esimsr_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       int status;
+       GAtResultIter iter;
+       DBG("");
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "*ESIMSR:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &status))
+               return;
+
+       handle_sim_status(status, modem);
+}
+
+static int ste_enable(struct ofono_modem *modem)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       int i;
+
+       for (i = 0; i < NUM_CHAT; i++) {
+               channel = ste_create_channel(modem);
+               syntax = g_at_syntax_new_gsm_permissive();
+               data->chat[i] = g_at_chat_new_blocking(channel, syntax);
+
+               if (data->chat[i] == NULL) {
+                       g_io_channel_unref(channel);
+                       g_at_syntax_unref(syntax);
+                       DBG("Failed to create AT chat %s", chat_prefixes[i]);
+                       goto error;
+               }
+
+               if (getenv("OFONO_AT_DEBUG"))
+                       g_at_chat_set_debug(data->chat[i], ste_debug,
+                                               chat_prefixes[i]);
+
+               g_at_chat_send(data->chat[i], "AT&F E0 V1 X4 &C1 +CMEE=1",
+                               NULL, NULL, NULL, NULL);
+
+               /* All STE modems support UTF-8 */
+               g_at_chat_send(data->chat[i], "AT+CSCS=\"UTF-8\"",
+                               NULL, NULL, NULL, NULL);
+
+               g_io_channel_unref(channel);
+               g_at_syntax_unref(syntax);
+       }
+
+       g_at_chat_send(data->chat[AT_DEFAULT], "AT+CFUN=4", NULL, cfun_enable,
+                               modem, NULL);
+
+       g_at_chat_register(data->chat[AT_SIM], "*ESIMSR:", esimsr_notify,
+                               FALSE, modem, NULL);
+
+       return -EINPROGRESS;
+
+error:
+       /* Unref open chats if any */
+       while (i--) {
+               g_at_chat_unref(data->chat[i]);
+               data->chat[i] = NULL;
+       }
+
+       return -EIO;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct ste_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("");
+
+       for (i = 0; i < NUM_CHAT; i++) {
+               g_at_chat_unref(data->chat[i]);
+               data->chat[i] = NULL;
+       }
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static int ste_disable(struct ofono_modem *modem)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+       int i;
+
+       DBG("%p", modem);
+
+       for (i = 0; i < NUM_CHAT; i++) {
+               g_at_chat_cancel_all(data->chat[i]);
+               g_at_chat_unregister_all(data->chat[i]);
+       }
+       g_at_chat_send(data->chat[AT_DEFAULT], "AT+CFUN=4", NULL,
+                                       cfun_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       if (ok)
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ste_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+       GAtChat *chat = data->chat[AT_DEFAULT];
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(chat, command, NULL, set_online_cb, cbd, g_free))
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ste_pre_sim(struct ofono_modem *modem)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+       data->sim = ofono_sim_create(modem, OFONO_VENDOR_MBM, "atmodem",
+                                       data->chat[AT_SIM]);
+       ofono_voicecall_create(modem, 0, "stemodem", data->chat[AT_VOICE]);
+}
+
+static void ste_post_sim(struct ofono_modem *modem)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_stk_create(modem, 0, "mbmmodem", data->chat[AT_SIM]);
+       ofono_phonebook_create(modem, 0, "atmodem", data->chat[AT_SIM]);
+       ofono_radio_settings_create(modem, 0, "stemodem", data->chat[AT_NET]);
+
+       ofono_sms_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+}
+
+static void ste_post_online(struct ofono_modem *modem)
+{
+       struct ste_data *data = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+       int i;
+
+       DBG("%p", modem);
+
+       ofono_ussd_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+       ofono_call_forwarding_create(modem, 0,
+                                       "atmodem", data->chat[AT_DEFAULT]);
+       ofono_call_settings_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+       ofono_netreg_create(modem, OFONO_VENDOR_MBM,
+                                       "atmodem", data->chat[AT_NET]);
+       ofono_call_meter_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+       ofono_call_barring_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+       ofono_call_volume_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+       ofono_cbs_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]);
+       ofono_gnss_create(modem, OFONO_VENDOR_STE, "atmodem",
+                               data->chat[AT_GNSS]);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_MBM,
+                                       "atmodem", data->chat[AT_GPRS]);
+
+       if (gprs) {
+               for (i = 0; i < MAX_PDP_CONTEXTS; i++) {
+                       gc = ofono_gprs_context_create(modem, 0, "stemodem",
+                                       data->chat[AT_GPRS]);
+                       if (gc == NULL)
+                               break;
+
+                       ofono_gprs_add_context(gprs, gc);
+               }
+       }
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver ste_driver = {
+       .name           = "ste",
+       .probe          = ste_probe,
+       .remove         = ste_remove,
+       .enable         = ste_enable,
+       .disable        = ste_disable,
+       .set_online     = ste_set_online,
+       .pre_sim        = ste_pre_sim,
+       .post_sim       = ste_post_sim,
+       .post_online    = ste_post_online,
+};
+
+static int ste_init(void)
+{
+       return ofono_modem_driver_register(&ste_driver);
+}
+
+static void ste_exit(void)
+{
+       ofono_modem_driver_unregister(&ste_driver);
+}
+
+OFONO_PLUGIN_DEFINE(ste, "ST-Ericsson modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, ste_init, ste_exit)
diff --git a/plugins/stemgr.c b/plugins/stemgr.c
new file mode 100644 (file)
index 0000000..126ec4e
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <net/if.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/dbus.h>
+
+/*
+ * ST-Ericsson's Modem Init Daemon is used for controlling the modem power
+ * cycles and provides a dbus API for modem state and properties.
+ */
+#define MGR_SERVICE            "com.stericsson.modeminit"
+#define MGR_INTERFACE          MGR_SERVICE ".Manager"
+#define MGR_GET_MODEMS         "GetModems"
+#define GET_MODEMS_TIMEOUT     5000
+
+#define MGR_MODEM_INTERFACE    MGR_SERVICE ".Modem"
+#define PROPERTY_CHANGED       "PropertyChanged"
+
+enum ste_state {
+       STE_STATE_OFF,
+       STE_STATE_READY,
+       STE_STATE_RESET
+};
+
+enum ste_operation {
+       STE_OP_STARTING,
+       STE_OP_READY,
+       STE_OP_RESTART,
+       STE_OP_OFF
+};
+
+struct ste_modem {
+       char *path;
+       struct ofono_modem *modem;
+       enum ste_state state;
+       char *serial;
+       char *interface;
+};
+
+static GHashTable *modem_list;
+static guint modem_daemon_watch;
+static guint property_changed_watch;
+static DBusConnection *connection;
+
+static void state_change(struct ste_modem *stemodem, enum ste_operation op)
+{
+       switch (stemodem->state) {
+       case STE_STATE_OFF:
+               /*
+                * The STE Modem is in state OFF and we're waiting for
+                * the Modem Init Daemon to signal that modem is ready
+                * in order to create and register the modem.
+                */
+               switch (op) {
+               case STE_OP_READY:
+                       stemodem->modem = ofono_modem_create(stemodem->serial,
+                                                               "ste");
+                       if (stemodem->modem == NULL) {
+                               ofono_error("Could not create modem %s, %s",
+                                               stemodem->path,
+                                               stemodem->serial);
+                               return;
+                       }
+
+                       DBG("register modem %s, %s", stemodem->path,
+                               stemodem->serial);
+
+                       if (stemodem->interface != NULL)
+                               ofono_modem_set_string(stemodem->modem,
+                                                       "Interface",
+                                                       stemodem->interface);
+
+                       ofono_modem_register(stemodem->modem);
+                       stemodem->state = STE_STATE_READY;
+                       break;
+               case STE_OP_STARTING:
+               case STE_OP_RESTART:
+               case STE_OP_OFF:
+                       break;
+               }
+               break;
+       case STE_STATE_READY:
+               /*
+                * The STE Modem is ready and the modem has been created
+                * and registered in oFono. In this state two things can
+                * happen: Modem restarts or is turned off. Turning off
+                * the modem is an exceptional situation e.g. high-temperature,
+                * low battery or upgrade. In this scenario we remove the
+                * STE modem from oFono.
+                */
+               switch (op) {
+               case STE_OP_READY:
+                       break;
+               case STE_OP_STARTING:
+               case STE_OP_RESTART:
+                       DBG("reset ongoing %s", stemodem->path);
+                       /* Note: Consider to power off modem here? */
+                       stemodem->state = STE_STATE_RESET;
+                       break;
+               case STE_OP_OFF:
+                       DBG("STE modem unregistering %s", stemodem->path);
+                       ofono_modem_remove(stemodem->modem);
+                       stemodem->modem = NULL;
+                       stemodem->state = STE_STATE_OFF;
+                       break;
+               }
+               break;
+       case STE_STATE_RESET:
+               /*
+                * The STE Modem is resetting.In this state two things can
+                * happen: Modem restarts succeeds, or modem is turned off.
+                */
+               switch (op) {
+               case STE_OP_STARTING:
+               case STE_OP_RESTART:
+                       break;
+               case STE_OP_READY:
+                       DBG("STE modem reset complete %s", stemodem->path);
+                       if (ofono_modem_get_powered(stemodem->modem))
+                               ofono_modem_reset(stemodem->modem);
+                       stemodem->state = STE_STATE_READY;
+                       break;
+               case STE_OP_OFF:
+                       DBG("STE modem unregistering %s", stemodem->path);
+                       ofono_modem_remove(stemodem->modem);
+                       stemodem->modem = NULL;
+                       stemodem->state = STE_STATE_OFF;
+                       break;
+               }
+               break;
+       }
+}
+
+static void update_property(struct ste_modem *stemodem, const char *prop,
+                               DBusMessageIter *iter, enum ste_operation *op,
+                               gboolean *op_valid)
+{
+       const char *value;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+               return;
+
+       dbus_message_iter_get_basic(iter, &value);
+
+       if (g_strcmp0(prop, "State") == 0) {
+               *op_valid = TRUE;
+               if (g_strcmp0(value, "booting") == 0)
+                       *op = STE_OP_STARTING;
+               else if (g_strcmp0(value, "upgrading") == 0)
+                       *op = STE_OP_OFF;
+               else if (g_strcmp0(value, "ready") == 0)
+                       *op = STE_OP_READY;
+               else if (g_strcmp0(value, "off") == 0)
+                       *op = STE_OP_OFF;
+               else if (g_strcmp0(value, "dumping") == 0)
+                       *op = STE_OP_RESTART;
+               else
+                       *op_valid = FALSE;
+       } else if (g_strcmp0(prop, "Interface") == 0) {
+               g_free(stemodem->interface);
+               stemodem->interface = g_strdup(value);
+       } else if (g_strcmp0(prop, "Serial") == 0) {
+               g_free(stemodem->serial);
+               stemodem->serial = g_strdup(value);
+       }
+}
+
+static void update_modem_properties(const char *path, DBusMessageIter *iter)
+{
+       enum ste_operation operation;
+       gboolean operation_valid;
+       struct ste_modem *stemodem = g_hash_table_lookup(modem_list, path);
+
+       if (stemodem == NULL) {
+               stemodem = g_try_new0(struct ste_modem, 1);
+               if (stemodem == NULL)
+                       return;
+
+               stemodem->path = g_strdup(path);
+               stemodem->state = STE_STATE_OFF;
+               g_hash_table_insert(modem_list, stemodem->path, stemodem);
+       }
+
+       while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(iter, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               update_property(stemodem, key, &value, &operation,
+                                       &operation_valid);
+
+               dbus_message_iter_next(iter);
+       }
+
+       if (operation_valid)
+               state_change(stemodem, operation);
+}
+
+static void get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessageIter iter, list;
+       DBusError err;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply)) {
+               ofono_error("%s: %s\n", err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (!dbus_message_has_signature(reply, "a(oa{sv})"))
+               goto done;
+
+       if (!dbus_message_iter_init(reply, &iter))
+               goto done;
+
+       dbus_message_iter_recurse(&iter, &list);
+
+       while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter entry, dict;
+               const char *path;
+
+               dbus_message_iter_recurse(&list, &entry);
+               dbus_message_iter_get_basic(&entry, &path);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &dict);
+
+               update_modem_properties(path, &dict);
+
+               dbus_message_iter_next(&list);
+       }
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void get_modems(void)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       message = dbus_message_new_method_call(MGR_SERVICE, "/",
+                                       MGR_INTERFACE, MGR_GET_MODEMS);
+       if (message == NULL) {
+               ofono_error("Unable to allocate new D-Bus message");
+               goto error;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       if (!dbus_connection_send_with_reply(connection, message, &call,
+                                               GET_MODEMS_TIMEOUT)) {
+               ofono_error("Sending D-Bus message failed");
+               goto error;
+       }
+
+       if (call == NULL) {
+               DBG("D-Bus connection not available");
+               goto error;
+       }
+
+       dbus_pending_call_set_notify(call, get_modems_reply, NULL, NULL);
+       dbus_pending_call_unref(call);
+
+error:
+       dbus_message_unref(message);
+}
+
+static gboolean property_changed(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter;
+       struct ste_modem *stemodem;
+       const char *key;
+       enum ste_operation operation;
+       gboolean operation_valid;
+
+       stemodem = g_hash_table_lookup(modem_list,
+                                       dbus_message_get_path(message));
+
+       if (stemodem == NULL)
+               return TRUE;
+
+
+       if (!dbus_message_iter_init(message, &iter))
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+       dbus_message_iter_next(&iter);
+
+       update_property(stemodem, key, &iter, &operation, &operation_valid);
+
+       if (operation_valid)
+               state_change(stemodem, operation);
+
+       return TRUE;
+}
+
+static void mgr_connect(DBusConnection *connection, void *user_data)
+{
+       property_changed_watch = g_dbus_add_signal_watch(connection, NULL,
+                                               NULL,
+                                               MGR_MODEM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               property_changed,
+                                               NULL, NULL);
+       get_modems();
+}
+
+static void mgr_disconnect(DBusConnection *connection, void *user_data)
+{
+       g_hash_table_remove_all(modem_list);
+       g_dbus_remove_watch(connection, property_changed_watch);
+       property_changed_watch = 0;
+}
+
+static void destroy_stemodem(gpointer data)
+{
+       struct ste_modem *stemodem = data;
+
+       ofono_modem_remove(stemodem->modem);
+
+       g_free(stemodem->interface);
+       g_free(stemodem->path);
+       g_free(stemodem->serial);
+       g_free(stemodem);
+}
+
+static int stemgr_init(void)
+{
+       connection = ofono_dbus_get_connection();
+
+       modem_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, destroy_stemodem);
+       modem_daemon_watch = g_dbus_add_service_watch(connection, MGR_SERVICE,
+                               mgr_connect, mgr_disconnect, NULL, NULL);
+       return 0;
+}
+
+static void stemgr_exit(void)
+{
+       g_hash_table_destroy(modem_list);
+       g_dbus_remove_watch(connection, modem_daemon_watch);
+
+       if (property_changed_watch > 0)
+               g_dbus_remove_watch(connection, property_changed_watch);
+
+}
+
+OFONO_PLUGIN_DEFINE(stemgr, "ST-Ericsson Modem Init Daemon detection", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, stemgr_init, stemgr_exit)
diff --git a/plugins/tc65.c b/plugins/tc65.c
new file mode 100644 (file)
index 0000000..eb64b89
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+
+#include <drivers/atmodem/atutil.h>
+
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+
+static int tc65_probe(struct ofono_modem *modem)
+{
+       return 0;
+}
+
+static void tc65_remove(struct ofono_modem *modem)
+{
+}
+
+static void tc65_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int tc65_enable(struct ofono_modem *modem)
+{
+       GAtChat *chat;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GHashTable *options;
+       const char *device;
+
+       DBG("%p", modem);
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               return -ENOMEM;
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL)
+               return -EINVAL;
+
+       g_hash_table_insert(options, "Baud", "115200");
+       g_hash_table_insert(options, "StopBits", "1");
+       g_hash_table_insert(options, "DataBits", "8");
+       g_hash_table_insert(options, "Parity", "none");
+       g_hash_table_insert(options, "XonXoff", "off");
+       g_hash_table_insert(options, "RtsCts", "on");
+       g_hash_table_insert(options, "Local", "on");
+       g_hash_table_insert(options, "Read", "on");
+
+       channel = g_at_tty_open(device, options);
+       g_hash_table_destroy(options);
+
+       if (channel == NULL)
+               return -EIO;
+
+       /*
+        * TC65 works almost as the 27.007 says. But for example after
+        * AT+CRSM the modem replies with the data in the queried EF and
+        * writes three pairs of <CR><LF> after the data and before OK.
+        */
+       syntax = g_at_syntax_new_gsm_permissive();
+
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return -ENOMEM;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, tc65_debug, "");
+
+       ofono_modem_set_data(modem, chat);
+
+       return 0;
+}
+
+static int tc65_disable(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_at_chat_send(chat, "AT+CFUN=7", NULL, NULL, NULL, NULL);
+
+       g_at_chat_unref(chat);
+
+       return 0;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void tc65_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                       ofono_modem_online_cb_t cb, void *user_data)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=7";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(chat, command, NULL, set_online_cb, cbd, g_free))
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void tc65_pre_sim(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", chat);
+       sim = ofono_sim_create(modem, 0, "atmodem", chat);
+       ofono_voicecall_create(modem, 0, "atmodem", chat);
+
+       if (sim)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void tc65_post_sim(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", chat);
+
+       ofono_sms_create(modem, 0, "atmodem", chat);
+}
+
+static void tc65_post_online(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_ussd_create(modem, 0, "atmodem", chat);
+       ofono_call_forwarding_create(modem, 0, "atmodem", chat);
+       ofono_call_settings_create(modem, 0, "atmodem", chat);
+       ofono_netreg_create(modem, 0, "atmodem", chat);
+       ofono_call_meter_create(modem, 0, "atmodem", chat);
+       ofono_call_barring_create(modem, 0, "atmodem", chat);
+
+       gprs = ofono_gprs_create(modem, 0, "atmodem", chat);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", chat);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver tc65_driver = {
+       .name           = "tc65",
+       .probe          = tc65_probe,
+       .remove         = tc65_remove,
+       .enable         = tc65_enable,
+       .disable        = tc65_disable,
+       .set_online     = tc65_set_online,
+       .pre_sim        = tc65_pre_sim,
+       .post_sim       = tc65_post_sim,
+       .post_online    = tc65_post_online,
+};
+
+static int tc65_init(void)
+{
+       return ofono_modem_driver_register(&tc65_driver);
+}
+
+static void tc65_exit(void)
+{
+       ofono_modem_driver_unregister(&tc65_driver);
+}
+
+OFONO_PLUGIN_DEFINE(tc65, "Cinterion TC65 driver plugin", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, tc65_init, tc65_exit)
diff --git a/plugins/telit.c b/plugins/telit.c
new file mode 100644 (file)
index 0000000..6ae7249
--- /dev/null
@@ -0,0 +1,690 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+#include "bluetooth.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *qss_prefix[] = { "#QSS:", NULL };
+static const char *rsen_prefix[]= { "#RSEN:", NULL };
+
+struct telit_data {
+       GAtChat *chat;
+       GAtChat *aux;
+       struct ofono_sim *sim;
+       guint sim_inserted_source;
+       struct ofono_modem *sap_modem;
+       GIOChannel *bt_io;
+       GIOChannel *hw_io;
+       guint bt_watch;
+       guint hw_watch;
+};
+
+static void telit_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static void sap_close_io(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       if (data->bt_io != NULL) {
+               int sk = g_io_channel_unix_get_fd(data->bt_io);
+               shutdown(sk, SHUT_RDWR);
+
+               g_io_channel_unref(data->bt_io);
+               data->bt_io = NULL;
+       }
+
+       if (data->bt_watch > 0)
+               g_source_remove(data->bt_watch);
+
+       g_io_channel_unref(data->hw_io);
+       data->hw_io = NULL;
+
+       if (data->hw_watch > 0)
+               g_source_remove(data->hw_watch);
+}
+
+static void bt_watch_remove(gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       data->bt_watch = 0;
+}
+
+static gboolean bt_event_cb(GIOChannel *bt_io, GIOCondition condition,
+                                                       gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       if (condition & G_IO_IN) {
+               GIOStatus status;
+               gsize bytes_read, bytes_written;
+               gchar buf[300];
+
+               status = g_io_channel_read_chars(bt_io, buf, 300,
+                                                       &bytes_read, NULL);
+
+               if (bytes_read > 0)
+                       g_io_channel_write_chars(data->hw_io, buf,
+                                       bytes_read, &bytes_written, NULL);
+
+               if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+                       return FALSE;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void hw_watch_remove(gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       data->hw_watch = 0;
+}
+
+static gboolean hw_event_cb(GIOChannel *hw_io, GIOCondition condition,
+                                                       gpointer userdata)
+{
+       struct ofono_modem *modem = userdata;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       if (condition & G_IO_IN) {
+               GIOStatus status;
+               gsize bytes_read, bytes_written;
+               gchar buf[300];
+
+               status = g_io_channel_read_chars(hw_io, buf, 300,
+                                                       &bytes_read, NULL);
+
+               if (bytes_read > 0)
+                       g_io_channel_write_chars(data->bt_io, buf,
+                                       bytes_read, &bytes_written, NULL);
+
+               if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+                       return FALSE;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GAtSyntax *syntax;
+       GIOChannel *channel;
+       GAtChat *chat;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       channel = g_at_tty_open(device, NULL);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsmv1();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, telit_debug, debug);
+
+       return chat;
+}
+
+static gboolean sim_inserted_timeout_cb(gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->sim_inserted_source = 0;
+
+       ofono_sim_inserted_notify(data->sim, TRUE);
+
+       return FALSE;
+}
+
+static void switch_sim_state_status(struct ofono_modem *modem, int status)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       switch (status) {
+       case 0:
+               DBG("SIM not inserted");
+               ofono_sim_inserted_notify(data->sim, FALSE);
+               break;
+       case 1:
+               DBG("SIM inserted");
+               /* We need to sleep a bit */
+               data->sim_inserted_source = g_timeout_add_seconds(1,
+                                                       sim_inserted_timeout_cb,
+                                                       modem);
+               break;
+       case 2:
+               DBG("SIM inserted and PIN unlocked");
+               break;
+       case 3:
+               DBG("SIM inserted and ready");
+               break;
+       }
+}
+
+static void telit_qss_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       int status;
+       GAtResultIter iter;
+
+       DBG("%p", modem);
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "#QSS:"))
+               return;
+
+       g_at_result_iter_next_number(&iter, &status);
+
+       switch_sim_state_status(modem, status);
+}
+
+static void telit_qss_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       int mode;
+       int status;
+       GAtResultIter iter;
+       g_at_result_iter_init(&iter, result);
+
+       DBG("%p", modem);
+
+       if (!g_at_result_iter_next(&iter, "#QSS:"))
+               return;
+
+       g_at_result_iter_next_number(&iter, &mode);
+       g_at_result_iter_next_number(&iter, &status);
+
+       switch_sim_state_status(modem, status);
+}
+
+static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct telit_data *data = ofono_modem_get_data(modem);
+       struct ofono_modem *m = data->sap_modem ? : modem;
+
+       DBG("%p", modem);
+
+       if (!ok) {
+               g_at_chat_unref(data->chat);
+               data->chat = NULL;
+               ofono_modem_set_powered(m, FALSE);
+               sap_close_io(modem);
+               return;
+       }
+
+       ofono_modem_set_powered(m, TRUE);
+
+       /* Enable sim state notification */
+       g_at_chat_send(data->chat, "AT#QSS=1", none_prefix, NULL, NULL, NULL);
+
+       /* Follow sim state */
+       g_at_chat_register(data->chat, "#QSS:", telit_qss_notify,
+                               FALSE, modem, NULL);
+
+       /* Query current sim state */
+       g_at_chat_send(data->chat, "AT#QSS?", qss_prefix,
+                               telit_qss_cb, modem, NULL);
+}
+
+static int telit_enable(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->chat = open_device(modem, "Modem", "Modem: ");
+       if (data->chat == NULL)
+               return -EINVAL;
+
+       /*
+        * Disable command echo and
+        * enable the Extended Error Result Codes
+        */
+       g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
+                               NULL, NULL, NULL);
+
+       /* Set phone functionality */
+       g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
+                               cfun_enable_cb, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void telit_rsen_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct telit_data *data = ofono_modem_get_data(modem);
+       int status;
+       GAtResultIter iter;
+
+       DBG("%p", modem);
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "#RSEN:"))
+               return;
+
+       g_at_result_iter_next_number(&iter, &status);
+
+       if (status == 0) {
+               ofono_modem_set_powered(data->sap_modem, FALSE);
+               sap_close_io(modem);
+               return;
+       }
+
+       telit_enable(modem);
+}
+
+static void rsen_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       if (!ok) {
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+               ofono_modem_set_powered(data->sap_modem, FALSE);
+               sap_close_io(modem);
+               return;
+       }
+}
+
+static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       if(data->sap_modem)
+               modem = data->sap_modem;
+
+       DBG("%p", modem);
+
+       g_at_chat_unref(data->chat);
+       data->chat = NULL;
+
+       if (data->sim_inserted_source > 0)
+               g_source_remove(data->sim_inserted_source);
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+
+       data->sap_modem = NULL;
+}
+
+static int telit_disable(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->chat);
+       g_at_chat_unregister_all(data->chat);
+
+       /* Power down modem */
+       g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix,
+                               cfun_disable_cb, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void rsen_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       sap_close_io(modem);
+
+       telit_disable(modem);
+}
+
+static int telit_sap_open(void)
+{
+       const char *device = "/dev/ttyUSB4";
+       struct termios ti;
+       int fd;
+
+       DBG("%s", device);
+
+       fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
+       if (fd < 0)
+               return -EINVAL;
+
+       /* Switch TTY to raw mode */
+       memset(&ti, 0, sizeof(ti));
+       cfmakeraw(&ti);
+
+       ti.c_cflag |= (B115200 | CLOCAL | CREAD);
+
+       tcflush(fd, TCIOFLUSH);
+       if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+               close(fd);
+               return -EBADF;
+       }
+
+       return fd;
+}
+
+static int telit_sap_enable(struct ofono_modem *modem,
+                                       struct ofono_modem *sap_modem,
+                                       int bt_fd)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+       int fd;
+
+       DBG("%p", modem);
+
+       fd = telit_sap_open();
+       if (fd < 0)
+               goto error;
+
+       data->hw_io = g_io_channel_unix_new(fd);
+       if (data->hw_io == NULL) {
+               close(fd);
+               goto error;
+       }
+
+       g_io_channel_set_encoding(data->hw_io, NULL, NULL);
+       g_io_channel_set_buffered(data->hw_io, FALSE);
+       g_io_channel_set_close_on_unref(data->hw_io, TRUE);
+
+       data->aux = open_device(modem, "Data", "Aux: ");
+       if (data->aux == NULL)
+               goto error;
+
+       data->bt_io = g_io_channel_unix_new(bt_fd);
+       if (data->bt_io == NULL)
+               goto error;
+
+       g_io_channel_set_encoding(data->bt_io, NULL, NULL);
+       g_io_channel_set_buffered(data->bt_io, FALSE);
+       g_io_channel_set_close_on_unref(data->bt_io, TRUE);
+
+       data->hw_watch = g_io_add_watch_full(data->hw_io, G_PRIORITY_DEFAULT,
+                               G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
+                               hw_event_cb, modem, hw_watch_remove);
+
+       data->bt_watch = g_io_add_watch_full(data->bt_io, G_PRIORITY_DEFAULT,
+                               G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
+                               bt_event_cb, modem, bt_watch_remove);
+
+       data->sap_modem = sap_modem;
+
+       g_at_chat_register(data->aux, "#RSEN:", telit_rsen_notify,
+                               FALSE, modem, NULL);
+
+       g_at_chat_send(data->aux, "AT#NOPT=0", NULL, NULL, NULL, NULL);
+
+       /* Set SAP functionality */
+       g_at_chat_send(data->aux, "AT#RSEN=1,1,0,2,0", rsen_prefix,
+                               rsen_enable_cb, modem, NULL);
+
+       return -EINPROGRESS;
+
+error:
+       shutdown(bt_fd, SHUT_RDWR);
+       close(bt_fd);
+
+       sap_close_io(modem);
+       return -EINVAL;
+}
+
+static int telit_sap_disable(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       g_at_chat_send(data->aux, "AT#RSEN=0", rsen_prefix,
+                               rsen_disable_cb, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void telit_pre_sim(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       if (data->sap_modem)
+               modem = data->sap_modem;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+       data->sim = ofono_sim_create(modem, 0, "atmodem", data->chat);
+       ofono_voicecall_create(modem, 0, "atmodem", data->chat);
+}
+
+static void telit_post_sim(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       if (data->sap_modem)
+               modem = data->sap_modem;
+
+       DBG("%p", modem);
+
+       ofono_sms_create(modem, 0, "atmodem", data->chat);
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void telit_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       g_at_chat_send(data->chat, command, none_prefix, set_online_cb,
+                                               cbd, g_free);
+}
+
+static void telit_post_online(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       if(data->sap_modem)
+               modem = data->sap_modem;
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
+       ofono_ussd_create(modem, 0, "atmodem", data->chat);
+       ofono_call_forwarding_create(modem, 0, "atmodem", data->chat);
+       ofono_call_settings_create(modem, 0, "atmodem", data->chat);
+       ofono_call_meter_create(modem, 0, "atmodem", data->chat);
+       ofono_call_barring_create(modem, 0, "atmodem", data->chat);
+
+       gprs = ofono_gprs_create(modem, 0, "atmodem", data->chat);
+       gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct bluetooth_sap_driver sap_driver = {
+       .name = "telit",
+       .enable = telit_sap_enable,
+       .pre_sim = telit_pre_sim,
+       .post_sim = telit_post_sim,
+       .set_online = telit_set_online,
+       .post_online = telit_post_online,
+       .disable = telit_sap_disable,
+};
+
+static int telit_probe(struct ofono_modem *modem)
+{
+       struct telit_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct telit_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       bluetooth_sap_client_register(&sap_driver, modem);
+
+       return 0;
+}
+
+static void telit_remove(struct ofono_modem *modem)
+{
+       struct telit_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       bluetooth_sap_client_unregister(modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       if (data->sim_inserted_source > 0)
+               g_source_remove(data->sim_inserted_source);
+
+       g_free(data);
+}
+
+static struct ofono_modem_driver telit_driver = {
+       .name           = "telit",
+       .probe          = telit_probe,
+       .remove         = telit_remove,
+       .enable         = telit_enable,
+       .disable        = telit_disable,
+       .set_online     = telit_set_online,
+       .pre_sim        = telit_pre_sim,
+       .post_sim       = telit_post_sim,
+       .post_online    = telit_post_online,
+};
+
+static int telit_init(void)
+{
+       return ofono_modem_driver_register(&telit_driver);
+}
+
+static void telit_exit(void)
+{
+       ofono_modem_driver_unregister(&telit_driver);
+}
+
+OFONO_PLUGIN_DEFINE(telit, "telit driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, telit_init, telit_exit)
diff --git a/plugins/u8500.c b/plugins/u8500.c
new file mode 100644 (file)
index 0000000..73ff4fa
--- /dev/null
@@ -0,0 +1,706 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include <gisi/netlink.h>
+#include <gisi/modem.h>
+#include <gisi/client.h>
+#include <gisi/message.h>
+#include <gisi/iter.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/phonebook.h>
+#include <ofono/netreg.h>
+#include <ofono/voicecall.h>
+#include <ofono/sms.h>
+#include <ofono/cbs.h>
+#include <ofono/sim.h>
+#include <ofono/ussd.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-meter.h>
+#include <ofono/radio-settings.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+
+#include "drivers/isimodem/isimodem.h"
+#include "drivers/isimodem/isiutil.h"
+#include "drivers/isimodem/mtc.h"
+#include "drivers/isimodem/debug.h"
+
+struct isi_data {
+       char const *ifname;
+       GIsiModem *modem;
+       GIsiClient *client;
+       GIsiPhonetNetlink *link;
+       enum GIsiPhonetLinkState linkstate;
+       unsigned interval;
+       int reported;
+       ofono_bool_t online;
+       struct isi_cb_data *online_cbd;
+};
+
+struct devinfo_data {
+       GIsiClient *client;
+};
+
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
+{
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       mce_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static void report_powered(struct ofono_modem *modem, struct isi_data *isi,
+                               ofono_bool_t powered)
+{
+       if (powered == isi->reported)
+               return;
+
+       isi->reported = powered;
+       ofono_modem_set_powered(modem, powered);
+}
+
+static void report_online(struct isi_data *isi, ofono_bool_t online)
+{
+       struct isi_cb_data *cbd = isi->online_cbd;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       isi->online_cbd = NULL;
+
+       if (isi->online == online)
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+       else
+               CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void set_power_by_mce_state(struct ofono_modem *modem,
+                                       struct isi_data *isi, int mce_state)
+{
+       switch (mce_state) {
+       case MCE_POWER_OFF:
+               report_powered(modem, isi, FALSE);
+               break;
+       case MCE_NORMAL:
+               if (isi->online_cbd)
+                       report_online(isi, mce_state == MCE_NORMAL);
+       default:
+               report_powered(modem, isi, TRUE);
+       }
+}
+
+static void mce_state_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t state;
+       uint8_t action;
+
+       if (isi == NULL || g_isi_msg_id(msg) != MCE_MODEM_STATE_IND)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &state) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &action))
+               return;
+
+       switch (action) {
+       case MCE_START:
+               DBG("target modem state: %s (0x%02X)",
+                       mce_modem_state_name(state), state);
+               break;
+
+       case MCE_READY:
+               DBG("current modem state: %s (0x%02X)",
+                       mce_modem_state_name(state), state);
+               set_power_by_mce_state(modem, isi, state);
+               break;
+       default:
+               break;
+       }
+}
+
+static void mce_rf_state_ind_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t state;
+       uint8_t action;
+
+       if (isi == NULL || g_isi_msg_id(msg) != MCE_RF_STATE_IND)
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &state) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &action))
+               return;
+
+       switch (action) {
+       case MCE_READY:
+               DBG("current rf state: %s (0x%02X)",
+                       mce_rf_state_name(state), state);
+               if (isi->online_cbd)
+                       report_online(isi, state);
+               break;
+       case MCE_START:
+       default:
+               break;
+       }
+}
+
+static void mce_query_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t current;
+       uint8_t target;
+
+       if (!check_response_status(msg, MCE_MODEM_STATE_QUERY_RESP))
+               return;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &current) ||
+                       !g_isi_msg_data_get_byte(msg, 1, &target))
+               return;
+
+       DBG("Modem state: current=%s (0x%02X) target=%s (0x%02X)",
+               mce_modem_state_name(current), current,
+               mce_modem_state_name(target), target);
+
+       if (current == target)
+               set_power_by_mce_state(modem, isi, current);
+}
+
+static gboolean bootstrap_current_state(gpointer user)
+{
+       struct ofono_modem *om = user;
+       struct isi_data *isi = ofono_modem_get_data(om);
+
+       const uint8_t req[] = {
+               MCE_MODEM_STATE_QUERY_REQ,
+               0x00, 0x00 /* Filler */
+       };
+       size_t len = sizeof(req);
+
+       g_isi_client_send(isi->client, req, len, mce_query_cb, om, NULL);
+
+       return FALSE;
+}
+
+static void reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_modem *om = data;
+       struct isi_data *isi = ofono_modem_get_data(om);
+
+       if (!g_isi_msg_error(msg) < 0)
+               return;
+
+       ISI_RESOURCE_DBG(msg);
+
+       g_isi_client_ind_subscribe(isi->client, MCE_MODEM_STATE_IND,
+                                       mce_state_ind_cb, om);
+
+       g_isi_client_ind_subscribe(isi->client, MCE_RF_STATE_IND,
+                                       mce_rf_state_ind_cb, om);
+
+       /*
+        * FIXME: There is a theoretical race condition here:
+        * g_isi_client_ind_subscribe() adds the actual message
+        * sending for committing changes to subscriptions in idle
+        * loop, which may or may not preserve ordering.  Thus, we
+        * might miss a state indication if the bootstrap request ends
+        * up being sent first.
+        */
+       g_idle_add(bootstrap_current_state, om);
+}
+
+static void phonet_status_cb(GIsiModem *modem, enum GIsiPhonetLinkState st,
+                               char const *ifname, void *data)
+{
+       struct ofono_modem *om = data;
+       struct isi_data *isi = ofono_modem_get_data(om);
+
+       DBG("Link %s (%u) is %s", isi->ifname, g_isi_modem_index(isi->modem),
+               st == PN_LINK_REMOVED ? "removed" :
+               st == PN_LINK_DOWN ? "down" : "up");
+
+       isi->linkstate = st;
+
+       if (st == PN_LINK_UP)
+               g_isi_client_verify(isi->client, reachable_cb, om, NULL);
+       else if (st == PN_LINK_DOWN)
+               set_power_by_mce_state(om, isi, MCE_POWER_OFF);
+}
+
+static int u8500_probe(struct ofono_modem *modem)
+{
+       const char *ifname = ofono_modem_get_string(modem, "Interface");
+       unsigned address = ofono_modem_get_integer(modem, "Address");
+       GIsiModem *isimodem;
+       GIsiClient *client = NULL;
+       GIsiPhonetNetlink *link = NULL;
+       struct isi_data *isi = NULL;
+
+       if (ifname == NULL)
+               return -EINVAL;
+
+       DBG("(%p) with %s", modem, ifname);
+
+       isimodem = g_isi_modem_create_by_name(ifname);
+       if (isimodem == NULL) {
+               DBG("Interface=%s: %s", ifname, strerror(errno));
+               return -errno;
+       }
+
+       g_isi_modem_set_userdata(isimodem, modem);
+
+       if (getenv("OFONO_ISI_DEBUG"))
+               g_isi_modem_set_debug(isimodem, ofono_debug);
+
+       if (getenv("OFONO_ISI_TRACE"))
+               g_isi_modem_set_trace(isimodem, isi_trace);
+
+       if (g_isi_pn_netlink_by_modem(isimodem)) {
+               DBG("%s: %s", ifname, strerror(EBUSY));
+               errno = EBUSY;
+               goto error;
+       }
+
+       link = g_isi_pn_netlink_start(isimodem, phonet_status_cb, modem);
+       if (link == NULL) {
+               DBG("%s: %s", ifname, strerror(errno));
+               goto error;
+       }
+
+       if (address) {
+               int error = g_isi_pn_netlink_set_address(isimodem, address);
+               if (error && error != -EEXIST) {
+                       DBG("g_isi_pn_netlink_set_address(): %s\n",
+                               strerror(-error));
+                       errno = -error;
+                       goto error;
+               }
+       }
+
+       isi = g_try_new0(struct isi_data, 1);
+       if (isi == NULL) {
+               errno = ENOMEM;
+               goto error;
+       }
+
+       client = g_isi_client_create(isimodem, PN_MODEM_MCE);
+       if (!client)
+               goto error;
+
+       g_isi_modem_set_device(isimodem, PN_DEV_MODEM);
+
+       isi->modem = isimodem;
+       isi->ifname = ifname;
+       isi->link = link;
+       isi->reported = -1;
+       isi->client = client;
+
+       ofono_modem_set_data(modem, isi);
+       return 0;
+
+error:
+       g_isi_pn_netlink_stop(link);
+       g_isi_client_destroy(client);
+       g_isi_modem_destroy(isimodem);
+       g_free(isi);
+
+       return -errno;
+}
+
+static void u8500_remove(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       if (isi == NULL)
+               return;
+
+       g_isi_pn_netlink_stop(isi->link);
+       g_isi_client_destroy(isi->client);
+       g_isi_modem_destroy(isi->modem);
+       g_free(isi);
+}
+
+static void mce_state_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       struct ofono_modem *modem = cbd->user;
+       ofono_modem_online_cb_t cb = cbd->cb;
+
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       uint8_t cause;
+
+       if (!check_response_status(msg, MCE_RF_STATE_RESP))
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &cause))
+               goto error;
+
+       DBG("MCE cause: %s (0x%02X)", mce_status_info(cause), cause);
+
+       if (cause == MCE_OK) {
+               isi->online_cbd = cbd;
+               return;
+       }
+
+       if (cause == MCE_ALREADY_ACTIVE) {
+               CALLBACK_WITH_SUCCESS(cb, cbd->data);
+               g_free(cbd);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+       g_free(cbd);
+}
+
+static void u8500_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *data)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+       struct isi_cb_data *cbd = isi_cb_data_new(modem, cb, data);
+       const uint8_t req[] = {
+               MCE_RF_STATE_REQ,
+               online ? MCE_RF_ON : MCE_RF_OFF,
+               0x00
+       };
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       if (cbd == NULL || isi == NULL)
+               goto error;
+
+       if (g_isi_client_send_with_timeout(isi->client, req, sizeof(req),
+                               MTC_STATE_REQ_TIMEOUT,
+                               mce_state_cb, cbd, NULL)) {
+               isi->online = online;
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, data);
+       g_free(cbd);
+}
+
+static void u8500_pre_sim(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_sim_create(modem, 0, "wgmodem2.5", isi->modem);
+       ofono_devinfo_create(modem, 0, "u8500", isi->modem);
+       ofono_voicecall_create(modem, 0, "isimodem", isi->modem);
+}
+
+static void u8500_post_sim(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_phonebook_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_forwarding_create(modem, 0, "isimodem", isi->modem);
+       ofono_radio_settings_create(modem, 0, "isimodem", isi->modem);
+}
+
+static void u8500_post_online(struct ofono_modem *modem)
+{
+       struct isi_data *isi = ofono_modem_get_data(modem);
+
+       DBG("(%p) with %s", modem, isi->ifname);
+
+       ofono_netreg_create(modem, 0, "isimodem", isi->modem);
+       ofono_sms_create(modem, 0, "isimodem", isi->modem);
+       ofono_cbs_create(modem, 0, "isimodem", isi->modem);
+       ofono_ussd_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_settings_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_barring_create(modem, 0, "isimodem", isi->modem);
+       ofono_call_meter_create(modem, 0, "isimodem", isi->modem);
+       ofono_gprs_create(modem, 0, "isimodem", isi->modem);
+}
+
+static int u8500_enable(struct ofono_modem *modem)
+{
+       return 0;
+}
+
+static int u8500_disable(struct ofono_modem *modem)
+{
+       return 0;
+}
+
+static void u8500_info_resp_cb(const GIsiMessage *msg, void *data)
+{
+       struct isi_cb_data *cbd = data;
+       ofono_devinfo_query_cb_t cb = cbd->cb;
+       GIsiSubBlockIter iter;
+       uint8_t msgid;
+       uint8_t status;
+
+       msgid = g_isi_msg_id(msg);
+       if (msgid != INFO_SERIAL_NUMBER_READ_RESP)
+               goto error;
+
+       if (g_isi_msg_error(msg) < 0)
+               goto error;
+
+       if (!g_isi_msg_data_get_byte(msg, 0, &status))
+               goto error;
+
+       if (status != INFO_OK)
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               uint8_t id = g_isi_sb_iter_get_id(&iter);
+               uint8_t chars;
+               char *info = NULL;
+
+               if (id != INFO_SB_PRODUCT_INFO_MANUFACTURER &&
+                               id != INFO_SB_PRODUCT_INFO_NAME &&
+                               id != INFO_SB_MCUSW_VERSION &&
+                               id != INFO_SB_SN_IMEI_PLAIN &&
+                               id != INFO_SB_MODEMSW_VERSION)
+                       continue;
+
+               if (g_isi_sb_iter_get_len(&iter) < 5)
+                       goto error;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &chars, 3))
+                       goto error;
+
+               if (!g_isi_sb_iter_get_latin_tag(&iter, &info, chars, 4))
+                       goto error;
+
+               CALLBACK_WITH_SUCCESS(cb, info, cbd->data);
+
+               g_free(info);
+               return;
+       }
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", cbd->data);
+}
+
+static void u8500_devinfo_reachable_cb(const GIsiMessage *msg, void *data)
+{
+       struct ofono_devinfo *info = data;
+
+       if (g_isi_msg_error(msg) < 0)
+               return;
+
+       ISI_RESOURCE_DBG(msg);
+
+       ofono_devinfo_register(info);
+}
+
+static void u8500_query_manufacturer(struct ofono_devinfo *info,
+                                       ofono_devinfo_query_cb_t cb,
+                                       void *data)
+{
+       CALLBACK_WITH_FAILURE(cb, "", data);
+}
+
+static void u8500_query_model(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb,
+                               void *data)
+{
+       CALLBACK_WITH_FAILURE(cb, "", data);
+}
+
+static void u8500_query_revision(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb,
+                               void *data)
+{
+       struct devinfo_data *dev = ofono_devinfo_get_data(info);
+       struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data);
+       const unsigned char msg[] = {
+               INFO_SERIAL_NUMBER_READ_REQ,
+               0x00, 0x00,
+               0x00, 0x00, 0x00, 0x01, /* M_INFO_MODEMSW */
+               0x00, 0x00
+       };
+       DBG("");
+
+       if (cbd == NULL || dev == NULL)
+               goto error;
+
+       if (g_isi_client_send(dev->client, msg, sizeof(msg),
+                               u8500_info_resp_cb, cbd, g_free))
+               return;
+
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", data);
+       g_free(cbd);
+}
+
+static void u8500_query_serial(struct ofono_devinfo *info,
+                               ofono_devinfo_query_cb_t cb,
+                               void *data)
+{
+       char imei[16]; /* IMEI 15 digits + 1 null*/
+       char numbers[] = "1234567890";
+       FILE *fp = fopen("/etc/imei", "r");
+       DBG("");
+
+       if (fp == NULL) {
+               DBG("failed to open /etc/imei file");
+               goto error;
+       }
+
+       if (fgets(imei, 16, fp)) {
+               DBG(" IMEI = %s", imei);
+               if (15 == strspn(imei, numbers))
+                       CALLBACK_WITH_SUCCESS(cb, imei, data);
+               else {
+                       CALLBACK_WITH_FAILURE(cb, "", data);
+                       fclose(fp);
+                       goto error;
+               }
+       }
+
+       fclose(fp);
+       return;
+
+error:
+       CALLBACK_WITH_FAILURE(cb, "", data);
+}
+
+static int u8500_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
+                               void *user)
+{
+       GIsiModem *idx = user;
+       struct devinfo_data *data = g_try_new0(struct devinfo_data, 1);
+
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->client = g_isi_client_create(idx, PN_MODEM_INFO);
+       if (data->client == NULL)
+               goto nomem;
+
+       ofono_devinfo_set_data(info, data);
+
+       g_isi_client_set_timeout(data->client, INFO_TIMEOUT);
+       g_isi_client_verify(data->client, u8500_devinfo_reachable_cb,
+                               info, NULL);
+
+       return 0;
+
+nomem:
+       g_isi_client_destroy(data->client);
+
+       g_free(data);
+       return -ENOMEM;
+
+}
+
+static void u8500_devinfo_remove(struct ofono_devinfo *info)
+{
+       struct devinfo_data *data = ofono_devinfo_get_data(info);
+
+       ofono_devinfo_set_data(info, NULL);
+
+       if (data == NULL)
+               return;
+
+       g_isi_client_destroy(data->client);
+       g_free(data);
+}
+
+static struct ofono_modem_driver driver = {
+       .name = "u8500",
+       .probe = u8500_probe,
+       .remove = u8500_remove,
+       .set_online = u8500_online,
+       .pre_sim = u8500_pre_sim,
+       .post_sim = u8500_post_sim,
+       .post_online = u8500_post_online,
+       .enable = u8500_enable,
+       .disable = u8500_disable,
+};
+
+static struct ofono_devinfo_driver devinfo_driver = {
+       .name                   = "u8500",
+       .probe                  = u8500_devinfo_probe,
+       .remove                 = u8500_devinfo_remove,
+       .query_manufacturer     = u8500_query_manufacturer,
+       .query_model            = u8500_query_model,
+       .query_revision         = u8500_query_revision,
+       .query_serial           = u8500_query_serial
+};
+
+static int u8500_init(void)
+{
+       int err;
+
+       err = ofono_modem_driver_register(&driver);
+
+       if (err < 0)
+               return err;
+
+       ofono_devinfo_driver_register(&devinfo_driver);
+
+       return 0;
+}
+
+static void u8500_exit(void)
+{
+       ofono_devinfo_driver_unregister(&devinfo_driver);
+
+       ofono_modem_driver_unregister(&driver);
+}
+
+OFONO_PLUGIN_DEFINE(u8500, "ST-Ericsson U8500 modem driver",
+                       VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       u8500_init, u8500_exit)
diff --git a/plugins/udev.c b/plugins/udev.c
new file mode 100644 (file)
index 0000000..8cb87a5
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <libudev.h>
+
+#include <glib.h>
+#include <string.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/log.h>
+
+static GSList *modem_list = NULL;
+static GHashTable *devpath_list = NULL;
+
+static struct ofono_modem *find_modem(const char *devpath)
+{
+       GSList *list;
+
+       for (list = modem_list; list; list = list->next) {
+               struct ofono_modem *modem = list->data;
+               const char *path = ofono_modem_get_string(modem, "Path");
+
+               if (g_strcmp0(devpath, path) == 0)
+                       return modem;
+       }
+
+       return NULL;
+}
+
+static const char *get_property(struct udev_device *device,
+                               char const *property_name)
+{
+       struct udev_list_entry *entry;
+
+       entry = udev_device_get_properties_list_entry(device);
+       while (entry) {
+               const char *name = udev_list_entry_get_name(entry);
+
+               if (g_strcmp0(name, property_name) == 0)
+                       return udev_list_entry_get_value(entry);
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       return NULL;
+}
+
+static const char *get_driver(struct udev_device *udev_device)
+{
+       return get_property(udev_device, "OFONO_DRIVER");
+}
+
+static const char *get_serial(struct udev_device *udev_device)
+{
+       const char *serial;
+
+       serial = get_property(udev_device, "ID_SERIAL_SHORT");
+
+       if (serial != NULL) {
+               unsigned int i, len = strlen(serial);
+
+               for (i = 0; i < len; i++) {
+                       if (!g_ascii_isalnum(serial[i]))
+                               return NULL;
+               }
+       }
+
+       return serial;
+}
+
+static void add_ifx(struct ofono_modem *modem,
+                                       struct udev_device *udev_device)
+{
+       struct udev_list_entry *entry;
+       const char *devnode;
+
+       DBG("modem %p", modem);
+
+       devnode = udev_device_get_devnode(udev_device);
+       ofono_modem_set_string(modem, "Device", devnode);
+
+       entry = udev_device_get_properties_list_entry(udev_device);
+       while (entry) {
+               const char *name = udev_list_entry_get_name(entry);
+               const char *value = udev_list_entry_get_value(entry);
+
+               if (g_str_equal(name, "OFONO_IFX_LDISC") == TRUE)
+                       ofono_modem_set_string(modem, "LineDiscipline", value);
+               else if (g_str_equal(name, "OFONO_IFX_AUDIO") == TRUE)
+                       ofono_modem_set_string(modem, "AudioSetting", value);
+               else if (g_str_equal(name, "OFONO_IFX_LOOPBACK") == TRUE)
+                       ofono_modem_set_string(modem, "AudioLoopback", value);
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       ofono_modem_register(modem);
+}
+
+static void add_isi(struct ofono_modem *modem,
+                                       struct udev_device *udev_device)
+{
+       const char *ifname, *type, *addr;
+
+       DBG("modem %p", modem);
+
+       if (ofono_modem_get_string(modem, "Interface"))
+               return;
+
+       addr = get_property(udev_device, "OFONO_ISI_ADDRESS");
+       if (addr != NULL)
+               ofono_modem_set_integer(modem, "Address", atoi(addr));
+
+       if (g_strcmp0(udev_device_get_subsystem(udev_device), "net") != 0)
+               return;
+
+       type = udev_device_get_sysattr_value(udev_device, "type");
+       if (g_strcmp0(type, "820") != 0)
+               return;
+
+       ifname = udev_device_get_sysname(udev_device);
+       ofono_modem_set_string(modem, "Interface", ifname);
+
+       DBG("interface %s", ifname);
+
+       ofono_modem_register(modem);
+}
+
+static void add_calypso(struct ofono_modem *modem,
+                                       struct udev_device *udev_device)
+{
+       const char *devnode;
+
+       DBG("modem %p", modem);
+
+       devnode = udev_device_get_devnode(udev_device);
+       ofono_modem_set_string(modem, "Device", devnode);
+
+       ofono_modem_register(modem);
+}
+
+static void add_tc65(struct ofono_modem *modem,
+                       struct udev_device *udev_device)
+{
+       const char *devnode;
+
+       DBG("modem %p", modem);
+
+       devnode = udev_device_get_devnode(udev_device);
+       ofono_modem_set_string(modem, "Device", devnode);
+
+       ofono_modem_register(modem);
+}
+
+static void add_nokiacdma(struct ofono_modem *modem,
+                                       struct udev_device *udev_device)
+{
+       const char *devnode;
+
+       DBG("modem %p", modem);
+
+       devnode = udev_device_get_devnode(udev_device);
+       ofono_modem_set_string(modem, "Device", devnode);
+
+       ofono_modem_register(modem);
+}
+
+static void add_sim900(struct ofono_modem *modem,
+                                       struct udev_device *udev_device)
+{
+       const char *devnode;
+
+       DBG("modem %p", modem);
+
+       devnode = udev_device_get_devnode(udev_device);
+       ofono_modem_set_string(modem, "Device", devnode);
+
+       ofono_modem_register(modem);
+}
+
+static void add_modem(struct udev_device *udev_device)
+{
+       struct ofono_modem *modem;
+       struct udev_device *parent;
+       const char *devpath, *curpath, *driver;
+
+       driver = get_driver(udev_device);
+       if (driver != NULL) {
+               devpath = udev_device_get_devpath(udev_device);
+               if (devpath == NULL)
+                       return;
+
+               modem = ofono_modem_create(NULL, driver);
+               if (modem == NULL)
+                       return;
+
+               ofono_modem_set_string(modem, "Path", devpath);
+
+               modem_list = g_slist_prepend(modem_list, modem);
+
+               goto done;
+       }
+
+       parent = udev_device_get_parent(udev_device);
+       if (parent == NULL)
+               return;
+
+       driver = get_driver(parent);
+       if (driver == NULL) {
+               parent = udev_device_get_parent(parent);
+               driver = get_driver(parent);
+               if (driver == NULL) {
+                       parent = udev_device_get_parent(parent);
+                       driver = get_driver(parent);
+                       if (driver == NULL)
+                               return;
+               }
+       }
+
+       devpath = udev_device_get_devpath(parent);
+       if (devpath == NULL)
+               return;
+
+       modem = find_modem(devpath);
+       if (modem == NULL) {
+               const char *serial = get_serial(parent);
+
+               modem = ofono_modem_create(serial, driver);
+               if (modem == NULL)
+                       return;
+
+               ofono_modem_set_string(modem, "Path", devpath);
+               ofono_modem_set_integer(modem, "Registered", 0);
+
+               modem_list = g_slist_prepend(modem_list, modem);
+       }
+
+done:
+       curpath = udev_device_get_devpath(udev_device);
+       if (curpath == NULL)
+               return;
+
+       DBG("%s (%s)", curpath, driver);
+
+       g_hash_table_insert(devpath_list, g_strdup(curpath), g_strdup(devpath));
+
+       if (g_strcmp0(driver, "ifx") == 0)
+               add_ifx(modem, udev_device);
+       else if (g_strcmp0(driver, "u8500") == 0)
+               add_isi(modem, udev_device);
+       else if (g_strcmp0(driver, "n900") == 0)
+               add_isi(modem, udev_device);
+       else if (g_strcmp0(driver, "calypso") == 0)
+               add_calypso(modem, udev_device);
+       else if (g_strcmp0(driver, "tc65") == 0)
+               add_tc65(modem, udev_device);
+       else if (g_strcmp0(driver, "nokiacdma") == 0)
+               add_nokiacdma(modem, udev_device);
+       else if (g_strcmp0(driver, "sim900") == 0)
+               add_sim900(modem, udev_device);
+}
+
+static gboolean devpath_remove(gpointer key, gpointer value, gpointer user_data)
+{
+       const char *path = value;
+       const char *devpath = user_data;
+
+       DBG("%s -> %s", path, devpath);
+
+       return g_str_equal(path, devpath);
+}
+
+static void remove_modem(struct udev_device *udev_device)
+{
+       struct ofono_modem *modem;
+       const char *curpath = udev_device_get_devpath(udev_device);
+       char *devpath, *remove;
+
+       if (curpath == NULL)
+               return;
+
+       DBG("%s", curpath);
+
+       devpath = g_hash_table_lookup(devpath_list, curpath);
+       if (devpath == NULL)
+               return;
+
+       modem = find_modem(devpath);
+       if (modem == NULL)
+               return;
+
+       modem_list = g_slist_remove(modem_list, modem);
+
+       ofono_modem_remove(modem);
+
+       DBG("%s", devpath);
+
+       remove = g_strdup(devpath);
+
+       g_hash_table_foreach_remove(devpath_list, devpath_remove, remove);
+
+       g_free(remove);
+}
+
+static void enumerate_devices(struct udev *context)
+{
+       struct udev_enumerate *enumerate;
+       struct udev_list_entry *entry;
+
+       enumerate = udev_enumerate_new(context);
+       if (enumerate == NULL)
+               return;
+
+       udev_enumerate_add_match_subsystem(enumerate, "tty");
+       udev_enumerate_add_match_subsystem(enumerate, "net");
+       udev_enumerate_add_match_subsystem(enumerate, "hsi");
+
+       udev_enumerate_scan_devices(enumerate);
+
+       entry = udev_enumerate_get_list_entry(enumerate);
+       while (entry) {
+               const char *syspath = udev_list_entry_get_name(entry);
+               struct udev_device *device;
+
+               device = udev_device_new_from_syspath(context, syspath);
+               if (device != NULL) {
+                       const char *subsystem;
+
+                       subsystem = udev_device_get_subsystem(device);
+
+                       if (g_strcmp0(subsystem, "tty") == 0 ||
+                                       g_strcmp0(subsystem, "net") == 0 ||
+                                       g_strcmp0(subsystem, "hsi") == 0)
+                               add_modem(device);
+
+                       udev_device_unref(device);
+               }
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       udev_enumerate_unref(enumerate);
+}
+
+static struct udev *udev_ctx;
+static struct udev_monitor *udev_mon;
+static guint udev_watch = 0;
+
+static gboolean udev_event(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct udev_device *device;
+       const char *subsystem, *action;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+               ofono_warn("Error with udev monitor channel");
+               udev_watch = 0;
+               return FALSE;
+       }
+
+       device = udev_monitor_receive_device(udev_mon);
+       if (device == NULL)
+               return TRUE;
+
+       subsystem = udev_device_get_subsystem(device);
+       if (subsystem == NULL)
+               goto done;
+
+       action = udev_device_get_action(device);
+       if (action == NULL)
+               goto done;
+
+       DBG("subsystem %s %s", subsystem, action);
+
+       if (g_str_equal(action, "add") == TRUE) {
+               if (g_strcmp0(subsystem, "tty") == 0 ||
+                               g_strcmp0(subsystem, "net") == 0 ||
+                                       g_strcmp0(subsystem, "hsi") == 0)
+                       add_modem(device);
+       } else if (g_str_equal(action, "remove") == TRUE) {
+               if (g_strcmp0(subsystem, "tty") == 0 ||
+                               g_strcmp0(subsystem, "net") == 0 ||
+                                       g_strcmp0(subsystem, "hsi") == 0)
+                       remove_modem(device);
+       }
+
+       DBG("subsystem %s finished", subsystem);
+
+done:
+       udev_device_unref(device);
+
+       return TRUE;
+}
+
+static void udev_start(void)
+{
+       GIOChannel *channel;
+       int fd;
+
+       if (udev_monitor_enable_receiving(udev_mon) < 0) {
+               ofono_error("Failed to enable udev monitor");
+               return;
+       }
+
+       enumerate_devices(udev_ctx);
+
+       fd = udev_monitor_get_fd(udev_mon);
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL)
+               return;
+
+       udev_watch = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                                       udev_event, NULL);
+
+       g_io_channel_unref(channel);
+}
+
+static int udev_init(void)
+{
+       devpath_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, g_free);
+       if (devpath_list == NULL) {
+               ofono_error("Failed to create udev path list");
+               return -ENOMEM;
+       }
+
+       udev_ctx = udev_new();
+       if (udev_ctx == NULL) {
+               ofono_error("Failed to create udev context");
+               g_hash_table_destroy(devpath_list);
+               return -EIO;
+       }
+
+       udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
+       if (udev_mon == NULL) {
+               ofono_error("Failed to create udev monitor");
+               g_hash_table_destroy(devpath_list);
+               udev_unref(udev_ctx);
+               udev_ctx = NULL;
+               return -EIO;
+       }
+
+       udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL);
+       udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL);
+       udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "hsi", NULL);
+
+       udev_monitor_filter_update(udev_mon);
+
+       udev_start();
+
+       return 0;
+}
+
+static void udev_exit(void)
+{
+       GSList *list;
+
+       if (udev_watch > 0)
+               g_source_remove(udev_watch);
+
+       for (list = modem_list; list; list = list->next) {
+               struct ofono_modem *modem = list->data;
+
+               ofono_modem_remove(modem);
+       }
+
+       g_slist_free(modem_list);
+       modem_list = NULL;
+
+       g_hash_table_destroy(devpath_list);
+       devpath_list = NULL;
+
+       if (udev_ctx == NULL)
+               return;
+
+       udev_monitor_filter_remove(udev_mon);
+
+       udev_monitor_unref(udev_mon);
+       udev_unref(udev_ctx);
+}
+
+OFONO_PLUGIN_DEFINE(udev, "udev hardware detection", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT, udev_init, udev_exit)
diff --git a/plugins/udevng.c b/plugins/udevng.c
new file mode 100644 (file)
index 0000000..2a9200d
--- /dev/null
@@ -0,0 +1,1155 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libudev.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/log.h>
+
+struct modem_info {
+       char *syspath;
+       char *devname;
+       char *driver;
+       char *vendor;
+       char *model;
+       GSList *devices;
+       struct ofono_modem *modem;
+       const char *sysattr;
+};
+
+struct device_info {
+       char *devpath;
+       char *devnode;
+       char *interface;
+       char *number;
+       char *label;
+       char *sysattr;
+};
+
+static gboolean setup_isi(struct modem_info *modem)
+{
+       const char *node = NULL;
+       int addr = 0;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s %s", info->devnode, info->interface,
+                               info->number, info->label, info->sysattr);
+
+               if (g_strcmp0(info->sysattr, "820") == 0) {
+                       if (g_strcmp0(info->interface, "2/254/0") == 0)
+                               addr = 16;
+
+                       node = info->devnode;
+               }
+       }
+
+       if (node == NULL)
+               return FALSE;
+
+       DBG("interface=%s address=%d", node, addr);
+
+       ofono_modem_set_string(modem->modem, "Interface", node);
+       ofono_modem_set_integer(modem->modem, "Address", addr);
+
+       return TRUE;
+}
+
+static gboolean setup_mbm(struct modem_info *modem)
+{
+       const char *mdm = NULL, *app = NULL, *network = NULL, *gps = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s %s", info->devnode, info->interface,
+                               info->number, info->label, info->sysattr);
+
+               if (g_str_has_suffix(info->sysattr, "Modem") == TRUE ||
+                               g_str_has_suffix(info->sysattr,
+                                                       "Modem 2") == TRUE) {
+                       if (mdm == NULL)
+                               mdm = info->devnode;
+                       else
+                               app = info->devnode;
+               } else if (g_str_has_suffix(info->sysattr,
+                                               "GPS Port") == TRUE ||
+                               g_str_has_suffix(info->sysattr,
+                                               "Module NMEA") == TRUE) {
+                       gps = info->devnode;
+               } else if (g_str_has_suffix(info->sysattr,
+                                               "Network Adapter") == TRUE ||
+                               g_str_has_suffix(info->sysattr,
+                                               "NetworkAdapter") == TRUE) {
+                       network = info->devnode;
+               }
+       }
+
+       if (mdm == NULL || app == NULL)
+               return FALSE;
+
+       DBG("modem=%s data=%s network=%s gps=%s", mdm, app, network, gps);
+
+       ofono_modem_set_string(modem->modem, "ModemDevice", mdm);
+       ofono_modem_set_string(modem->modem, "DataDevice", app);
+       ofono_modem_set_string(modem->modem, "GPSDevice", gps);
+       ofono_modem_set_string(modem->modem, "NetworkInterface", network);
+
+       return TRUE;
+}
+
+static gboolean setup_hso(struct modem_info *modem)
+{
+       const char *control = NULL, *application = NULL, *network = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s %s", info->devnode, info->interface,
+                               info->number, info->label, info->sysattr);
+
+               if (g_strcmp0(info->sysattr, "Control") == 0)
+                       control = info->devnode;
+               else if (g_strcmp0(info->sysattr, "Application") == 0)
+                       application = info->devnode;
+               else if (info->sysattr == NULL &&
+                               g_str_has_prefix(info->devnode, "hso") == TRUE)
+                       network = info->devnode;
+       }
+
+       if (control == NULL || application == NULL)
+               return FALSE;
+
+       DBG("control=%s application=%s network=%s",
+                               control, application, network);
+
+       ofono_modem_set_string(modem->modem, "ControlPort", control);
+       ofono_modem_set_string(modem->modem, "ApplicationPort", application);
+       ofono_modem_set_string(modem->modem, "NetworkInterface", network);
+
+       return TRUE;
+}
+
+static gboolean setup_gobi(struct modem_info *modem)
+{
+       const char *device = NULL, *gps = NULL, *qcdm = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->interface, "255/255/255") == 0) {
+                       if (g_strcmp0(info->number, "01") == 0)
+                               qcdm = info->devnode;
+                       else if (g_strcmp0(info->number, "02") == 0)
+                               device = info->devnode;
+                       else if (g_strcmp0(info->number, "03") == 0)
+                               gps = info->devnode;
+               }
+       }
+
+       if (device == NULL)
+               return FALSE;
+
+       DBG("device=%s gps=%s qcdm=%s", device, gps, qcdm);
+
+       ofono_modem_set_string(modem->modem, "Device", device);
+
+       return TRUE;
+}
+
+static gboolean setup_sierra(struct modem_info *modem)
+{
+       const char *device = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->interface, "255/255/255") == 0 &&
+                                       g_strcmp0(info->number, "03") == 0) {
+                       device = info->devnode;
+                       break;
+               }
+       }
+
+       if (device == NULL)
+               return FALSE;
+
+       DBG("device=%s", device);
+
+       ofono_modem_set_string(modem->modem, "Device", device);
+
+       return TRUE;
+}
+
+static gboolean setup_huawei(struct modem_info *modem)
+{
+       const char *mdm = NULL, *pcui = NULL, *diag = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "modem") == 0 ||
+                               g_strcmp0(info->interface, "255/1/1") == 0 ||
+                               g_strcmp0(info->interface, "255/2/1") == 0) {
+                       mdm = info->devnode;
+                       if (pcui != NULL && diag != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "pcui") == 0 ||
+                               g_strcmp0(info->interface, "255/1/2") == 0 ||
+                               g_strcmp0(info->interface, "255/2/2") == 0) {
+                       pcui = info->devnode;
+                       if (mdm != NULL && diag != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "diag") == 0 ||
+                               g_strcmp0(info->interface, "255/1/3") == 0 ||
+                               g_strcmp0(info->interface, "255/2/3") == 0) {
+                       diag = info->devnode;
+                       if (mdm != NULL && pcui != NULL)
+                               break;
+               } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
+                       if (g_strcmp0(info->number, "00") == 0)
+                               mdm = info->devnode;
+                       else if (g_strcmp0(info->number, "01") == 0)
+                               pcui = info->devnode;
+                       else if (g_strcmp0(info->number, "02") == 0)
+                               pcui = info->devnode;
+                       else if (g_strcmp0(info->number, "03") == 0)
+                               pcui = info->devnode;
+                       else if (g_strcmp0(info->number, "04") == 0)
+                               pcui = info->devnode;
+               }
+       }
+
+       if (mdm == NULL || pcui == NULL)
+               return FALSE;
+
+       DBG("modem=%s pcui=%s diag=%s", mdm, pcui, diag);
+
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+       ofono_modem_set_string(modem->modem, "Pcui", pcui);
+       ofono_modem_set_string(modem->modem, "Diag", diag);
+
+       return TRUE;
+}
+
+static gboolean setup_speedup(struct modem_info *modem)
+{
+       const char *aux = NULL, *mdm = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "aux") == 0) {
+                       aux = info->devnode;
+                       if (mdm != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "modem") == 0) {
+                       mdm = info->devnode;
+                       if (aux != NULL)
+                               break;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("aux=%s modem=%s", aux, mdm);
+
+       ofono_modem_set_string(modem->modem, "Aux", aux);
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+
+       return TRUE;
+}
+
+static gboolean setup_linktop(struct modem_info *modem)
+{
+       const char *aux = NULL, *mdm = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->interface, "2/2/1") == 0) {
+                       if (g_strcmp0(info->number, "01") == 0)
+                               aux = info->devnode;
+                       else if (g_strcmp0(info->number, "03") == 0)
+                               mdm = info->devnode;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("aux=%s modem=%s", aux, mdm);
+
+       ofono_modem_set_string(modem->modem, "Aux", aux);
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+
+       return TRUE;
+}
+
+static gboolean setup_alcatel(struct modem_info *modem)
+{
+       const char *aux = NULL, *mdm = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "aux") == 0) {
+                       aux = info->devnode;
+                       if (mdm != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "modem") == 0) {
+                       mdm = info->devnode;
+                       if (aux != NULL)
+                               break;
+               } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
+                       if (g_strcmp0(info->number, "03") == 0)
+                               aux = info->devnode;
+                       else if (g_strcmp0(info->number, "05") == 0)
+                               mdm = info->devnode;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("aux=%s modem=%s", aux, mdm);
+
+       ofono_modem_set_string(modem->modem, "Aux", aux);
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+
+       return TRUE;
+}
+
+static gboolean setup_novatel(struct modem_info *modem)
+{
+       const char *aux = NULL, *mdm = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "aux") == 0) {
+                       aux = info->devnode;
+                       if (mdm != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "modem") == 0) {
+                       mdm = info->devnode;
+                       if (aux != NULL)
+                               break;
+               } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
+                       if (g_strcmp0(info->number, "00") == 0)
+                               aux = info->devnode;
+                       else if (g_strcmp0(info->number, "01") == 0)
+                               mdm = info->devnode;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("aux=%s modem=%s", aux, mdm);
+
+       ofono_modem_set_string(modem->modem, "Aux", aux);
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+
+       return TRUE;
+}
+
+static gboolean setup_nokia(struct modem_info *modem)
+{
+       const char *aux = NULL, *mdm = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "aux") == 0) {
+                       aux = info->devnode;
+                       if (mdm != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "modem") == 0) {
+                       mdm = info->devnode;
+                       if (aux != NULL)
+                               break;
+               } else if (g_strcmp0(info->interface, "10/0/0") == 0) {
+                       if (g_strcmp0(info->number, "02") == 0)
+                               mdm = info->devnode;
+                       else if (g_strcmp0(info->number, "04") == 0)
+                               aux = info->devnode;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("aux=%s modem=%s", aux, mdm);
+
+       ofono_modem_set_string(modem->modem, "Aux", aux);
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+
+       return TRUE;
+}
+
+static gboolean setup_telit(struct modem_info *modem)
+{
+       const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "aux") == 0) {
+                       aux = info->devnode;
+                       if (mdm != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "modem") == 0) {
+                       mdm = info->devnode;
+                       if (aux != NULL)
+                               break;
+               } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
+                       if (g_strcmp0(info->number, "00") == 0)
+                               mdm = info->devnode;
+                       else if (g_strcmp0(info->number, "01") == 0)
+                               diag = info->devnode;
+                       else if (g_strcmp0(info->number, "02") == 0)
+                               gps = info->devnode;
+                       else if (g_strcmp0(info->number, "03") == 0)
+                               aux = info->devnode;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag);
+
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+       ofono_modem_set_string(modem->modem, "Data", aux);
+       ofono_modem_set_string(modem->modem, "GPS", gps);
+
+       return TRUE;
+}
+
+static gboolean setup_simcom(struct modem_info *modem)
+{
+       const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "aux") == 0) {
+                       aux = info->devnode;
+                       if (mdm != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "modem") == 0) {
+                       mdm = info->devnode;
+                       if (aux != NULL)
+                               break;
+               } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
+                       if (g_strcmp0(info->number, "00") == 0)
+                               diag = info->devnode;
+                       else if (g_strcmp0(info->number, "01") == 0)
+                               gps = info->devnode;
+                       else if (g_strcmp0(info->number, "02") == 0)
+                               aux = info->devnode;
+                       else if (g_strcmp0(info->number, "03") == 0)
+                               mdm = info->devnode;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag);
+
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+       ofono_modem_set_string(modem->modem, "Data", aux);
+       ofono_modem_set_string(modem->modem, "GPS", gps);
+
+       return TRUE;
+}
+
+static gboolean setup_zte(struct modem_info *modem)
+{
+       const char *aux = NULL, *mdm = NULL, *qcdm = NULL;
+       const char *modem_intf;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       if (g_strcmp0(modem->model, "0016") == 0 ||
+                               g_strcmp0(modem->model, "0017") == 0 ||
+                               g_strcmp0(modem->model, "0117") == 0)
+               modem_intf = "02";
+       else
+               modem_intf = "03";
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->label, "aux") == 0) {
+                       aux = info->devnode;
+                       if (mdm != NULL)
+                               break;
+               } else if (g_strcmp0(info->label, "modem") == 0) {
+                       mdm = info->devnode;
+                       if (aux != NULL)
+                               break;
+               } else if (g_strcmp0(info->interface, "255/255/255") == 0) {
+                       if (g_strcmp0(info->number, "00") == 0)
+                               qcdm = info->devnode;
+                       else if (g_strcmp0(info->number, "01") == 0)
+                               aux = info->devnode;
+                       else if (g_strcmp0(info->number, modem_intf) == 0)
+                               mdm = info->devnode;
+               }
+       }
+
+       if (aux == NULL || mdm == NULL)
+               return FALSE;
+
+       DBG("aux=%s modem=%s qcdm=%s", aux, mdm, qcdm);
+
+       ofono_modem_set_string(modem->modem, "Aux", aux);
+       ofono_modem_set_string(modem->modem, "Modem", mdm);
+
+       return TRUE;
+}
+
+static gboolean setup_samsung(struct modem_info *modem)
+{
+       const char *control = NULL, *network = NULL;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s %s %s %s", info->devnode, info->interface,
+                                               info->number, info->label);
+
+               if (g_strcmp0(info->interface, "10/0/0") == 0)
+                       control = info->devnode;
+               else if (g_strcmp0(info->interface, "255/0/0") == 0)
+                       network = info->devnode;
+       }
+
+       if (control == NULL && network == NULL)
+               return FALSE;
+
+       DBG("control=%s network=%s", control, network);
+
+       ofono_modem_set_string(modem->modem, "ControlPort", control);
+       ofono_modem_set_string(modem->modem, "NetworkInterface", network);
+
+       return TRUE;
+}
+
+static struct {
+       const char *name;
+       gboolean (*setup)(struct modem_info *modem);
+       const char *sysattr;
+} driver_list[] = {
+       { "isiusb",     setup_isi,      "type"                  },
+       { "mbm",        setup_mbm,      "device/interface"      },
+       { "hso",        setup_hso,      "hsotype"               },
+       { "gobi",       setup_gobi,     },
+       { "sierra",     setup_sierra    },
+       { "huawei",     setup_huawei    },
+       { "speedupcdma",setup_speedup   },
+       { "speedup",    setup_speedup   },
+       { "linktop",    setup_linktop   },
+       { "alcatel",    setup_alcatel   },
+       { "novatel",    setup_novatel   },
+       { "nokia",      setup_nokia     },
+       { "telit",      setup_telit     },
+       { "simcom",     setup_simcom    },
+       { "zte",        setup_zte       },
+       { "samsung",    setup_samsung   },
+       { }
+};
+
+static GHashTable *modem_list;
+
+static const char *get_sysattr(const char *driver)
+{
+       unsigned int i;
+
+       for (i = 0; driver_list[i].name; i++) {
+               if (g_str_equal(driver_list[i].name, driver) == TRUE)
+                       return driver_list[i].sysattr;
+       }
+
+       return NULL;
+}
+
+static void destroy_modem(gpointer data)
+{
+       struct modem_info *modem = data;
+       GSList *list;
+
+       DBG("%s", modem->syspath);
+
+       ofono_modem_remove(modem->modem);
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               DBG("%s", info->devnode);
+
+               g_free(info->devpath);
+               g_free(info->devnode);
+               g_free(info->interface);
+               g_free(info->number);
+               g_free(info->label);
+               g_free(info->sysattr);
+               g_free(info);
+
+               list->data = NULL;
+       }
+
+       g_slist_free(modem->devices);
+
+       g_free(modem->syspath);
+       g_free(modem->devname);
+       g_free(modem->driver);
+       g_free(modem->vendor);
+       g_free(modem->model);
+       g_free(modem);
+}
+
+static gboolean check_remove(gpointer key, gpointer value, gpointer user_data)
+{
+       struct modem_info *modem = value;
+       const char *devpath = user_data;
+       GSList *list;
+
+       for (list = modem->devices; list; list = list->next) {
+               struct device_info *info = list->data;
+
+               if (g_strcmp0(info->devpath, devpath) == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void remove_device(struct udev_device *device)
+{
+       const char *syspath;
+
+       syspath = udev_device_get_syspath(device);
+       if (syspath == NULL)
+               return;
+
+       DBG("%s", syspath);
+
+       g_hash_table_foreach_remove(modem_list, check_remove,
+                                               (char *) syspath);
+}
+
+static gint compare_device(gconstpointer a, gconstpointer b)
+{
+       const struct device_info *info1 = a;
+       const struct device_info *info2 = b;
+
+       return g_strcmp0(info1->number, info2->number);
+}
+
+static void add_device(const char *syspath, const char *devname,
+                       const char *driver, const char *vendor,
+                       const char *model, struct udev_device *device)
+{
+       struct udev_device *intf;
+       const char *devpath, *devnode, *interface, *number, *label, *sysattr;
+       struct modem_info *modem;
+       struct device_info *info;
+
+       devpath = udev_device_get_syspath(device);
+       if (devpath == NULL)
+               return;
+
+       devnode = udev_device_get_devnode(device);
+       if (devnode == NULL) {
+               devnode = udev_device_get_property_value(device, "INTERFACE");
+               if (devnode == NULL)
+                       return;
+       }
+
+       intf = udev_device_get_parent_with_subsystem_devtype(device,
+                                               "usb", "usb_interface");
+       if (intf == NULL)
+               return;
+
+       modem = g_hash_table_lookup(modem_list, syspath);
+       if (modem == NULL) {
+               modem = g_try_new0(struct modem_info, 1);
+               if (modem == NULL)
+                       return;
+
+               modem->syspath = g_strdup(syspath);
+               modem->devname = g_strdup(devname);
+               modem->driver = g_strdup(driver);
+               modem->vendor = g_strdup(vendor);
+               modem->model = g_strdup(model);
+
+               modem->sysattr = get_sysattr(driver);
+
+               g_hash_table_replace(modem_list, modem->syspath, modem);
+       }
+
+       interface = udev_device_get_property_value(intf, "INTERFACE");
+       number = udev_device_get_property_value(device, "ID_USB_INTERFACE_NUM");
+
+       label = udev_device_get_property_value(device, "OFONO_LABEL");
+
+       if (modem->sysattr != NULL)
+               sysattr = udev_device_get_sysattr_value(device, modem->sysattr);
+       else
+               sysattr = NULL;
+
+       DBG("%s", devpath);
+       DBG("%s (%s) %s [%s] ==> %s %s", devnode, driver,
+                                       interface, number, label, sysattr);
+
+       info = g_try_new0(struct device_info, 1);
+       if (info == NULL)
+               return;
+
+       info->devpath = g_strdup(devpath);
+       info->devnode = g_strdup(devnode);
+       info->interface = g_strdup(interface);
+       info->number = g_strdup(number);
+       info->label = g_strdup(label);
+       info->sysattr = g_strdup(sysattr);
+
+       modem->devices = g_slist_insert_sorted(modem->devices, info,
+                                                       compare_device);
+}
+
+static struct {
+       const char *driver;
+       const char *drv;
+       const char *vid;
+       const char *pid;
+} vendor_list[] = {
+       { "isiusb",     "cdc_phonet"                    },
+       { "linktop",    "cdc_acm",      "230d"          },
+       { "mbm",        "cdc_acm",      "0bdb"          },
+       { "mbm"         "cdc_ether",    "0bdb"          },
+       { "mbm",        "cdc_acm",      "0fce"          },
+       { "mbm",        "cdc_ether",    "0fce"          },
+       { "mbm",        "cdc_acm",      "413c"          },
+       { "mbm",        "cdc_ether",    "413c"          },
+       { "mbm",        "cdc_acm",      "03f0"          },
+       { "mbm",        "cdc_ether",    "03f0"          },
+       { "mbm",        "cdc_acm",      "0930"          },
+       { "mbm",        "cdc_ether",    "0930"          },
+       { "hso",        "hso"                           },
+       { "gobi",       "qcserial"                      },
+       { "sierra",     "sierra"                        },
+       { "huawei",     "option",       "201e"          },
+       { "huawei",     "cdc_ether",    "12d1"          },
+       { "huawei",     "option",       "12d1"          },
+       { "speedupcdma","option",       "1c9e", "9e00"  },
+       { "speedup",    "option",       "1c9e"          },
+       { "speedup",    "option",       "2020"          },
+       { "alcatel",    "option",       "1bbb", "0017"  },
+       { "novatel",    "option",       "1410"          },
+       { "zte",        "option",       "19d2"          },
+       { "simcom",     "option",       "05c6", "9000"  },
+       { "telit",      "usbserial",    "1bc7"          },
+       { "telit",      "option",       "1bc7"          },
+       { "nokia",      "option",       "0421", "060e"  },
+       { "nokia",      "option",       "0421", "0623"  },
+       { "samsung",    "option",       "04e8", "6889"  },
+       { "samsung",    "kalmia"                        },
+       { }
+};
+
+static void check_usb_device(struct udev_device *device)
+{
+       struct udev_device *usb_device;
+       const char *syspath, *devname, *driver;
+       const char *vendor = NULL, *model = NULL;
+
+       usb_device = udev_device_get_parent_with_subsystem_devtype(device,
+                                                       "usb", "usb_device");
+       if (usb_device == NULL)
+               return;
+
+       syspath = udev_device_get_syspath(usb_device);
+       if (syspath == NULL)
+               return;
+
+       devname = udev_device_get_devnode(usb_device);
+       if (devname == NULL)
+               return;
+
+       driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER");
+       if (driver == NULL) {
+               const char *drv, *vid, *pid;
+               unsigned int i;
+
+               drv = udev_device_get_property_value(device, "ID_USB_DRIVER");
+               if (drv == NULL)
+                       return;
+
+               vid = udev_device_get_property_value(device, "ID_VENDOR_ID");
+               if (vid == NULL)
+                       return;
+
+               pid = udev_device_get_property_value(device, "ID_MODEL_ID");
+               if (pid == NULL)
+                       return;
+
+               DBG("%s [%s:%s]", drv, vid, pid);
+
+               for (i = 0; vendor_list[i].driver; i++) {
+                       if (g_str_equal(vendor_list[i].drv, drv) == FALSE)
+                               continue;
+
+                       if (vendor_list[i].vid == NULL) {
+                               driver = vendor_list[i].driver;
+                               vendor = vid;
+                               model = pid;
+                               break;
+                       }
+
+                       if (g_str_equal(vendor_list[i].vid, vid) == TRUE) {
+                               if (vendor_list[i].pid == NULL) {
+                                       if (driver == NULL) {
+                                               driver = vendor_list[i].driver;
+                                               vendor = vid;
+                                               model = pid;
+                                       }
+                                       continue;
+                               }
+
+                               if (g_strcmp0(vendor_list[i].pid, pid) == 0) {
+                                       driver = vendor_list[i].driver;
+                                       vendor = vid;
+                                       model = pid;
+                                       break;
+                               }
+                       }
+               }
+
+               if (driver == NULL)
+                       return;
+       }
+
+       add_device(syspath, devname, driver, vendor, model, device);
+}
+
+static void check_device(struct udev_device *device)
+{
+       const char *bus;
+
+       bus = udev_device_get_property_value(device, "ID_BUS");
+       if (bus == NULL)
+               return;
+
+       if (g_str_equal(bus, "usb") == TRUE)
+               check_usb_device(device);
+}
+
+static gboolean create_modem(gpointer key, gpointer value, gpointer user_data)
+{
+       struct modem_info *modem = value;
+       const char *syspath = key;
+       unsigned int i;
+
+       if (modem->modem != NULL)
+               return FALSE;
+
+       DBG("%s", syspath);
+
+       if (modem->devices == NULL)
+               return TRUE;
+
+       DBG("driver=%s", modem->driver);
+
+       modem->modem = ofono_modem_create(NULL, modem->driver);
+       if (modem->modem == NULL)
+               return TRUE;
+
+       for (i = 0; driver_list[i].name; i++) {
+               if (g_str_equal(driver_list[i].name, modem->driver) == FALSE)
+                       continue;
+
+               if (driver_list[i].setup(modem) == TRUE) {
+                       ofono_modem_register(modem->modem);
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+static void enumerate_devices(struct udev *context)
+{
+       struct udev_enumerate *enumerate;
+       struct udev_list_entry *entry;
+
+       DBG("");
+
+       enumerate = udev_enumerate_new(context);
+       if (enumerate == NULL)
+               return;
+
+       udev_enumerate_add_match_subsystem(enumerate, "tty");
+       udev_enumerate_add_match_subsystem(enumerate, "net");
+
+       udev_enumerate_scan_devices(enumerate);
+
+       entry = udev_enumerate_get_list_entry(enumerate);
+       while (entry) {
+               const char *syspath = udev_list_entry_get_name(entry);
+               struct udev_device *device;
+
+               device = udev_device_new_from_syspath(context, syspath);
+               if (device != NULL) {
+                       check_device(device);
+                       udev_device_unref(device);
+               }
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       udev_enumerate_unref(enumerate);
+
+       g_hash_table_foreach_remove(modem_list, create_modem, NULL);
+}
+
+static struct udev *udev_ctx;
+static struct udev_monitor *udev_mon;
+static guint udev_watch = 0;
+static guint udev_delay = 0;
+
+static gboolean check_modem_list(gpointer user_data)
+{
+       udev_delay = 0;
+
+       DBG("");
+
+       g_hash_table_foreach_remove(modem_list, create_modem, NULL);
+
+       return FALSE;
+}
+
+static gboolean udev_event(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct udev_device *device;
+       const char *action;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+               ofono_warn("Error with udev monitor channel");
+               udev_watch = 0;
+               return FALSE;
+       }
+
+       device = udev_monitor_receive_device(udev_mon);
+       if (device == NULL)
+               return TRUE;
+
+       action = udev_device_get_action(device);
+       if (action == NULL)
+               return TRUE;
+
+       if (g_str_equal(action, "add") == TRUE) {
+               if (udev_delay > 0)
+                       g_source_remove(udev_delay);
+
+               check_device(device);
+
+               udev_delay = g_timeout_add_seconds(1, check_modem_list, NULL);
+       } else if (g_str_equal(action, "remove") == TRUE)
+               remove_device(device);
+
+       udev_device_unref(device);
+
+       return TRUE;
+}
+
+static void udev_start(void)
+{
+       GIOChannel *channel;
+       int fd;
+
+       DBG("");
+
+       if (udev_monitor_enable_receiving(udev_mon) < 0) {
+               ofono_error("Failed to enable udev monitor");
+               return;
+       }
+
+       enumerate_devices(udev_ctx);
+
+       fd = udev_monitor_get_fd(udev_mon);
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL)
+               return;
+
+       udev_watch = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                                       udev_event, NULL);
+
+       g_io_channel_unref(channel);
+}
+
+static int detect_init(void)
+{
+       udev_ctx = udev_new();
+       if (udev_ctx == NULL) {
+               ofono_error("Failed to create udev context");
+               return -EIO;
+       }
+
+       udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
+       if (udev_mon == NULL) {
+               ofono_error("Failed to create udev monitor");
+               udev_unref(udev_ctx);
+               udev_ctx = NULL;
+               return -EIO;
+       }
+
+       modem_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, destroy_modem);
+
+       udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL);
+       udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL);
+
+       udev_monitor_filter_update(udev_mon);
+
+       udev_start();
+
+       return 0;
+}
+
+static void detect_exit(void)
+{
+       if (udev_delay > 0)
+               g_source_remove(udev_delay);
+
+       if (udev_watch > 0)
+               g_source_remove(udev_watch);
+
+       if (udev_ctx == NULL)
+               return;
+
+       udev_monitor_filter_remove(udev_mon);
+
+       g_hash_table_destroy(modem_list);
+
+       udev_monitor_unref(udev_mon);
+       udev_unref(udev_ctx);
+}
+
+OFONO_PLUGIN_DEFINE(udevng, "udev hardware detection", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, detect_init, detect_exit)
diff --git a/plugins/wavecom.c b/plugins/wavecom.c
new file mode 100644 (file)
index 0000000..5d30f39
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+
+#include <drivers/atmodem/vendor.h>
+
+
+static int wavecom_probe(struct ofono_modem *modem)
+{
+       return 0;
+}
+
+static void wavecom_remove(struct ofono_modem *modem)
+{
+}
+
+static void wavecom_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static int wavecom_enable(struct ofono_modem *modem)
+{
+       GAtChat *chat;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       const char *device;
+       GHashTable *options;
+
+       DBG("%p", modem);
+
+       device = ofono_modem_get_string(modem, "Device");
+       if (device == NULL)
+               return -EINVAL;
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+
+       if (options == NULL)
+               return -ENOMEM;
+
+       g_hash_table_insert(options, "Baud", "115200");
+       g_hash_table_insert(options, "Parity", "none");
+       g_hash_table_insert(options, "StopBits", "1");
+       g_hash_table_insert(options, "DataBits", "8");
+
+       channel = g_at_tty_open(device, options);
+
+       g_hash_table_destroy(options);
+
+       if (channel == NULL)
+               return -EIO;
+
+       /*
+        * Could not figure out whether it is fully compliant or not, use
+        * permissive for now
+        * */
+       syntax = g_at_syntax_new_gsm_permissive();
+
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return -ENOMEM;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, wavecom_debug, "");
+
+       ofono_modem_set_data(modem, chat);
+
+       return 0;
+}
+
+static int wavecom_disable(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       g_at_chat_unref(chat);
+
+       return 0;
+}
+
+static void wavecom_pre_sim(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", chat);
+       ofono_sim_create(modem, OFONO_VENDOR_WAVECOM, "atmodem", chat);
+       ofono_voicecall_create(modem, 0, "atmodem", chat);
+}
+
+static void wavecom_post_sim(struct ofono_modem *modem)
+{
+       GAtChat *chat = ofono_modem_get_data(modem);
+       struct ofono_message_waiting *mw;
+
+       DBG("%p", modem);
+
+       ofono_ussd_create(modem, 0, "atmodem", chat);
+       ofono_call_forwarding_create(modem, 0, "atmodem", chat);
+       ofono_call_settings_create(modem, 0, "atmodem", chat);
+       ofono_netreg_create(modem, 0, "atmodem", chat);
+       ofono_call_meter_create(modem, 0, "atmodem", chat);
+       ofono_call_barring_create(modem, 0, "atmodem", chat);
+       ofono_sms_create(modem, 0, "atmodem", chat);
+       ofono_phonebook_create(modem, 0, "atmodem", chat);
+
+       mw = ofono_message_waiting_create(modem);
+       if (mw)
+               ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver wavecom_driver = {
+       .name           = "wavecom",
+       .probe          = wavecom_probe,
+       .remove         = wavecom_remove,
+       .enable         = wavecom_enable,
+       .disable        = wavecom_disable,
+       .pre_sim        = wavecom_pre_sim,
+       .post_sim       = wavecom_post_sim,
+};
+
+static int wavecom_init(void)
+{
+       return ofono_modem_driver_register(&wavecom_driver);
+}
+
+static void wavecom_exit(void)
+{
+       ofono_modem_driver_unregister(&wavecom_driver);
+}
+
+OFONO_PLUGIN_DEFINE(wavecom, "Wavecom driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, wavecom_init, wavecom_exit)
diff --git a/plugins/zte.c b/plugins/zte.c
new file mode 100644 (file)
index 0000000..9a1b9e9
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/sim.h>
+#include <ofono/cbs.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/phonebook.h>
+#include <ofono/log.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+
+struct zte_data {
+       GAtChat *modem;
+       GAtChat *aux;
+       gboolean have_sim;
+       struct at_util_sim_state_query *sim_state_query;
+};
+
+static int zte_probe(struct ofono_modem *modem)
+{
+       struct zte_data *data;
+
+       DBG("%p", modem);
+
+       data = g_try_new0(struct zte_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ofono_modem_set_data(modem, data);
+
+       return 0;
+}
+
+static void zte_remove(struct ofono_modem *modem)
+{
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_modem_set_data(modem, NULL);
+
+       /* Cleanup potential SIM state polling */
+       at_util_sim_state_query_free(data->sim_state_query);
+
+       /* Cleanup after hot-unplug */
+       g_at_chat_unref(data->aux);
+
+       g_free(data);
+}
+
+static void zte_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+                               const char *key, char *debug)
+{
+       const char *device;
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+       GHashTable *options;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+
+       DBG("%s %s", key, device);
+
+       options = g_hash_table_new(g_str_hash, g_str_equal);
+       if (options == NULL)
+               return NULL;
+
+       g_hash_table_insert(options, "Baud", "115200");
+       g_hash_table_insert(options, "Parity", "none");
+       g_hash_table_insert(options, "StopBits", "1");
+       g_hash_table_insert(options, "DataBits", "8");
+       g_hash_table_insert(options, "XonXoff", "off");
+       g_hash_table_insert(options, "RtsCts", "on");
+       g_hash_table_insert(options, "Local", "on");
+       g_hash_table_insert(options, "Read", "on");
+
+       channel = g_at_tty_open(device, options);
+
+       g_hash_table_destroy(options);
+
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, zte_debug, debug);
+
+       return chat;
+}
+
+static void zoprt_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       /* AT&C0 needs to be send separate and on both channel */
+       g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "AT&C0", NULL, NULL, NULL, NULL);
+
+       /*
+        * Ensure that the modem is using GSM character set and not IRA,
+        * otherwise weirdness with umlauts and other non-ASCII characters
+        * can result
+        */
+       g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "AT+CSCS=\"GSM\"", none_prefix,
+                                                       NULL, NULL, NULL);
+
+       /* Read PCB information */
+       g_at_chat_send(data->aux, "AT+ZPCB?", none_prefix, NULL, NULL, NULL);
+
+       ofono_modem_set_powered(modem, TRUE);
+}
+
+static void sim_state_cb(gboolean present, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       at_util_sim_state_query_free(data->sim_state_query);
+       data->sim_state_query = NULL;
+
+       data->have_sim = present;
+
+       /* Switch device into offline mode now */
+       g_at_chat_send(data->aux, "AT+ZOPRT=6", none_prefix,
+                                       zoprt_enable, modem, NULL);
+}
+
+static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       if (!ok) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+
+               g_at_chat_unref(data->aux);
+               data->aux = NULL;
+
+               ofono_modem_set_powered(modem, FALSE);
+               return;
+       }
+
+       data->sim_state_query = at_util_sim_state_query_new(data->aux,
+                                               2, 20, sim_state_cb, modem);
+}
+
+static int zte_enable(struct ofono_modem *modem)
+{
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->modem = open_device(modem, "Modem", "Modem: ");
+       if (data->modem == NULL)
+               return -EINVAL;
+
+       data->aux = open_device(modem, "Aux", "Aux: ");
+       if (data->aux == NULL) {
+               g_at_chat_unref(data->modem);
+               data->modem = NULL;
+               return -EIO;
+       }
+
+       g_at_chat_set_slave(data->modem, data->aux);
+
+       g_at_chat_blacklist_terminator(data->aux,
+                                       G_AT_CHAT_TERMINATOR_NO_CARRIER);
+
+       g_at_chat_send(data->modem, "ATZ E0 +CMEE=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
+
+       /* Switch device on first */
+       g_at_chat_send(data->aux, "AT+CFUN=1", NULL,
+                                       cfun_enable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_unref(data->aux);
+       data->aux = NULL;
+
+       if (ok)
+               ofono_modem_set_powered(modem, FALSE);
+}
+
+static void zoprt_disable(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       g_at_chat_send(data->aux, "AT+CFUN=0", NULL,
+                                       cfun_disable, modem, NULL);
+}
+
+static int zte_disable(struct ofono_modem *modem)
+{
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_cancel_all(data->modem);
+       g_at_chat_unregister_all(data->modem);
+
+       g_at_chat_unref(data->modem);
+       data->modem = NULL;
+
+       g_at_chat_cancel_all(data->aux);
+       g_at_chat_unregister_all(data->aux);
+
+       /* Switch to offline mode first */
+       g_at_chat_send(data->aux, "AT+ZOPRT=6", none_prefix,
+                                       zoprt_disable, modem, NULL);
+
+       return -EINPROGRESS;
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_modem_online_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, cbd->data);
+}
+
+static void zte_set_online(struct ofono_modem *modem, ofono_bool_t online,
+                               ofono_modem_online_cb_t cb, void *user_data)
+{
+       struct zte_data *data = ofono_modem_get_data(modem);
+       struct cb_data *cbd = cb_data_new(cb, user_data);
+       char const *command = online ? "AT+ZOPRT=5" : "AT+ZOPRT=6";
+
+       DBG("modem %p %s", modem, online ? "online" : "offline");
+
+       if (g_at_chat_send(data->aux, command, none_prefix,
+                                       set_online_cb, cbd, g_free) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+       g_free(cbd);
+}
+
+static void zte_pre_sim(struct ofono_modem *modem)
+{
+       struct zte_data *data = ofono_modem_get_data(modem);
+       struct ofono_sim *sim;
+
+       DBG("%p", modem);
+
+       ofono_devinfo_create(modem, 0, "atmodem", data->aux);
+       sim = ofono_sim_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux);
+
+       if (sim && data->have_sim == TRUE)
+               ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void zte_post_sim(struct ofono_modem *modem)
+{
+       struct zte_data *data = ofono_modem_get_data(modem);
+       struct ofono_gprs *gprs;
+       struct ofono_gprs_context *gc;
+
+       DBG("%p", modem);
+
+       ofono_phonebook_create(modem, 0, "atmodem", data->aux);
+
+       ofono_sms_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux);
+
+       gprs = ofono_gprs_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux);
+       gc = ofono_gprs_context_create(modem, OFONO_VENDOR_ZTE,
+                                               "atmodem", data->modem);
+
+       if (gprs && gc)
+               ofono_gprs_add_context(gprs, gc);
+}
+
+static void zte_post_online(struct ofono_modem *modem)
+{
+       struct zte_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       ofono_netreg_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux);
+
+       ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                       "atmodem", data->aux);
+       ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM,
+                                       "atmodem", data->aux);
+}
+
+static struct ofono_modem_driver zte_driver = {
+       .name           = "zte",
+       .probe          = zte_probe,
+       .remove         = zte_remove,
+       .enable         = zte_enable,
+       .disable        = zte_disable,
+       .set_online     = zte_set_online,
+       .pre_sim        = zte_pre_sim,
+       .post_sim       = zte_post_sim,
+       .post_online    = zte_post_online,
+};
+
+static int zte_init(void)
+{
+       return ofono_modem_driver_register(&zte_driver);
+}
+
+static void zte_exit(void)
+{
+       ofono_modem_driver_unregister(&zte_driver);
+}
+
+OFONO_PLUGIN_DEFINE(zte, "ZTE modem driver", VERSION,
+               OFONO_PLUGIN_PRIORITY_DEFAULT, zte_init, zte_exit)
diff --git a/src/audio-settings.c b/src/audio-settings.c
new file mode 100644 (file)
index 0000000..77930d9
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License veasion 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+#include "common.h"
+
+static GSList *g_drivers = NULL;
+
+struct ofono_audio_settings {
+       ofono_bool_t active;
+       char *mode;
+       const struct ofono_audio_settings_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+void ofono_audio_settings_active_notify(struct ofono_audio_settings *as,
+                                       ofono_bool_t active)
+{
+       const char *path = __ofono_atom_get_path(as->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (as->active == active)
+               return;
+
+       DBG("active %d", active);
+
+       as->active = active;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_AUDIO_SETTINGS_INTERFACE,
+                               "Active", DBUS_TYPE_BOOLEAN, &as->active);
+
+}
+
+void ofono_audio_settings_mode_notify(struct ofono_audio_settings *as,
+                                               const char *mode)
+{
+       const char *path = __ofono_atom_get_path(as->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       DBG("mode %s", mode);
+
+       g_free(as->mode);
+       as->mode = g_strdup(mode);
+
+       if (as->mode == NULL)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_AUDIO_SETTINGS_INTERFACE,
+                               "Mode", DBUS_TYPE_STRING, &as->mode);
+}
+
+static DBusMessage *audio_get_properties_reply(DBusMessage *msg,
+                                       struct ofono_audio_settings *as)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "Active", DBUS_TYPE_BOOLEAN, &as->active);
+
+       if (as->mode)
+               ofono_dbus_dict_append(&dict, "Mode",
+                                       DBUS_TYPE_STRING, &as->mode);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *audio_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_audio_settings *as = data;
+
+       return audio_get_properties_reply(msg, as);
+}
+
+static GDBusMethodTable audio_methods[] = {
+       { "GetProperties", "", "a{sv}", audio_get_properties,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable audio_signals[] = {
+       { "PropertyChanged", "sv" },
+       { }
+};
+
+int ofono_audio_settings_driver_register(const struct ofono_audio_settings_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_audio_settings_driver_unregister(const struct ofono_audio_settings_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void audio_settings_unregister(struct ofono_atom *atom)
+{
+       struct ofono_audio_settings *as = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(as->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(as->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_AUDIO_SETTINGS_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_AUDIO_SETTINGS_INTERFACE);
+}
+
+static void audio_settings_remove(struct ofono_atom *atom)
+{
+       struct ofono_audio_settings *as = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (as == NULL)
+               return;
+
+       if (as->driver && as->driver->remove)
+               as->driver->remove(as);
+
+       g_free(as->mode);
+       g_free(as);
+}
+
+struct ofono_audio_settings *ofono_audio_settings_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data)
+{
+       struct ofono_audio_settings *as;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       as = g_try_new0(struct ofono_audio_settings, 1);
+       if (as == NULL)
+               return NULL;
+
+       as->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_AUDIO_SETTINGS,
+                                               audio_settings_remove, as);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_audio_settings_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver) != 0)
+                       continue;
+
+               if (drv->probe(as, vendor, data) < 0)
+                       continue;
+
+               as->driver = drv;
+               break;
+       }
+
+       return as;
+}
+
+void ofono_audio_settings_register(struct ofono_audio_settings *as)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(as->atom);
+       const char *path = __ofono_atom_get_path(as->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_AUDIO_SETTINGS_INTERFACE,
+                                       audio_methods, audio_signals,
+                                       NULL, as, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_AUDIO_SETTINGS_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_AUDIO_SETTINGS_INTERFACE);
+       __ofono_atom_register(as->atom, audio_settings_unregister);
+}
+
+void ofono_audio_settings_remove(struct ofono_audio_settings *as)
+{
+       __ofono_atom_free(as->atom);
+}
+
+void ofono_audio_settings_set_data(struct ofono_audio_settings *as, void *data)
+{
+       as->driver_data = data;
+}
+
+void *ofono_audio_settings_get_data(struct ofono_audio_settings *as)
+{
+       return as->driver_data;
+}
+
+struct ofono_modem *ofono_audio_settings_get_modem(struct ofono_audio_settings *as)
+{
+       return __ofono_atom_get_modem(as->atom);
+}
diff --git a/src/call-barring.c b/src/call-barring.c
new file mode 100644 (file)
index 0000000..afb3fce
--- /dev/null
@@ -0,0 +1,1128 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+
+#define CALL_BARRING_FLAG_CACHED 0x1
+#define NUM_OF_BARRINGS 5
+
+static GSList *g_drivers = NULL;
+
+static void cb_ss_query_next_lock(struct ofono_call_barring *cb);
+static void get_query_next_lock(struct ofono_call_barring *cb);
+static void set_query_next_lock(struct ofono_call_barring *cb);
+
+struct ofono_call_barring {
+       int flags;
+       DBusMessage *pending;
+       int cur_locks[NUM_OF_BARRINGS];
+       int new_locks[NUM_OF_BARRINGS];
+       int query_start;
+       int query_end;
+       int query_next;
+       int ss_req_type;
+       int ss_req_cls;
+       int ss_req_lock;
+       struct ofono_ussd *ussd;
+       unsigned int ussd_watch;
+       const struct ofono_call_barring_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+struct call_barring_lock {
+       const char *name;
+       const char *value;
+       const char *fac;
+};
+
+static struct call_barring_lock cb_locks[] = {
+       { "AllOutgoing",                        "all",                  "AO" },
+       { "InternationalOutgoing",              "international",        "OI" },
+       { "InternationalOutgoingExceptHome",    "internationalnothome", "OX" },
+       { "AllIncoming",                        "always",               "AI" },
+       { "IncomingWhenRoaming",                "whenroaming",          "IR" },
+       { "AllBarringServices",                 NULL,                   "AB" },
+       { "AllOutgoingServices",                NULL,                   "AG" },
+       { "AllIncomingServices",                NULL,                   "AC" },
+       { NULL,                                 NULL,                   NULL },
+};
+
+/* These are inclusive */
+#define CB_OUTGOING_START 0
+#define CB_OUTGOING_END 2
+#define CB_INCOMING_START 3
+#define CB_INCOMING_END 4
+#define CB_ALL_START 0
+#define CB_ALL_END 4
+#define CB_ALL_OUTGOING 6
+#define CB_ALL_INCOMING 7
+
+static inline void emit_barring_changed(struct ofono_call_barring *cb,
+                                       int start, int end,
+                                       const char *type, int cls)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cb->atom);
+       char property_name[64];
+       const char *value;
+       int i;
+       int j;
+
+       for (i = start; i <= end; i++)
+               if (cb->cur_locks[i] & cls)
+                       break;
+
+       for (j = start; j <= end; j++)
+               if (cb->new_locks[j] & cls)
+                       break;
+
+       if (i == j)
+               return;
+
+       if (j > end)
+               value = "disabled";
+       else
+               value = cb_locks[j].value;
+
+       snprintf(property_name, sizeof(property_name), "%s%s",
+                       bearer_class_to_string(cls), type);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_BARRING_INTERFACE,
+                                               property_name, DBUS_TYPE_STRING,
+                                               &value);
+}
+
+static void update_barrings(struct ofono_call_barring *cb, int mask)
+{
+       int cls;
+       int i;
+
+       /* We're only interested in emitting signals for Voice, Fax & Data */
+       for (cls = 1; cls <= BEARER_CLASS_PAD; cls = cls << 1) {
+               if ((cls & mask) == 0)
+                       continue;
+
+               emit_barring_changed(cb, cb->query_start, CB_OUTGOING_END,
+                                       "Outgoing", cls);
+               emit_barring_changed(cb, CB_INCOMING_START, cb->query_end,
+                                       "Incoming", cls);
+       }
+
+       for (i = cb->query_start; i <= cb->query_end; i++)
+               cb->cur_locks[i] = cb->new_locks[i];
+}
+
+static void cb_ss_property_append(struct ofono_call_barring *cb,
+                                       DBusMessageIter *dict, int lock,
+                                       int mask)
+{
+       int i;
+       char property_name[64];
+       const char *strvalue;
+
+       for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) {
+               if (!(mask & i))
+                       continue;
+
+               strvalue = (cb->new_locks[lock] & i) ? "enabled" : "disabled";
+
+               snprintf(property_name, sizeof(property_name), "%s%s",
+                               bearer_class_to_string(i),
+                               cb_locks[lock].name);
+
+               ofono_dbus_dict_append(dict, property_name, DBUS_TYPE_STRING,
+                                       &strvalue);
+       }
+}
+
+static void cb_set_query_bounds(struct ofono_call_barring *cb,
+                               const char *fac, gboolean fac_only)
+{
+       int i;
+
+       if (!strcmp("AB", fac)) {
+               cb->query_start = CB_ALL_START;
+               cb->query_end = CB_ALL_END;
+               cb->query_next = CB_ALL_START;
+               return;
+       }
+
+       if (!strcmp("AG", fac))
+               goto outgoing;
+
+       if (!strcmp("AC", fac))
+               goto incoming;
+
+       for (i = 0; cb_locks[i].name; i++) {
+               if (strcmp(cb_locks[i].fac, fac))
+                       continue;
+
+               if (fac_only) {
+                       cb->query_start = i;
+                       cb->query_end = i;
+                       cb->query_next = i;
+                       return;
+               }
+
+               if ((i >= CB_OUTGOING_START) &&
+                       (i <= CB_OUTGOING_END))
+                       goto outgoing;
+               else if ((i >= CB_INCOMING_START) &&
+                               (i <= CB_INCOMING_END))
+                       goto incoming;
+       }
+
+       ofono_error("Unable to set query boundaries for %s", fac);
+       return;
+
+outgoing:
+       cb->query_start = CB_OUTGOING_START;
+       cb->query_end = CB_OUTGOING_END;
+       cb->query_next = CB_OUTGOING_START;
+       return;
+
+incoming:
+       cb->query_start = CB_INCOMING_START;
+       cb->query_end = CB_INCOMING_END;
+       cb->query_next = CB_INCOMING_START;
+       return;
+}
+
+static void generate_ss_query_reply(struct ofono_call_barring *cb)
+{
+       const char *context = "CallBarring";
+       const char *sig = "(ssa{sv})";
+       const char *ss_type = ss_control_type_to_string(cb->ss_req_type);
+       const char *ss_fac = cb_locks[cb->ss_req_lock].name;
+       DBusMessageIter iter;
+       DBusMessageIter variant;
+       DBusMessageIter vstruct;
+       DBusMessageIter dict;
+       DBusMessage *reply;
+       int lock;
+       int start, end;
+
+       reply = dbus_message_new_method_return(cb->pending);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
+                                               &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_STRUCT, NULL,
+                                               &vstruct);
+
+       dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_type);
+
+       dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_fac);
+
+       dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       /* We report all affected locks only for the special case ones */
+       if (cb->ss_req_lock <= CB_ALL_END) {
+               start = cb->ss_req_lock;
+               end = cb->ss_req_lock;
+       } else {
+               start = cb->query_start;
+               end = cb->query_end;
+       }
+
+       for (lock = start; lock <= end; lock++)
+               cb_ss_property_append(cb, &dict, lock, cb->ss_req_cls);
+
+       dbus_message_iter_close_container(&vstruct, &dict);
+
+       dbus_message_iter_close_container(&variant, &vstruct);
+
+       dbus_message_iter_close_container(&iter, &variant);
+
+       __ofono_dbus_pending_reply(&cb->pending, reply);
+}
+
+static void cb_ss_query_next_lock_callback(const struct ofono_error *error,
+                                       int status, void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               if (cb->ss_req_type != SS_CONTROL_TYPE_QUERY)
+                       ofono_error("Enabling/disabling Call Barring via SS "
+                                       "successful, but query was not");
+
+               cb->flags &= ~CALL_BARRING_FLAG_CACHED;
+
+               __ofono_dbus_pending_reply(&cb->pending,
+                                       __ofono_error_failed(cb->pending));
+               return;
+       }
+
+       cb->new_locks[cb->query_next] = status;
+
+       if (cb->query_next < cb->query_end) {
+               cb->query_next += 1;
+               cb_ss_query_next_lock(cb);
+               return;
+       }
+
+       generate_ss_query_reply(cb);
+       update_barrings(cb, BEARER_CLASS_VOICE);
+}
+
+static void cb_ss_query_next_lock(struct ofono_call_barring *cb)
+{
+       int cls;
+
+       cls = cb->ss_req_cls | BEARER_CLASS_DEFAULT;
+
+       cb->driver->query(cb, cb_locks[cb->query_next].fac, cls,
+                       cb_ss_query_next_lock_callback, cb);
+}
+
+static void cb_ss_set_lock_callback(const struct ofono_error *error,
+               void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Enabling/disabling Call Barring via SS failed");
+               __ofono_dbus_pending_reply(&cb->pending,
+                                       __ofono_error_failed(cb->pending));
+               return;
+       }
+
+       /* Assume we have query always */
+       cb_ss_query_next_lock(cb);
+}
+
+static const char *cb_ss_service_to_fac(const char *svc)
+{
+       if (!strcmp(svc, "33"))
+               return "AO";
+       else if (!strcmp(svc, "331"))
+               return "OI";
+       else if (!strcmp(svc, "332"))
+               return "OX";
+       else if (!strcmp(svc, "35"))
+               return "AI";
+       else if (!strcmp(svc, "351"))
+               return "IR";
+       else if (!strcmp(svc, "330"))
+               return "AB";
+       else if (!strcmp(svc, "333"))
+               return "AG";
+       else if (!strcmp(svc, "353"))
+               return "AC";
+
+       return NULL;
+}
+
+static gboolean cb_ss_control(int type, const char *sc,
+                               const char *sia, const char *sib,
+                               const char *sic, const char *dn,
+                               DBusMessage *msg, void *data)
+{
+       struct ofono_call_barring *cb = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       int cls = BEARER_CLASS_DEFAULT;
+       const char *fac;
+       DBusMessage *reply;
+       void *operation = NULL;
+       int i;
+
+       if (__ofono_call_barring_is_busy(cb)) {
+               reply = __ofono_error_busy(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       DBG("Received call barring ss control request");
+
+       DBG("type: %d, sc: %s, sia: %s, sib: %s, sic: %s, dn: %s",
+               type, sc, sia, sib, sic, dn);
+
+       fac = cb_ss_service_to_fac(sc);
+       if (fac == NULL)
+               return FALSE;
+
+       cb_set_query_bounds(cb, fac, type == SS_CONTROL_TYPE_QUERY);
+
+       i = 0;
+       while (cb_locks[i].name && strcmp(cb_locks[i].fac, fac))
+               i++;
+
+       cb->ss_req_lock = i;
+
+       if (strlen(sic) > 0)
+               goto bad_format;
+
+       if (strlen(dn) > 0)
+               goto bad_format;
+
+       if (type != SS_CONTROL_TYPE_QUERY && !__ofono_is_valid_net_pin(sia))
+               goto bad_format;
+
+       switch (type) {
+       case SS_CONTROL_TYPE_ACTIVATION:
+       case SS_CONTROL_TYPE_DEACTIVATION:
+       case SS_CONTROL_TYPE_REGISTRATION:
+       case SS_CONTROL_TYPE_ERASURE:
+               operation = cb->driver->set;
+               break;
+       case SS_CONTROL_TYPE_QUERY:
+               operation = cb->driver->query;
+               break;
+       default:
+               break;
+       }
+
+       if (operation == NULL) {
+               reply = __ofono_error_not_implemented(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       /*
+        * According to 27.007, AG, AC and AB only work with mode = 0
+        * We support query by querying all relevant types, since we must
+        * do this for the deactivation case anyway
+        */
+       if ((!strcmp(fac, "AG") || !strcmp(fac, "AC") || !strcmp(fac, "AB")) &&
+               (type == SS_CONTROL_TYPE_ACTIVATION ||
+                       type == SS_CONTROL_TYPE_REGISTRATION))
+               goto bad_format;
+
+       if (strlen(sib) > 0) {
+               long service_code;
+               char *end;
+
+               service_code = strtoul(sib, &end, 10);
+
+               if (end == sib || *end != '\0')
+                       goto bad_format;
+
+               cls = mmi_service_code_to_bearer_class(service_code);
+
+               if (cls == 0)
+                       goto bad_format;
+       }
+
+       cb->ss_req_cls = cls;
+       cb->pending = dbus_message_ref(msg);
+
+       switch (type) {
+       case SS_CONTROL_TYPE_ACTIVATION:
+       case SS_CONTROL_TYPE_REGISTRATION:
+               cb->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
+               cb->driver->set(cb, fac, 1, sia, cls,
+                               cb_ss_set_lock_callback, cb);
+               break;
+       case SS_CONTROL_TYPE_ERASURE:
+       case SS_CONTROL_TYPE_DEACTIVATION:
+               cb->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
+               cb->driver->set(cb, fac, 0, sia, cls,
+                               cb_ss_set_lock_callback, cb);
+               break;
+       case SS_CONTROL_TYPE_QUERY:
+               cb->ss_req_type = SS_CONTROL_TYPE_QUERY;
+               cb_ss_query_next_lock(cb);
+               break;
+       }
+
+       return TRUE;
+
+bad_format:
+       reply = __ofono_error_invalid_format(msg);
+       g_dbus_send_message(conn, reply);
+       return TRUE;
+}
+
+static void cb_set_passwd_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_barring *cb = data;
+       DBusMessage *reply;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               reply = dbus_message_new_method_return(cb->pending);
+       else {
+               reply = __ofono_error_failed(cb->pending);
+               DBG("Changing Call Barring password via SS failed");
+       }
+
+       __ofono_dbus_pending_reply(&cb->pending, reply);
+}
+
+static gboolean cb_ss_passwd(const char *sc,
+                               const char *old, const char *new,
+                               DBusMessage *msg, void *data)
+{
+       struct ofono_call_barring *cb = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *reply;
+       const char *fac;
+
+       if (__ofono_call_barring_is_busy(cb)) {
+               reply = __ofono_error_busy(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       DBG("Received call barring ss password change request");
+
+       DBG("sc: %s", sc);
+
+       if (!strcmp(sc, ""))
+               fac = "AB";
+       else
+               fac = cb_ss_service_to_fac(sc);
+
+       if (fac == NULL)
+               return FALSE;
+
+       if (!__ofono_is_valid_net_pin(old) || !__ofono_is_valid_net_pin(new))
+               goto bad_format;
+
+       cb->pending = dbus_message_ref(msg);
+       cb->driver->set_passwd(cb, fac, old, new, cb_set_passwd_callback, cb);
+
+       return TRUE;
+bad_format:
+       reply = __ofono_error_invalid_format(msg);
+       g_dbus_send_message(conn, reply);
+       return TRUE;
+}
+
+static void cb_register_ss_controls(struct ofono_call_barring *cb)
+{
+       __ofono_ussd_ssc_register(cb->ussd, "33", cb_ss_control, cb, NULL);
+       __ofono_ussd_ssc_register(cb->ussd, "331", cb_ss_control, cb, NULL);
+       __ofono_ussd_ssc_register(cb->ussd, "332", cb_ss_control, cb, NULL);
+       __ofono_ussd_ssc_register(cb->ussd, "35", cb_ss_control, cb, NULL);
+       __ofono_ussd_ssc_register(cb->ussd, "351", cb_ss_control, cb, NULL);
+       __ofono_ussd_ssc_register(cb->ussd, "330", cb_ss_control, cb, NULL);
+       __ofono_ussd_ssc_register(cb->ussd, "333", cb_ss_control, cb, NULL);
+       __ofono_ussd_ssc_register(cb->ussd, "353", cb_ss_control, cb, NULL);
+
+       __ofono_ussd_passwd_register(cb->ussd, "", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "33", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "331", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "332", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "35", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "351", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "330", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "333", cb_ss_passwd, cb, NULL);
+       __ofono_ussd_passwd_register(cb->ussd, "353", cb_ss_passwd, cb, NULL);
+}
+
+static void cb_unregister_ss_controls(struct ofono_call_barring *cb)
+{
+       __ofono_ussd_ssc_unregister(cb->ussd, "33");
+       __ofono_ussd_ssc_unregister(cb->ussd, "331");
+       __ofono_ussd_ssc_unregister(cb->ussd, "332");
+       __ofono_ussd_ssc_unregister(cb->ussd, "35");
+       __ofono_ussd_ssc_unregister(cb->ussd, "351");
+       __ofono_ussd_ssc_unregister(cb->ussd, "330");
+       __ofono_ussd_ssc_unregister(cb->ussd, "333");
+       __ofono_ussd_ssc_unregister(cb->ussd, "353");
+
+       __ofono_ussd_passwd_unregister(cb->ussd, "");
+       __ofono_ussd_passwd_unregister(cb->ussd, "33");
+       __ofono_ussd_passwd_unregister(cb->ussd, "331");
+       __ofono_ussd_passwd_unregister(cb->ussd, "332");
+       __ofono_ussd_passwd_unregister(cb->ussd, "35");
+       __ofono_ussd_passwd_unregister(cb->ussd, "351");
+       __ofono_ussd_passwd_unregister(cb->ussd, "330");
+       __ofono_ussd_passwd_unregister(cb->ussd, "333");
+       __ofono_ussd_passwd_unregister(cb->ussd, "353");
+}
+
+gboolean __ofono_call_barring_is_busy(struct ofono_call_barring *cb)
+{
+       return cb->pending ? TRUE : FALSE;
+}
+
+static inline void cb_append_property(struct ofono_call_barring *cb,
+                                       DBusMessageIter *dict, int start,
+                                       int end, int cls, const char *property)
+{
+       char property_name[64];
+       const char *value = "disabled";
+       int i;
+
+       for (i = start; i <= end; i++)
+               if (cb->new_locks[i] & cls)
+                       break;
+
+       if (i <= end)
+               value = cb_locks[i].value;
+
+       snprintf(property_name, sizeof(property_name), "%s%s",
+                       bearer_class_to_string(cls), property);
+
+       ofono_dbus_dict_append(dict, property_name, DBUS_TYPE_STRING,
+                               &value);
+}
+
+static void cb_get_properties_reply(struct ofono_call_barring *cb, int mask)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, dict;
+       int j;
+
+       if (!(cb->flags & CALL_BARRING_FLAG_CACHED))
+               ofono_error("Generating a get_properties reply with no cache");
+
+       reply = dbus_message_new_method_return(cb->pending);
+       if (reply == NULL)
+               return;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) {
+               if ((j & mask) == 0)
+                       continue;
+
+               cb_append_property(cb, &dict, CB_OUTGOING_START,
+                                       CB_OUTGOING_END, j, "Outgoing");
+               cb_append_property(cb, &dict, CB_INCOMING_START,
+                                       CB_INCOMING_END, j, "Incoming");
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       __ofono_dbus_pending_reply(&cb->pending, reply);
+}
+
+static void get_query_lock_callback(const struct ofono_error *error,
+                                       int status, void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+               cb->new_locks[cb->query_next] = status;
+
+               if (cb->query_next == CB_ALL_END)
+                       cb->flags |= CALL_BARRING_FLAG_CACHED;
+       }
+
+       if (cb->query_next < CB_ALL_END) {
+               cb->query_next = cb->query_next + 1;
+               get_query_next_lock(cb);
+               return;
+       }
+
+       cb_get_properties_reply(cb, BEARER_CLASS_VOICE);
+       update_barrings(cb, BEARER_CLASS_VOICE);
+}
+
+static void get_query_next_lock(struct ofono_call_barring *cb)
+{
+       cb->driver->query(cb, cb_locks[cb->query_next].fac,
+                       BEARER_CLASS_DEFAULT, get_query_lock_callback, cb);
+}
+
+static DBusMessage *cb_get_properties(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd))
+               return __ofono_error_busy(msg);
+
+       if (cb->driver->query == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       cb->pending = dbus_message_ref(msg);
+
+       if (cb->flags & CALL_BARRING_FLAG_CACHED)
+               cb_get_properties_reply(cb, BEARER_CLASS_VOICE);
+       else {
+               cb->query_next = CB_ALL_START;
+               get_query_next_lock(cb);
+       }
+
+       return NULL;
+}
+
+static void set_query_lock_callback(const struct ofono_error *error,
+                               int status, void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Disabling all barring successful, "
+                               "but query was not");
+
+               cb->flags &= ~CALL_BARRING_FLAG_CACHED;
+
+               __ofono_dbus_pending_reply(&cb->pending,
+                                       __ofono_error_failed(cb->pending));
+               return;
+       }
+
+       cb->new_locks[cb->query_next] = status;
+
+       if (cb->query_next < cb->query_end) {
+               cb->query_next += 1;
+               set_query_next_lock(cb);
+               return;
+       }
+
+       __ofono_dbus_pending_reply(&cb->pending,
+                               dbus_message_new_method_return(cb->pending));
+       update_barrings(cb, BEARER_CLASS_VOICE);
+}
+
+static void set_query_next_lock(struct ofono_call_barring *cb)
+{
+       cb->driver->query(cb, cb_locks[cb->query_next].fac,
+                       BEARER_CLASS_DEFAULT, set_query_lock_callback, cb);
+}
+
+static void set_lock_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Enabling/disabling a lock failed");
+               __ofono_dbus_pending_reply(&cb->pending,
+                                       __ofono_error_failed(cb->pending));
+               return;
+       }
+
+       /*
+        * If we successfully set the value, we must query it back
+        * Call Barring is a special case, since according to 22.088 2.2.1:
+        * "The PLMN will ensure that only one of the barring programs is
+        * active per basic service group. The activation of one specific
+        * barring program will override an already active one (i.e. the
+        * old one will be permanently deactivated)."
+        * So we actually query all outgoing / incoming barrings depending
+        * on what kind we set.
+        */
+       set_query_next_lock(cb);
+}
+
+static gboolean cb_lock_property_lookup(const char *property, const char *value,
+                                       int mask, int *out_which, int *out_cls,
+                                       int *out_mode)
+{
+       int i, j;
+       const char *prefix;
+       size_t len;
+       int start, end;
+
+       for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) {
+               if ((i & mask) == 0)
+                       continue;
+
+               prefix = bearer_class_to_string(i);
+               len = strlen(prefix);
+
+               if (!strncmp(property, prefix, len))
+                       break;
+       }
+
+       if (i > BEARER_CLASS_PAD)
+               return FALSE;
+
+       property += len;
+
+       if (!strcmp(property, "Outgoing")) {
+               start = CB_OUTGOING_START;
+               end = CB_OUTGOING_END;
+       } else if (!strcmp(property, "Incoming")) {
+               start = CB_INCOMING_START;
+               end = CB_INCOMING_END;
+       } else {
+               return FALSE;
+       }
+
+       /*
+        * Gah, this is a special case.  If we're setting a barring to
+        * disabled, then generate a disable all outgoing/incoming
+        * request for a particular basic service
+        */
+       if (!strcmp(value, "disabled")) {
+               *out_mode = 0;
+               *out_cls = i;
+
+               if (!strcmp(property, "Outgoing"))
+                       *out_which = CB_ALL_OUTGOING;
+               else
+                       *out_which = CB_ALL_INCOMING;
+
+               return TRUE;
+       }
+
+       for (j = start; j <= end; j++) {
+               if (strcmp(value, cb_locks[j].value))
+                       continue;
+
+               *out_mode = 1;
+               *out_cls = i;
+               *out_which = j;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static DBusMessage *cb_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_barring *cb = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *name, *passwd = "";
+       const char *value;
+       int lock;
+       int cls;
+       int mode;
+
+       if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd))
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&var, &value);
+
+       if (!cb_lock_property_lookup(name, value, BEARER_CLASS_VOICE,
+                                       &lock, &cls, &mode))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_next(&iter)) {
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&iter, &passwd);
+               if (!__ofono_is_valid_net_pin(passwd))
+                       return __ofono_error_invalid_format(msg);
+       }
+
+       if (cb->driver->set == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       cb_set_query_bounds(cb, cb_locks[lock].fac, FALSE);
+
+       cb->pending = dbus_message_ref(msg);
+       cb->driver->set(cb, cb_locks[lock].fac, mode, passwd, cls,
+                       set_lock_callback, cb);
+
+       return NULL;
+}
+
+static void disable_all_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Disabling all barring failed");
+               __ofono_dbus_pending_reply(&cb->pending,
+                                       __ofono_error_failed(cb->pending));
+               return;
+       }
+
+       /* Assume if we have set, we have query */
+       set_query_next_lock(cb);
+}
+
+static DBusMessage *cb_disable_all(DBusConnection *conn, DBusMessage *msg,
+                                       void *data, const char *fac)
+{
+       struct ofono_call_barring *cb = data;
+       const char *passwd;
+
+       if (cb->driver->set == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd))
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &passwd,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_is_valid_net_pin(passwd))
+               return __ofono_error_invalid_format(msg);
+
+       cb_set_query_bounds(cb, fac, FALSE);
+
+       cb->pending = dbus_message_ref(msg);
+       cb->driver->set(cb, fac, 0, passwd,
+                       BEARER_CLASS_DEFAULT, disable_all_callback, cb);
+
+       return NULL;
+}
+
+static DBusMessage *cb_disable_ab(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       return cb_disable_all(conn, msg, data, "AB");
+}
+
+static DBusMessage *cb_disable_ac(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       return cb_disable_all(conn, msg, data, "AC");
+}
+
+static DBusMessage *cb_disable_ag(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       return cb_disable_all(conn, msg, data, "AG");
+}
+
+static DBusMessage *cb_set_passwd(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_barring *cb = data;
+       const char *old_passwd;
+       const char *new_passwd;
+
+       if (cb->driver->set_passwd == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd))
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &old_passwd,
+                                       DBUS_TYPE_STRING, &new_passwd,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_is_valid_net_pin(old_passwd))
+               return __ofono_error_invalid_format(msg);
+
+       if (!__ofono_is_valid_net_pin(new_passwd))
+               return __ofono_error_invalid_format(msg);
+
+       cb->pending = dbus_message_ref(msg);
+       cb->driver->set_passwd(cb, "AB", old_passwd, new_passwd,
+                       cb_set_passwd_callback, cb);
+
+       return NULL;
+}
+
+static GDBusMethodTable cb_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        cb_get_properties,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "SetProperty",        "svs",  "",             cb_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "DisableAll",         "s",    "",             cb_disable_ab,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "DisableAllIncoming", "s",    "",             cb_disable_ac,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "DisableAllOutgoing", "s",    "",             cb_disable_ag,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "ChangePassword",     "ss",   "",             cb_set_passwd,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable cb_signals[] = {
+       { "PropertyChanged",            "sv" },
+       { }
+};
+
+int ofono_call_barring_driver_register(const struct ofono_call_barring_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_call_barring_driver_unregister(const struct ofono_call_barring_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void call_barring_unregister(struct ofono_atom *atom)
+{
+       struct ofono_call_barring *cb = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(cb->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cb->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_CALL_BARRING_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_CALL_BARRING_INTERFACE);
+
+       if (cb->ussd)
+               cb_unregister_ss_controls(cb);
+
+       if (cb->ussd_watch)
+               __ofono_modem_remove_atom_watch(modem, cb->ussd_watch);
+}
+
+static void call_barring_remove(struct ofono_atom *atom)
+{
+       struct ofono_call_barring *cb = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cb == NULL)
+               return;
+
+       if (cb->driver != NULL && cb->driver->remove != NULL)
+               cb->driver->remove(cb);
+
+       g_free(cb);
+}
+
+struct ofono_call_barring *ofono_call_barring_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data)
+{
+       struct ofono_call_barring *cb;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cb = g_try_new0(struct ofono_call_barring, 1);
+
+       if (cb == NULL)
+               return NULL;
+
+       cb->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CALL_BARRING,
+                                               call_barring_remove, cb);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_call_barring_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cb, vendor, data) < 0)
+                       continue;
+
+               cb->driver = drv;
+               break;
+       }
+
+       return cb;
+}
+
+static void ussd_watch(struct ofono_atom *atom,
+                       enum ofono_atom_watch_condition cond, void *data)
+{
+       struct ofono_call_barring *cb = data;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               cb->ussd = NULL;
+               return;
+       }
+
+       cb->ussd = __ofono_atom_get_data(atom);
+       cb_register_ss_controls(cb);
+}
+
+void ofono_call_barring_register(struct ofono_call_barring *cb)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cb->atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(cb->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CALL_BARRING_INTERFACE,
+                                       cb_methods, cb_signals, NULL, cb,
+                                       NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CALL_BARRING_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_CALL_BARRING_INTERFACE);
+
+       cb->ussd_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_USSD,
+                                       ussd_watch, cb, NULL);
+
+       __ofono_atom_register(cb->atom, call_barring_unregister);
+}
+
+void ofono_call_barring_remove(struct ofono_call_barring *cb)
+{
+       __ofono_atom_free(cb->atom);
+}
+
+void ofono_call_barring_set_data(struct ofono_call_barring *cb, void *data)
+{
+       cb->driver_data = data;
+}
+
+void *ofono_call_barring_get_data(struct ofono_call_barring *cb)
+{
+       return cb->driver_data;
+}
diff --git a/src/call-forwarding.c b/src/call-forwarding.c
new file mode 100644 (file)
index 0000000..a58ca21
--- /dev/null
@@ -0,0 +1,1563 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "simutil.h"
+
+#define uninitialized_var(x) x = x
+
+#define CALL_FORWARDING_FLAG_CACHED 0x1
+#define CALL_FORWARDING_FLAG_CPHS_CFF 0x2
+
+/* According to 27.007 Spec */
+#define DEFAULT_NO_REPLY_TIMEOUT 20
+
+static GSList *g_drivers = NULL;
+
+enum call_forwarding_type {
+       CALL_FORWARDING_TYPE_UNCONDITIONAL =            0,
+       CALL_FORWARDING_TYPE_BUSY =                     1,
+       CALL_FORWARDING_TYPE_NO_REPLY =                 2,
+       CALL_FORWARDING_TYPE_NOT_REACHABLE =            3,
+       CALL_FORWARDING_TYPE_ALL =                      4,
+       CALL_FORWARDING_TYPE_ALL_CONDITIONAL =          5
+};
+
+struct ofono_call_forwarding {
+       GSList *cf_conditions[4];
+       int flags;
+       DBusMessage *pending;
+       int query_next;
+       int query_end;
+       struct cf_ss_request *ss_req;
+       struct ofono_sim *sim;
+       struct ofono_sim_context *sim_context;
+       unsigned char cfis_record_id;
+       struct ofono_ussd *ussd;
+       unsigned int ussd_watch;
+       const struct ofono_call_forwarding_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static void get_query_next_cf_cond(struct ofono_call_forwarding *cf);
+static void set_query_next_cf_cond(struct ofono_call_forwarding *cf);
+static void ss_set_query_next_cf_cond(struct ofono_call_forwarding *cf);
+
+struct cf_ss_request {
+       int ss_type;
+       int cf_type;
+       int cls;
+       GSList *cf_list[4];
+};
+
+static gint cf_condition_compare(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_call_forwarding_condition *ca = a;
+       const struct ofono_call_forwarding_condition *cb = b;
+
+       if (ca->cls < cb->cls)
+               return -1;
+
+       if (ca->cls > cb->cls)
+               return 1;
+
+       return 0;
+}
+
+static gint cf_condition_find_with_cls(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_call_forwarding_condition *c = a;
+       int cls = GPOINTER_TO_INT(b);
+
+       if (c->cls < cls)
+               return -1;
+
+       if (c->cls > cls)
+               return 1;
+
+       return 0;
+}
+
+static int cf_find_timeout(GSList *cf_list, int cls)
+{
+       GSList *l;
+       struct ofono_call_forwarding_condition *c;
+
+       l = g_slist_find_custom(cf_list, GINT_TO_POINTER(cls),
+               cf_condition_find_with_cls);
+
+       if (l == NULL)
+               return DEFAULT_NO_REPLY_TIMEOUT;
+
+       c = l->data;
+
+       return c->time;
+}
+
+static void cf_cond_list_print(GSList *list)
+{
+       GSList *l;
+       struct ofono_call_forwarding_condition *cond;
+
+       for (l = list; l; l = l->next) {
+               cond = l->data;
+
+               DBG("CF Condition status: %d, class: %d, number: %s,"
+                       " number_type: %d, time: %d",
+                       cond->status, cond->cls, cond->phone_number.number,
+                       cond->phone_number.type, cond->time);
+       }
+}
+
+static GSList *cf_cond_list_create(int total,
+                       const struct ofono_call_forwarding_condition *list)
+{
+       GSList *l = NULL;
+       int i;
+       int j;
+       struct ofono_call_forwarding_condition *cond;
+
+       /*
+        * Specification is not really clear how the results are reported,
+        * so assume both multiple list items & compound values of class
+        * are possible
+        */
+       for (i = 0; i < total; i++) {
+               for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) {
+                       if (!(list[i].cls & j))
+                               continue;
+
+                       if (list[i].status == 0)
+                               continue;
+
+                       cond = g_try_new0(struct ofono_call_forwarding_condition, 1);
+                       if (cond == NULL)
+                               continue;
+
+                       memcpy(cond, &list[i],
+                               sizeof(struct ofono_call_forwarding_condition));
+                       cond->cls = j;
+
+                       l = g_slist_insert_sorted(l, cond,
+                                                       cf_condition_compare);
+               }
+       }
+
+       return l;
+}
+
+static inline void cf_list_clear(GSList *cf_list)
+{
+       GSList *l;
+
+       for (l = cf_list; l; l = l->next)
+               g_free(l->data);
+
+       g_slist_free(cf_list);
+}
+
+static inline void cf_clear_all(struct ofono_call_forwarding *cf)
+{
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               cf_list_clear(cf->cf_conditions[i]);
+               cf->cf_conditions[i] = NULL;
+       }
+}
+
+static const char *cf_type_lut[] = {
+       "Unconditional",
+       "Busy",
+       "NoReply",
+       "NotReachable",
+       "All",
+       "AllConditional"
+};
+
+static void sim_cfis_update_cb(int ok, void *data)
+{
+       if (!ok)
+               ofono_info("Failed to update EFcfis");
+}
+
+static void sim_cphs_cff_update_cb(int ok, void *data)
+{
+       if (!ok)
+               ofono_info("Failed to update EFcphs-cff");
+}
+
+static gboolean is_cfu_enabled(struct ofono_call_forwarding *cf,
+                               struct ofono_call_forwarding_condition **out)
+{
+       GSList *l = cf->cf_conditions[CALL_FORWARDING_TYPE_UNCONDITIONAL];
+       struct ofono_call_forwarding_condition *cond;
+
+       /*
+        * For now we only support Voice, although Fax & all Data
+        * basic services are applicable as well.
+        */
+       for (; l; l = l->next) {
+               cond = l->data;
+
+               if (cond->cls > BEARER_CLASS_VOICE)
+                       continue;
+
+               if (out)
+                       *out = cond;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void sim_set_cf_indicator(struct ofono_call_forwarding *cf)
+{
+       gboolean cfu_voice;
+       struct ofono_call_forwarding_condition *uninitialized_var(cond);
+
+       cfu_voice = is_cfu_enabled(cf, &cond);
+
+       if (cf->cfis_record_id) {
+               unsigned char data[16];
+               int number_len;
+
+               memset(data, 0xff, sizeof(data));
+
+               /* Profile Identifier */
+               data[0] = 0x01;
+
+               if (cfu_voice) {
+                       number_len = strlen(cond->phone_number.number);
+
+                       /* CFU indicator Status - Voice */
+                       data[1] = 0x01;
+                       number_len = (number_len + 1) / 2;
+                       data[2] = number_len + 1;
+                       data[3] = cond->phone_number.type;
+
+                       sim_encode_bcd_number(cond->phone_number.number,
+                                               data + 4);
+               } else {
+                       data[1] = 0x00;
+                       data[2] = 1;
+                       data[3] = 128;
+               }
+
+               ofono_sim_write(cf->sim_context, SIM_EFCFIS_FILEID,
+                                       sim_cfis_update_cb,
+                                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                                       cf->cfis_record_id, data,
+                                       sizeof(data), cf);
+               return;
+       }
+
+       if (cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) {
+               unsigned char cff_voice = cfu_voice ? 0x0A : 0x05;
+
+               ofono_sim_write(cf->sim_context, SIM_EF_CPHS_CFF_FILEID,
+                                       sim_cphs_cff_update_cb,
+                                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                                       0, &cff_voice, sizeof(cff_voice), cf);
+       }
+}
+
+static void set_new_cond_list(struct ofono_call_forwarding *cf,
+                               int type, GSList *list)
+{
+       GSList *old = cf->cf_conditions[type];
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cf->atom);
+       GSList *l;
+       GSList *o;
+       struct ofono_call_forwarding_condition *lc;
+       struct ofono_call_forwarding_condition *oc;
+       const char *number;
+       dbus_uint16_t timeout;
+       char attr[64];
+       char tattr[64];
+       gboolean update_sim = FALSE;
+       gboolean old_cfu;
+       gboolean new_cfu;
+
+       if ((cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) ||
+                       cf->cfis_record_id > 0)
+               old_cfu = is_cfu_enabled(cf, NULL);
+       else
+               old_cfu = FALSE;
+
+       for (l = list; l; l = l->next) {
+               lc = l->data;
+
+               /*
+                * New condition lists might have attributes we don't care about
+                * triggered by e.g. ss control magic strings just skip them
+                * here.  For now we only support Voice, although Fax & all Data
+                * basic services are applicable as well.
+                */
+               if (lc->cls > BEARER_CLASS_VOICE)
+                       continue;
+
+               timeout = lc->time;
+               number = phone_number_to_string(&lc->phone_number);
+
+               snprintf(attr, sizeof(attr), "%s%s",
+                       bearer_class_to_string(lc->cls), cf_type_lut[type]);
+
+               if (type == CALL_FORWARDING_TYPE_NO_REPLY)
+                       snprintf(tattr, sizeof(tattr), "%sTimeout", attr);
+
+               o = g_slist_find_custom(old, GINT_TO_POINTER(lc->cls),
+                                       cf_condition_find_with_cls);
+
+               if (o) { /* On the old list, must be active */
+                       oc = o->data;
+
+                       if (oc->phone_number.type != lc->phone_number.type ||
+                               strcmp(oc->phone_number.number,
+                                       lc->phone_number.number)) {
+                               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_FORWARDING_INTERFACE,
+                                               attr, DBUS_TYPE_STRING,
+                                               &number);
+
+                               if (type == CALL_FORWARDING_TYPE_UNCONDITIONAL)
+                                       update_sim = TRUE;
+                       }
+
+                       if (type == CALL_FORWARDING_TYPE_NO_REPLY &&
+                               oc->time != lc->time)
+                               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_FORWARDING_INTERFACE,
+                                               tattr, DBUS_TYPE_UINT16,
+                                               &timeout);
+
+                       /* Remove from the old list */
+                       g_free(o->data);
+                       old = g_slist_remove(old, o->data);
+               } else {
+                       number = phone_number_to_string(&lc->phone_number);
+
+                       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_FORWARDING_INTERFACE,
+                                               attr, DBUS_TYPE_STRING,
+                                               &number);
+
+                       if (type == CALL_FORWARDING_TYPE_UNCONDITIONAL)
+                               update_sim = TRUE;
+
+                       if (type == CALL_FORWARDING_TYPE_NO_REPLY &&
+                               lc->time != DEFAULT_NO_REPLY_TIMEOUT)
+                               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_FORWARDING_INTERFACE,
+                                               tattr, DBUS_TYPE_UINT16,
+                                               &timeout);
+               }
+       }
+
+       timeout = DEFAULT_NO_REPLY_TIMEOUT;
+       number = "";
+
+       for (o = old; o; o = o->next) {
+               oc = o->data;
+
+               /*
+                * For now we only support Voice, although Fax & all Data
+                * basic services are applicable as well.
+                */
+               if (oc->cls > BEARER_CLASS_VOICE)
+                       continue;
+
+               snprintf(attr, sizeof(attr), "%s%s",
+                       bearer_class_to_string(oc->cls), cf_type_lut[type]);
+
+               if (type == CALL_FORWARDING_TYPE_NO_REPLY)
+                       snprintf(tattr, sizeof(tattr), "%sTimeout", attr);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CALL_FORWARDING_INTERFACE, attr,
+                                       DBUS_TYPE_STRING, &number);
+
+               if (type == CALL_FORWARDING_TYPE_UNCONDITIONAL)
+                       update_sim = TRUE;
+
+               if (type == CALL_FORWARDING_TYPE_NO_REPLY &&
+                       oc->time != DEFAULT_NO_REPLY_TIMEOUT)
+                       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_FORWARDING_INTERFACE,
+                                               tattr, DBUS_TYPE_UINT16,
+                                               &timeout);
+       }
+
+       cf_list_clear(old);
+       cf->cf_conditions[type] = list;
+
+       if (update_sim == TRUE)
+               sim_set_cf_indicator(cf);
+
+       if ((cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) ||
+                       cf->cfis_record_id > 0)
+               new_cfu = is_cfu_enabled(cf, NULL);
+       else
+               new_cfu = FALSE;
+
+       if (new_cfu != old_cfu) {
+               ofono_bool_t status = new_cfu;
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CALL_FORWARDING_INTERFACE,
+                                       "ForwardingFlagOnSim",
+                                       DBUS_TYPE_BOOLEAN, &status);
+       }
+}
+
+static inline void property_append_cf_condition(DBusMessageIter *dict, int cls,
+                                               const char *postfix,
+                                               const char *value,
+                                               dbus_uint16_t timeout)
+{
+       char attr[64];
+       char tattr[64];
+       int addt = !strcmp(postfix, "NoReply");
+
+       snprintf(attr, sizeof(attr), "%s%s",
+                       bearer_class_to_string(cls), postfix);
+
+       if (addt)
+               snprintf(tattr, sizeof(tattr), "%s%sTimeout",
+                               bearer_class_to_string(cls), postfix);
+
+       ofono_dbus_dict_append(dict, attr, DBUS_TYPE_STRING, &value);
+
+       if (addt)
+               ofono_dbus_dict_append(dict, tattr, DBUS_TYPE_UINT16, &timeout);
+}
+
+static void property_append_cf_conditions(DBusMessageIter *dict,
+                                               GSList *cf_list, int mask,
+                                               const char *postfix)
+{
+       GSList *l;
+       int i;
+       struct ofono_call_forwarding_condition *cf;
+       const char *number;
+
+       for (i = 1, l = cf_list; i <= BEARER_CLASS_PAD; i = i << 1) {
+               if (!(mask & i))
+                       continue;
+
+               while (l && (cf = l->data) && (cf->cls < i))
+                               l = l->next;
+
+               if (l == NULL || cf->cls != i) {
+                       property_append_cf_condition(dict, i, postfix, "",
+                                               DEFAULT_NO_REPLY_TIMEOUT);
+                       continue;
+               }
+
+               number = phone_number_to_string(&cf->phone_number);
+
+               property_append_cf_condition(dict, i, postfix, number,
+                                               cf->time);
+       }
+}
+
+static DBusMessage *cf_get_properties_reply(DBusMessage *msg,
+                                               struct ofono_call_forwarding *cf)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       int i;
+       dbus_bool_t status;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                               &dict);
+
+       for (i = 0; i < 4; i++)
+               property_append_cf_conditions(&dict, cf->cf_conditions[i],
+                                               BEARER_CLASS_VOICE,
+                                               cf_type_lut[i]);
+
+       if ((cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) ||
+                       cf->cfis_record_id > 0)
+               status = is_cfu_enabled(cf, NULL);
+       else
+               status = FALSE;
+
+       ofono_dbus_dict_append(&dict, "ForwardingFlagOnSim", DBUS_TYPE_BOOLEAN,
+                                       &status);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void get_query_cf_callback(const struct ofono_error *error, int total,
+                       const struct ofono_call_forwarding_condition *list,
+                       void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+               GSList *l;
+               l = cf_cond_list_create(total, list);
+               set_new_cond_list(cf, cf->query_next, l);
+
+               DBG("%s conditions:", cf_type_lut[cf->query_next]);
+               cf_cond_list_print(l);
+
+               if (cf->query_next == CALL_FORWARDING_TYPE_NOT_REACHABLE)
+                       cf->flags |= CALL_FORWARDING_FLAG_CACHED;
+       }
+
+       if (cf->query_next == CALL_FORWARDING_TYPE_NOT_REACHABLE) {
+               DBusMessage *reply = cf_get_properties_reply(cf->pending, cf);
+               __ofono_dbus_pending_reply(&cf->pending, reply);
+               return;
+       }
+
+       cf->query_next++;
+       get_query_next_cf_cond(cf);
+}
+
+static void get_query_next_cf_cond(struct ofono_call_forwarding *cf)
+{
+       cf->driver->query(cf, cf->query_next, BEARER_CLASS_DEFAULT,
+                       get_query_cf_callback, cf);
+}
+
+static DBusMessage *cf_get_properties(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+       struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom);
+
+       if ((cf->flags & CALL_FORWARDING_FLAG_CACHED) ||
+                       ofono_modem_get_online(modem) == FALSE)
+               return cf_get_properties_reply(msg, cf);
+
+       if (cf->driver->query == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (__ofono_call_forwarding_is_busy(cf) ||
+                       __ofono_ussd_is_busy(cf->ussd))
+               return __ofono_error_busy(msg);
+
+       cf->pending = dbus_message_ref(msg);
+       cf->query_next = 0;
+
+       get_query_next_cf_cond(cf);
+
+       return NULL;
+}
+
+static gboolean cf_condition_enabled_property(struct ofono_call_forwarding *cf,
+                       const char *property, int *out_type, int *out_cls)
+{
+       int i;
+       int j;
+       int len;
+       const char *prefix;
+
+       for (i = 1; i <= BEARER_CLASS_VOICE; i = i << 1) {
+               prefix = bearer_class_to_string(i);
+
+               len = strlen(prefix);
+
+               if (strncmp(property, prefix, len))
+                       continue;
+
+               /*
+                * We check the 4 call forwarding types, e.g.
+                * unconditional, busy, no reply, not reachable
+                */
+               for (j = 0; j < 4; j++)
+                       if (!strcmp(property+len, cf_type_lut[j])) {
+                               *out_type = j;
+                               *out_cls = i;
+                               return TRUE;
+                       }
+       }
+
+       return FALSE;
+}
+
+static gboolean cf_condition_timeout_property(const char *property,
+                                               int *out_cls)
+{
+       int i;
+       int len;
+       const char *prefix;
+
+       for (i = 1; i <= BEARER_CLASS_VOICE; i = i << 1) {
+               prefix = bearer_class_to_string(i);
+
+               len = strlen(prefix);
+
+               if (strncmp(property, prefix, len))
+                       continue;
+
+               if (!strcmp(property+len, "NoReplyTimeout")) {
+                       *out_cls = i;
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static void set_query_cf_callback(const struct ofono_error *error, int total,
+                       const struct ofono_call_forwarding_condition *list,
+                       void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+       GSList *l;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Setting succeeded, but query failed");
+               cf->flags &= ~CALL_FORWARDING_FLAG_CACHED;
+               reply = __ofono_error_failed(cf->pending);
+               __ofono_dbus_pending_reply(&cf->pending, reply);
+               return;
+       }
+
+       if (cf->query_next == cf->query_end) {
+               reply = dbus_message_new_method_return(cf->pending);
+               __ofono_dbus_pending_reply(&cf->pending, reply);
+       }
+
+       l = cf_cond_list_create(total, list);
+       set_new_cond_list(cf, cf->query_next, l);
+
+       DBG("%s conditions:", cf_type_lut[cf->query_next]);
+       cf_cond_list_print(l);
+
+       if (cf->query_next != cf->query_end) {
+               cf->query_next++;
+               set_query_next_cf_cond(cf);
+       }
+}
+
+static void set_query_next_cf_cond(struct ofono_call_forwarding *cf)
+{
+       cf->driver->query(cf, cf->query_next, BEARER_CLASS_DEFAULT,
+                       set_query_cf_callback, cf);
+}
+
+static void set_property_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error occurred during set/erasure");
+               __ofono_dbus_pending_reply(&cf->pending,
+                                       __ofono_error_failed(cf->pending));
+               return;
+       }
+
+       /* Successfully set, query the entire set just in case */
+       set_query_next_cf_cond(cf);
+}
+
+static DBusMessage *set_property_request(struct ofono_call_forwarding *cf,
+                                               DBusMessage *msg,
+                                               int type, int cls,
+                                               struct ofono_phone_number *ph,
+                                               int timeout)
+{
+       if (ph->number[0] != '\0' && cf->driver->registration == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (ph->number[0] == '\0' && cf->driver->erasure == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       cf->pending = dbus_message_ref(msg);
+       cf->query_next = type;
+       cf->query_end = type;
+
+       DBG("Farming off request, will be erasure: %d", ph->number[0] == '\0');
+
+       if (ph->number[0] != '\0')
+               cf->driver->registration(cf, type, cls, ph, timeout,
+                                       set_property_callback, cf);
+       else
+               cf->driver->erasure(cf, type, cls, set_property_callback, cf);
+
+       return NULL;
+}
+
+static DBusMessage *cf_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+       struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom);
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+       int cls;
+       int type;
+
+       if (ofono_modem_get_online(modem) == FALSE)
+               return __ofono_error_not_available(msg);
+
+       if (__ofono_call_forwarding_is_busy(cf) ||
+                       __ofono_ussd_is_busy(cf->ussd))
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (cf_condition_timeout_property(property, &cls)) {
+               dbus_uint16_t timeout;
+               GSList *l;
+               struct ofono_call_forwarding_condition *c;
+
+               type = CALL_FORWARDING_TYPE_NO_REPLY;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT16)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &timeout);
+
+               if (timeout < 1 || timeout > 30)
+                       return __ofono_error_invalid_format(msg);
+
+               l = g_slist_find_custom(cf->cf_conditions[type],
+                               GINT_TO_POINTER(cls),
+                               cf_condition_find_with_cls);
+
+               if (l == NULL)
+                       return __ofono_error_failed(msg);
+
+               c = l->data;
+
+               return set_property_request(cf, msg, type, cls,
+                                               &c->phone_number, timeout);
+       } else if (cf_condition_enabled_property(cf, property, &type, &cls)) {
+               struct ofono_phone_number ph;
+               const char *number;
+               int timeout;
+
+               ph.number[0] = '\0';
+               ph.type = 129;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &number);
+
+               if (strlen(number) > 0 && !valid_phone_number_format(number))
+                       return __ofono_error_invalid_format(msg);
+
+               if (number[0] != '\0')
+                       string_to_phone_number(number, &ph);
+
+               timeout = cf_find_timeout(cf->cf_conditions[type], cls);
+
+               return set_property_request(cf, msg, type, cls, &ph,
+                                               timeout);
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static void disable_conditional_callback(const struct ofono_error *error,
+                                               void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error occurred during conditional erasure");
+
+               __ofono_dbus_pending_reply(&cf->pending,
+                                       __ofono_error_failed(cf->pending));
+               return;
+       }
+
+       /* Query the three conditional cf types */
+       cf->query_next = CALL_FORWARDING_TYPE_BUSY;
+       cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE;
+       set_query_next_cf_cond(cf);
+}
+
+static void disable_all_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error occurred during erasure of all");
+
+               __ofono_dbus_pending_reply(&cf->pending,
+                                       __ofono_error_failed(cf->pending));
+               return;
+       }
+
+       /* Query all cf types */
+       cf->query_next = CALL_FORWARDING_TYPE_UNCONDITIONAL;
+       cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE;
+       set_query_next_cf_cond(cf);
+}
+
+static DBusMessage *cf_disable_all(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+       const char *strtype;
+       int type;
+
+       if (cf->driver->erasure == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (__ofono_call_forwarding_is_busy(cf) ||
+                       __ofono_ussd_is_busy(cf->ussd))
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &strtype,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!strcmp(strtype, "all") || !strcmp(strtype, ""))
+               type = CALL_FORWARDING_TYPE_ALL;
+       else if (!strcmp(strtype, "conditional"))
+               type = CALL_FORWARDING_TYPE_ALL_CONDITIONAL;
+       else
+               return __ofono_error_invalid_format(msg);
+
+       cf->pending = dbus_message_ref(msg);
+
+       if (type == CALL_FORWARDING_TYPE_ALL)
+               cf->driver->erasure(cf, type, BEARER_CLASS_DEFAULT,
+                               disable_all_callback, cf);
+       else
+               cf->driver->erasure(cf, type, BEARER_CLASS_DEFAULT,
+                               disable_conditional_callback, cf);
+
+       return NULL;
+}
+
+static GDBusMethodTable cf_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        cf_get_properties,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "SetProperty",        "sv",   "",             cf_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "DisableAll",         "s",    "",             cf_disable_all,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable cf_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static DBusMessage *cf_ss_control_reply(struct ofono_call_forwarding *cf,
+                                       struct cf_ss_request *req)
+{
+       const char *context = "CallForwarding";
+       const char *sig = "(ssa{sv})";
+       const char *ss_type = ss_control_type_to_string(req->ss_type);
+       const char *cf_type = cf_type_lut[req->cf_type];
+       DBusMessageIter iter;
+       DBusMessageIter variant;
+       DBusMessageIter vstruct;
+       DBusMessageIter dict;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(cf->pending);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
+                                               &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_STRUCT, NULL,
+                                               &vstruct);
+
+       dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+                                       &ss_type);
+
+       dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+                                       &cf_type);
+
+       dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY,
+                               OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+       if (req->cf_type == CALL_FORWARDING_TYPE_UNCONDITIONAL ||
+               req->cf_type == CALL_FORWARDING_TYPE_ALL)
+               property_append_cf_conditions(&dict,
+                       req->cf_list[CALL_FORWARDING_TYPE_UNCONDITIONAL],
+                       req->cls,
+                       cf_type_lut[CALL_FORWARDING_TYPE_UNCONDITIONAL]);
+
+       if (req->cf_type == CALL_FORWARDING_TYPE_NO_REPLY ||
+               req->cf_type == CALL_FORWARDING_TYPE_ALL ||
+               req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+               property_append_cf_conditions(&dict,
+                       req->cf_list[CALL_FORWARDING_TYPE_NO_REPLY],
+                       req->cls, cf_type_lut[CALL_FORWARDING_TYPE_NO_REPLY]);
+
+       if (req->cf_type == CALL_FORWARDING_TYPE_NOT_REACHABLE ||
+               req->cf_type == CALL_FORWARDING_TYPE_ALL ||
+               req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+               property_append_cf_conditions(&dict,
+                       req->cf_list[CALL_FORWARDING_TYPE_NOT_REACHABLE],
+                       req->cls,
+                       cf_type_lut[CALL_FORWARDING_TYPE_NOT_REACHABLE]);
+
+       if (req->cf_type == CALL_FORWARDING_TYPE_BUSY ||
+               req->cf_type == CALL_FORWARDING_TYPE_ALL ||
+               req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+               property_append_cf_conditions(&dict,
+                       req->cf_list[CALL_FORWARDING_TYPE_BUSY],
+                       req->cls, cf_type_lut[CALL_FORWARDING_TYPE_BUSY]);
+
+       dbus_message_iter_close_container(&vstruct, &dict);
+
+       dbus_message_iter_close_container(&variant, &vstruct);
+
+       dbus_message_iter_close_container(&iter, &variant);
+
+       return reply;
+}
+
+static void ss_set_query_cf_callback(const struct ofono_error *error, int total,
+                       const struct ofono_call_forwarding_condition *list,
+                       void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+       GSList *l;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Setting succeeded, but query failed");
+               cf->flags &= ~CALL_FORWARDING_FLAG_CACHED;
+               reply = __ofono_error_failed(cf->pending);
+               __ofono_dbus_pending_reply(&cf->pending, reply);
+               return;
+       }
+
+       l = cf_cond_list_create(total, list);
+       DBG("%s conditions:", cf_type_lut[cf->query_next]);
+       cf_cond_list_print(l);
+
+       cf->ss_req->cf_list[cf->query_next] = l;
+
+       if (cf->query_next == cf->query_end) {
+               reply = cf_ss_control_reply(cf, cf->ss_req);
+               __ofono_dbus_pending_reply(&cf->pending, reply);
+               g_free(cf->ss_req);
+               cf->ss_req = NULL;
+       }
+
+       set_new_cond_list(cf, cf->query_next, l);
+
+       if (cf->query_next != cf->query_end) {
+               cf->query_next++;
+               ss_set_query_next_cf_cond(cf);
+       }
+}
+
+static void ss_set_query_next_cf_cond(struct ofono_call_forwarding *cf)
+{
+       cf->driver->query(cf, cf->query_next, BEARER_CLASS_DEFAULT,
+                       ss_set_query_cf_callback, cf);
+}
+
+static void cf_ss_control_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error occurred during cf ss control set/erasure");
+
+               __ofono_dbus_pending_reply(&cf->pending,
+                                       __ofono_error_failed(cf->pending));
+               g_free(cf->ss_req);
+               cf->ss_req = NULL;
+               return;
+       }
+
+       ss_set_query_next_cf_cond(cf);
+}
+
+static gboolean cf_ss_control(int type, const char *sc,
+                               const char *sia, const char *sib,
+                               const char *sic, const char *dn,
+                               DBusMessage *msg, void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       int cls = BEARER_CLASS_SS_DEFAULT;
+       int timeout = DEFAULT_NO_REPLY_TIMEOUT;
+       int cf_type;
+       DBusMessage *reply;
+       struct ofono_phone_number ph;
+       void *operation = NULL;
+
+       /* Before we do anything, make sure we're actually initialized */
+       if (cf == NULL)
+               return FALSE;
+
+       if (__ofono_call_forwarding_is_busy(cf)) {
+               reply = __ofono_error_busy(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       DBG("Received call forwarding ss control request");
+
+       DBG("type: %d, sc: %s, sia: %s, sib: %s, sic: %s, dn: %s",
+               type, sc, sia, sib, sic, dn);
+
+       if (!strcmp(sc, "21"))
+               cf_type = CALL_FORWARDING_TYPE_UNCONDITIONAL;
+       else if (!strcmp(sc, "67"))
+               cf_type = CALL_FORWARDING_TYPE_BUSY;
+       else if (!strcmp(sc, "61"))
+               cf_type = CALL_FORWARDING_TYPE_NO_REPLY;
+       else if (!strcmp(sc, "62"))
+               cf_type = CALL_FORWARDING_TYPE_NOT_REACHABLE;
+       else if (!strcmp(sc, "002"))
+               cf_type = CALL_FORWARDING_TYPE_ALL;
+       else if (!strcmp(sc, "004"))
+               cf_type = CALL_FORWARDING_TYPE_ALL_CONDITIONAL;
+       else
+               return FALSE;
+
+       if (strlen(sia) &&
+               (type == SS_CONTROL_TYPE_QUERY ||
+               type == SS_CONTROL_TYPE_ERASURE ||
+               type == SS_CONTROL_TYPE_DEACTIVATION))
+               goto error;
+
+       /*
+        * Activation / Registration is figured context specific according to
+        * 22.030 Section 6.5.2 "The UE shall determine from the context
+        * whether, an entry of a single *, activation or registration
+        * was intended."
+        */
+       if (type == SS_CONTROL_TYPE_ACTIVATION && strlen(sia) > 0)
+               type = SS_CONTROL_TYPE_REGISTRATION;
+
+       if (type == SS_CONTROL_TYPE_REGISTRATION &&
+               !valid_phone_number_format(sia))
+               goto error;
+
+       if (strlen(sib) > 0) {
+               long service_code;
+               char *end;
+
+               service_code = strtoul(sib, &end, 10);
+
+               if (end == sib || *end != '\0')
+                       goto error;
+
+               cls = mmi_service_code_to_bearer_class(service_code);
+
+               if (cls == 0)
+                       goto error;
+       }
+
+       if (strlen(sic) > 0) {
+               char *end;
+
+               if  (type != SS_CONTROL_TYPE_REGISTRATION)
+                       goto error;
+
+               if (cf_type != CALL_FORWARDING_TYPE_ALL &&
+                       cf_type != CALL_FORWARDING_TYPE_ALL_CONDITIONAL &&
+                       cf_type != CALL_FORWARDING_TYPE_NO_REPLY)
+                       goto error;
+
+               timeout = strtoul(sic, &end, 10);
+
+               if (end == sic || *end != '\0')
+                       goto error;
+
+               if (timeout < 1 || timeout > 30)
+                       goto error;
+       }
+
+       switch (type) {
+       case SS_CONTROL_TYPE_REGISTRATION:
+               operation = cf->driver->registration;
+               break;
+       case SS_CONTROL_TYPE_ACTIVATION:
+               operation = cf->driver->activation;
+               break;
+       case SS_CONTROL_TYPE_DEACTIVATION:
+               operation = cf->driver->deactivation;
+               break;
+       case SS_CONTROL_TYPE_ERASURE:
+               operation = cf->driver->erasure;
+               break;
+       case SS_CONTROL_TYPE_QUERY:
+               operation = cf->driver->query;
+               break;
+       }
+
+       if (operation == NULL) {
+               reply = __ofono_error_not_implemented(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       cf->ss_req = g_try_new0(struct cf_ss_request, 1);
+
+       if (cf->ss_req == NULL) {
+               reply = __ofono_error_failed(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       cf->ss_req->ss_type = type;
+       cf->ss_req->cf_type = cf_type;
+       cf->ss_req->cls = cls;
+
+       cf->pending = dbus_message_ref(msg);
+
+       switch (cf->ss_req->cf_type) {
+       case CALL_FORWARDING_TYPE_ALL:
+               cf->query_next = CALL_FORWARDING_TYPE_UNCONDITIONAL;
+               cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE;
+               break;
+       case CALL_FORWARDING_TYPE_ALL_CONDITIONAL:
+               cf->query_next = CALL_FORWARDING_TYPE_BUSY;
+               cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE;
+               break;
+       default:
+               cf->query_next = cf->ss_req->cf_type;
+               cf->query_end = cf->ss_req->cf_type;
+               break;
+       }
+
+       /*
+        * Some modems don't understand all classes very well, particularly
+        * the older models.  So if the bearer class is the default, we
+        * just use the more commonly understood value of 7 since BEARER_SMS
+        * is not applicable to CallForwarding conditions according to 22.004
+        * Annex A
+        */
+       if (cls == BEARER_CLASS_SS_DEFAULT)
+               cls = BEARER_CLASS_DEFAULT;
+
+       switch (cf->ss_req->ss_type) {
+       case SS_CONTROL_TYPE_REGISTRATION:
+               string_to_phone_number(sia, &ph);
+               cf->driver->registration(cf, cf_type, cls, &ph, timeout,
+                                       cf_ss_control_callback, cf);
+               break;
+       case SS_CONTROL_TYPE_ACTIVATION:
+               cf->driver->activation(cf, cf_type, cls, cf_ss_control_callback,
+                                       cf);
+               break;
+       case SS_CONTROL_TYPE_DEACTIVATION:
+               cf->driver->deactivation(cf, cf_type, cls,
+                                       cf_ss_control_callback, cf);
+               break;
+       case SS_CONTROL_TYPE_ERASURE:
+               cf->driver->erasure(cf, cf_type, cls, cf_ss_control_callback,
+                                       cf);
+               break;
+       case SS_CONTROL_TYPE_QUERY:
+               ss_set_query_next_cf_cond(cf);
+               break;
+       }
+
+       return TRUE;
+
+error:
+       reply = __ofono_error_invalid_format(msg);
+       g_dbus_send_message(conn, reply);
+       return TRUE;
+}
+
+static void cf_register_ss_controls(struct ofono_call_forwarding *cf)
+{
+       __ofono_ussd_ssc_register(cf->ussd, "21", cf_ss_control, cf, NULL);
+       __ofono_ussd_ssc_register(cf->ussd, "67", cf_ss_control, cf, NULL);
+       __ofono_ussd_ssc_register(cf->ussd, "61", cf_ss_control, cf, NULL);
+       __ofono_ussd_ssc_register(cf->ussd, "62", cf_ss_control, cf, NULL);
+
+       __ofono_ussd_ssc_register(cf->ussd, "002", cf_ss_control, cf, NULL);
+       __ofono_ussd_ssc_register(cf->ussd, "004", cf_ss_control, cf, NULL);
+}
+
+static void cf_unregister_ss_controls(struct ofono_call_forwarding *cf)
+{
+       __ofono_ussd_ssc_unregister(cf->ussd, "21");
+       __ofono_ussd_ssc_unregister(cf->ussd, "67");
+       __ofono_ussd_ssc_unregister(cf->ussd, "61");
+       __ofono_ussd_ssc_unregister(cf->ussd, "62");
+
+       __ofono_ussd_ssc_unregister(cf->ussd, "002");
+       __ofono_ussd_ssc_unregister(cf->ussd, "004");
+}
+
+gboolean __ofono_call_forwarding_is_busy(struct ofono_call_forwarding *cf)
+{
+       return cf->pending ? TRUE : FALSE;
+}
+
+static void sim_cfis_read_cb(int ok, int total_length, int record,
+                       const unsigned char *data,
+                       int record_length, void *userdata)
+{
+       struct ofono_call_forwarding *cf = userdata;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cf->atom);
+
+       if (!ok || record_length < 16 || total_length < record_length) {
+               cf->cfis_record_id = 0;
+               return;
+       }
+
+       /*
+        * Multiple Subscriber Profile number which can have values 1-4.
+        * Profile id 1 is assumed as the current profile.
+        */
+       if (data[0] != 1)
+               return;
+
+       cf->cfis_record_id = record;
+
+       if (cf->flags & CALL_FORWARDING_FLAG_CACHED)
+               return;
+
+       /*
+        * For now we only support Voice, although Fax & all Data
+        * basic services are applicable as well.
+        */
+       if (data[1] & 0x01) {
+               int ton_npi;
+               int number_len;
+               const char *number;
+               char attr[64];
+               struct ofono_call_forwarding_condition *cond;
+               dbus_bool_t status;
+
+               number_len = data[2];
+               ton_npi = data[3];
+
+               if (number_len > 11 || ton_npi == 0xff)
+                       return;
+
+               cond = g_try_new0(struct ofono_call_forwarding_condition, 1);
+               if (cond == NULL)
+                       return;
+
+               status = TRUE;
+               cond->status = TRUE;
+               cond->cls = BEARER_CLASS_VOICE;
+               cond->time = 0;
+               cond->phone_number.type = ton_npi;
+
+               sim_extract_bcd_number(data + 4, number_len - 1,
+                                       cond->phone_number.number);
+               number = phone_number_to_string(&cond->phone_number);
+
+               snprintf(attr, sizeof(attr), "%s%s",
+                       bearer_class_to_string(BEARER_CLASS_VOICE),
+                       cf_type_lut[CALL_FORWARDING_TYPE_UNCONDITIONAL]);
+
+               cf->cf_conditions[CALL_FORWARDING_TYPE_UNCONDITIONAL] =
+                                               g_slist_append(NULL, cond);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CALL_FORWARDING_INTERFACE,
+                                       attr, DBUS_TYPE_STRING, &number);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CALL_FORWARDING_INTERFACE,
+                                       "ForwardingFlagOnSim",
+                                       DBUS_TYPE_BOOLEAN, &status);
+       }
+}
+
+static void sim_cphs_cff_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_call_forwarding *cf = userdata;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cf->atom);
+       dbus_bool_t cfu_voice;
+
+       if (!ok || total_length < 1)
+               return;
+
+       cf->flags |= CALL_FORWARDING_FLAG_CPHS_CFF;
+
+       if (cf->flags & CALL_FORWARDING_FLAG_CACHED)
+               return;
+
+       /*
+        * For now we only support Voice, although Fax & all Data
+        * basic services are applicable as well.
+        */
+       if ((data[0] & 0xf) != 0xA)
+               return;
+
+       cfu_voice = TRUE;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CALL_FORWARDING_INTERFACE,
+                                       "ForwardingFlagOnSim",
+                                       DBUS_TYPE_BOOLEAN, &cfu_voice);
+}
+
+static void call_forwarding_unregister(struct ofono_atom *atom)
+{
+       struct ofono_call_forwarding *cf = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(cf->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_CALL_FORWARDING_INTERFACE);
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_CALL_FORWARDING_INTERFACE);
+
+       if (cf->sim_context) {
+               ofono_sim_context_free(cf->sim_context);
+               cf->sim_context = NULL;
+       }
+
+       if (cf->ussd)
+               cf_unregister_ss_controls(cf);
+
+       if (cf->ussd_watch)
+               __ofono_modem_remove_atom_watch(modem, cf->ussd_watch);
+
+       cf->flags = 0;
+}
+
+static void sim_cfis_changed(int id, void *userdata)
+{
+       struct ofono_call_forwarding *cf = userdata;
+
+       if (!(cf->flags & CALL_FORWARDING_FLAG_CACHED))
+               return;
+
+       /*
+        * If the values are cached it's because at least one client
+        * requested them and we need to notify them about this
+        * change.  However the authoritative source of current
+        * Call-Forwarding settings is the network operator and the
+        * query can take a noticeable amount of time.  Instead of
+        * sending PropertyChanged, we reregister the Call Forwarding
+        * atom.  The client will invoke GetProperties only if it
+        * is still interested.
+        */
+       call_forwarding_unregister(cf->atom);
+       ofono_call_forwarding_register(cf);
+}
+
+static void sim_read_cf_indicator(struct ofono_call_forwarding *cf)
+{
+       if (__ofono_sim_service_available(cf->sim,
+                       SIM_UST_SERVICE_CFIS,
+                       SIM_SST_SERVICE_CFIS) == TRUE) {
+               ofono_sim_read(cf->sim_context, SIM_EFCFIS_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               sim_cfis_read_cb, cf);
+               ofono_sim_add_file_watch(cf->sim_context, SIM_EFCFIS_FILEID,
+                                               sim_cfis_changed, cf, NULL);
+       } else {
+               ofono_sim_read(cf->sim_context, SIM_EF_CPHS_CFF_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               sim_cphs_cff_read_cb, cf);
+               ofono_sim_add_file_watch(cf->sim_context,
+                                               SIM_EF_CPHS_CFF_FILEID,
+                                               sim_cfis_changed, cf, NULL);
+       }
+}
+
+int ofono_call_forwarding_driver_register(const struct ofono_call_forwarding_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_call_forwarding_driver_unregister(const struct ofono_call_forwarding_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void call_forwarding_remove(struct ofono_atom *atom)
+{
+       struct ofono_call_forwarding *cf = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cf == NULL)
+               return;
+
+       if (cf->driver && cf->driver->remove)
+               cf->driver->remove(cf);
+
+       cf_clear_all(cf);
+
+       g_free(cf);
+}
+
+struct ofono_call_forwarding *ofono_call_forwarding_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data)
+{
+       struct ofono_call_forwarding *cf;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cf = g_try_new0(struct ofono_call_forwarding, 1);
+
+       if (cf == NULL)
+               return NULL;
+
+       cf->atom = __ofono_modem_add_atom(modem,
+                                               OFONO_ATOM_TYPE_CALL_FORWARDING,
+                                               call_forwarding_remove, cf);
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_call_forwarding_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cf, vendor, data) < 0)
+                       continue;
+
+               cf->driver = drv;
+               break;
+       }
+
+       return cf;
+}
+
+static void ussd_watch(struct ofono_atom *atom,
+                       enum ofono_atom_watch_condition cond, void *data)
+{
+       struct ofono_call_forwarding *cf = data;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               cf->ussd = NULL;
+               return;
+       }
+
+       cf->ussd = __ofono_atom_get_data(atom);
+       cf_register_ss_controls(cf);
+}
+
+void ofono_call_forwarding_register(struct ofono_call_forwarding *cf)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cf->atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CALL_FORWARDING_INTERFACE,
+                                       cf_methods, cf_signals, NULL, cf,
+                                       NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CALL_FORWARDING_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_CALL_FORWARDING_INTERFACE);
+
+       cf->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+       if (cf->sim) {
+               cf->sim_context = ofono_sim_context_create(cf->sim);
+               sim_read_cf_indicator(cf);
+       }
+
+       cf->ussd_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_USSD,
+                                       ussd_watch, cf, NULL);
+
+       __ofono_atom_register(cf->atom, call_forwarding_unregister);
+}
+
+void ofono_call_forwarding_remove(struct ofono_call_forwarding *cf)
+{
+       __ofono_atom_free(cf->atom);
+}
+
+void ofono_call_forwarding_set_data(struct ofono_call_forwarding *cf, void *data)
+{
+       cf->driver_data = data;
+}
+
+void *ofono_call_forwarding_get_data(struct ofono_call_forwarding *cf)
+{
+       return cf->driver_data;
+}
diff --git a/src/call-meter.c b/src/call-meter.c
new file mode 100644 (file)
index 0000000..c3ae6f7
--- /dev/null
@@ -0,0 +1,793 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+
+#define CALL_METER_FLAG_CACHED 0x1
+#define CALL_METER_FLAG_HAVE_PUCT 0x2
+
+static GSList *g_drivers = NULL;
+
+struct ofono_call_meter {
+       int flags;
+       DBusMessage *pending;
+       int call_meter;
+       int acm;
+       int acm_max;
+       double ppu;
+       char currency[4];
+       const struct ofono_call_meter_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static void set_call_meter(struct ofono_call_meter *cm, int value)
+{
+       DBusConnection *conn;
+       const char *path;
+
+       if (cm->call_meter == value)
+               return;
+
+       cm->call_meter = value;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cm->atom);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_METER_INTERFACE,
+                                               "CallMeter", DBUS_TYPE_UINT32,
+                                               &cm->call_meter);
+}
+
+static void set_acm(struct ofono_call_meter *cm, int value)
+{
+       DBusConnection *conn;
+       const char *path;
+
+       if (cm->acm == value)
+               return;
+
+       cm->acm = value;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cm->atom);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_METER_INTERFACE,
+                                               "AccumulatedCallMeter",
+                                               DBUS_TYPE_UINT32, &cm->acm);
+}
+
+static void set_acm_max(struct ofono_call_meter *cm, int value)
+{
+       DBusConnection *conn;
+       const char *path;
+
+       if (cm->acm_max == value)
+               return;
+
+       cm->acm_max = value;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cm->atom);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_METER_INTERFACE,
+                                               "AccumulatedCallMeterMaximum",
+                                               DBUS_TYPE_UINT32, &cm->acm_max);
+}
+
+static void set_ppu(struct ofono_call_meter *cm, double value)
+{
+       DBusConnection *conn;
+       const char *path;
+
+       if (cm->ppu == value)
+               return;
+
+       cm->ppu = value;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cm->atom);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_METER_INTERFACE,
+                                               "PricePerUnit",
+                                               DBUS_TYPE_DOUBLE, &cm->ppu);
+}
+
+static void set_currency(struct ofono_call_meter *cm, const char *value)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *dbusval;
+
+       if (strlen(value) > 3) {
+               ofono_error("Currency reported with size > 3: %s", value);
+               return;
+       }
+
+       if (!strcmp(cm->currency, value))
+               return;
+
+       strncpy(cm->currency, value, 3);
+       cm->currency[3] = '\0';
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cm->atom);
+       dbusval = cm->currency;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_METER_INTERFACE,
+                                               "Currency", DBUS_TYPE_STRING,
+                                               &dbusval);
+}
+
+static void cm_get_properties_reply(struct ofono_call_meter *cm)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, dict;
+       const char *currency = cm->currency;
+
+       reply = dbus_message_new_method_return(cm->pending);
+       if (reply == NULL)
+               return;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "CallMeter", DBUS_TYPE_UINT32,
+                               &cm->call_meter);
+
+       ofono_dbus_dict_append(&dict, "AccumulatedCallMeter", DBUS_TYPE_UINT32,
+                               &cm->acm);
+
+       ofono_dbus_dict_append(&dict, "AccumulatedCallMeterMaximum",
+                               DBUS_TYPE_UINT32, &cm->acm_max);
+
+       ofono_dbus_dict_append(&dict, "PricePerUnit", DBUS_TYPE_DOUBLE,
+                               &cm->ppu);
+
+       ofono_dbus_dict_append(&dict, "Currency", DBUS_TYPE_STRING, &currency);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       __ofono_dbus_pending_reply(&cm->pending, reply);
+}
+
+static void query_call_meter_callback(const struct ofono_error *error,
+                                       int value, void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_call_meter(cm, value);
+
+       if (cm->pending)
+               cm_get_properties_reply(cm);
+}
+
+static void query_call_meter(struct ofono_call_meter *cm)
+{
+       if (cm->driver->call_meter_query == NULL) {
+               if (cm->pending)
+                       cm_get_properties_reply(cm);
+
+               return;
+       }
+
+       cm->driver->call_meter_query(cm, query_call_meter_callback, cm);
+}
+
+static void query_acm_callback(const struct ofono_error *error, int value,
+                                       void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_acm(cm, value);
+
+       query_call_meter(cm);
+}
+
+static void query_acm(struct ofono_call_meter *cm)
+{
+       if (cm->driver->acm_query == NULL) {
+               query_call_meter(cm);
+               return;
+       }
+
+       cm->driver->acm_query(cm, query_acm_callback, cm);
+}
+
+static void query_acm_max_callback(const struct ofono_error *error, int value,
+                                       void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_acm_max(cm, value);
+
+       cm->flags |= CALL_METER_FLAG_CACHED;
+
+       query_acm(cm);
+}
+
+static void query_acm_max(struct ofono_call_meter *cm)
+{
+       if (cm->driver->acm_max_query == NULL) {
+               cm->flags |= CALL_METER_FLAG_CACHED;
+
+               query_acm(cm);
+               return;
+       }
+
+       cm->driver->acm_max_query(cm, query_acm_max_callback, cm);
+}
+
+static void query_puct_callback(const struct ofono_error *error,
+                               const char *currency, double ppu, void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+               cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
+               set_currency(cm, currency);
+               set_ppu(cm, ppu);
+       }
+
+       query_acm_max(cm);
+}
+
+static void query_puct(struct ofono_call_meter *cm)
+{
+       if (cm->driver->puct_query == NULL)
+               query_acm_max(cm);
+       else
+               cm->driver->puct_query(cm, query_puct_callback, cm);
+}
+
+static DBusMessage *cm_get_properties(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (cm->pending)
+               return __ofono_error_busy(msg);
+
+       cm->pending = dbus_message_ref(msg);
+
+       /*
+        * We don't need to query ppu, currency & acm_max every time
+        * Not sure if we have to query acm & call_meter every time
+        * so lets play on the safe side and query them.  They should be
+        * fast to query anyway
+        */
+       if (cm->flags & CALL_METER_FLAG_CACHED)
+               query_acm(cm);
+       else
+               query_puct(cm);
+
+       return NULL;
+}
+
+static void set_acm_max_query_callback(const struct ofono_error *error,
+                                       int value, void *data)
+{
+       struct ofono_call_meter *cm = data;
+       DBusMessage *reply;
+
+       if (cm->pending == NULL)
+               return;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Setting acm_max successful, but query was not");
+
+               cm->flags &= ~CALL_METER_FLAG_CACHED;
+
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               return;
+       }
+
+       reply = dbus_message_new_method_return(cm->pending);
+       __ofono_dbus_pending_reply(&cm->pending, reply);
+
+       set_acm_max(cm, value);
+}
+
+static void check_pin2_state(struct ofono_call_meter *cm)
+{
+       struct ofono_atom *sim_atom;
+
+       sim_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(cm->atom),
+                                               OFONO_ATOM_TYPE_SIM);
+       if (sim_atom == NULL)
+               return;
+
+       __ofono_sim_recheck_pin(__ofono_atom_get_data(sim_atom));
+}
+
+static void set_acm_max_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Setting acm_max failed");
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               check_pin2_state(cm);
+               return;
+       }
+
+       /* Assume if we have acm_reset, we have acm_query */
+       cm->driver->acm_max_query(cm, set_acm_max_query_callback, cm);
+}
+
+static DBusMessage *prop_set_acm_max(DBusMessage *msg,
+                                       struct ofono_call_meter *cm,
+                                       DBusMessageIter *dbus_value,
+                                       const char *pin2)
+{
+       dbus_uint32_t value;
+
+       if (cm->driver->acm_max_set == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       dbus_message_iter_get_basic(dbus_value, &value);
+
+       cm->pending = dbus_message_ref(msg);
+
+       cm->driver->acm_max_set(cm, value, pin2, set_acm_max_callback, cm);
+
+       return NULL;
+}
+
+static void set_puct_query_callback(const struct ofono_error *error,
+                                       const char *currency, double ppu,
+                                       void *data)
+{
+       struct ofono_call_meter *cm = data;
+       DBusMessage *reply;
+
+       if (cm->pending == NULL)
+               return;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Setting PUCT successful, but query was not");
+
+               cm->flags &= ~CALL_METER_FLAG_CACHED;
+
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               return;
+       }
+
+       reply = dbus_message_new_method_return(cm->pending);
+       __ofono_dbus_pending_reply(&cm->pending, reply);
+
+       set_currency(cm, currency);
+       set_ppu(cm, ppu);
+}
+
+static void set_puct_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("setting puct failed");
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               check_pin2_state(cm);
+               return;
+       }
+
+       /* Assume if we have puct_set, we have puct_query */
+       cm->driver->puct_query(cm, set_puct_query_callback, cm);
+}
+
+/*
+ * This function is for the really bizarre case of someone trying to call
+ * SetProperty before GetProperties.  But we must handle it...
+ */
+static void set_puct_initial_query_callback(const struct ofono_error *error,
+                                               const char *currency,
+                                               double ppu, void *data)
+{
+       struct ofono_call_meter *cm = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *name;
+       const char *pin2;
+
+       if (cm->pending == NULL)
+               return;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               return;
+       }
+
+       set_currency(cm, currency);
+       set_ppu(cm, ppu);
+
+       cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
+
+       dbus_message_iter_init(cm->pending, &iter);
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &var);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_get_basic(&iter, &pin2);
+
+       if (!strcmp(name, "PricePerUnit"))
+               dbus_message_iter_get_basic(&var, &ppu);
+       else
+               dbus_message_iter_get_basic(&var, &currency);
+
+       cm->driver->puct_set(cm, currency, ppu, pin2,
+                               set_puct_callback, cm);
+}
+
+static DBusMessage *prop_set_ppu(DBusMessage *msg, struct ofono_call_meter *cm,
+                               DBusMessageIter *var, const char *pin2)
+{
+       double ppu;
+
+       if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       dbus_message_iter_get_basic(var, &ppu);
+
+       if (ppu < 0.0)
+               return __ofono_error_invalid_format(msg);
+
+       cm->pending = dbus_message_ref(msg);
+
+       if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
+               cm->driver->puct_set(cm, cm->currency, ppu, pin2,
+                                       set_puct_callback, cm);
+       else
+               cm->driver->puct_query(cm, set_puct_initial_query_callback, cm);
+
+       return NULL;
+}
+
+static DBusMessage *prop_set_cur(DBusMessage *msg, struct ofono_call_meter *cm,
+                               DBusMessageIter *var, const char *pin2)
+{
+       const char *value;
+
+       if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       dbus_message_iter_get_basic(var, &value);
+
+       if (strlen(value) > 3)
+               return __ofono_error_invalid_format(msg);
+
+       cm->pending = dbus_message_ref(msg);
+
+       if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
+               cm->driver->puct_set(cm, value, cm->ppu, pin2,
+                                       set_puct_callback, cm);
+       else
+               cm->driver->puct_query(cm, set_puct_initial_query_callback, cm);
+
+       return NULL;
+}
+
+struct call_meter_property {
+       const char *name;
+       int type;
+       DBusMessage* (*set)(DBusMessage *msg, struct ofono_call_meter *cm,
+                               DBusMessageIter *var, const char *pin2);
+};
+
+static struct call_meter_property cm_properties[] = {
+       { "AccumulatedCallMeterMaximum",DBUS_TYPE_UINT32,       prop_set_acm_max },
+       { "PricePerUnit",               DBUS_TYPE_DOUBLE,       prop_set_ppu },
+       { "Currency",                   DBUS_TYPE_STRING,       prop_set_cur },
+       { NULL, 0, 0 },
+};
+
+static DBusMessage *cm_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_meter *cm = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *name, *passwd = "";
+       struct call_meter_property *property;
+
+       if (cm->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (!dbus_message_iter_next(&iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &passwd);
+
+       if (!__ofono_is_valid_sim_pin(passwd, OFONO_SIM_PASSWORD_SIM_PIN2))
+               return __ofono_error_invalid_format(msg);
+
+       for (property = cm_properties; property->name; property++) {
+               if (strcmp(name, property->name))
+                       continue;
+
+               if (dbus_message_iter_get_arg_type(&var) != property->type)
+                       return __ofono_error_invalid_args(msg);
+
+               return property->set(msg, cm, &var, passwd);
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static void reset_acm_query_callback(const struct ofono_error *error, int value,
+                                       void *data)
+{
+       struct ofono_call_meter *cm = data;
+       DBusMessage *reply;
+
+       if (cm->pending == NULL)
+               return;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Reseting ACM successful, but query was not");
+
+               cm->flags &= ~CALL_METER_FLAG_CACHED;
+
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               return;
+       }
+
+       reply = dbus_message_new_method_return(cm->pending);
+       __ofono_dbus_pending_reply(&cm->pending, reply);
+
+       set_acm(cm, value);
+}
+
+static void acm_reset_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_meter *cm = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("reseting acm failed");
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               check_pin2_state(cm);
+               return;
+       }
+
+       /* Assume if we have acm_reset, we have acm_query */
+       cm->driver->acm_query(cm, reset_acm_query_callback, cm);
+}
+
+static DBusMessage *cm_acm_reset(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_meter *cm = data;
+       const char *pin2;
+
+       if (cm->driver->acm_reset == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (cm->pending)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pin2,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_is_valid_sim_pin(pin2, OFONO_SIM_PASSWORD_SIM_PIN2))
+               return __ofono_error_invalid_format(msg);
+
+       cm->pending = dbus_message_ref(msg);
+
+       cm->driver->acm_reset(cm, pin2, acm_reset_callback, cm);
+
+       return NULL;
+}
+
+static GDBusMethodTable cm_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        cm_get_properties,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "SetProperty",        "svs",  "",             cm_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "Reset",              "s",    "",             cm_acm_reset,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable cm_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { "NearMaximumWarning", "" },
+       { }
+};
+
+void ofono_call_meter_changed_notify(struct ofono_call_meter *cm, int new_value)
+{
+       set_call_meter(cm, new_value);
+}
+
+void ofono_call_meter_maximum_notify(struct ofono_call_meter *cm)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cm->atom);
+
+       g_dbus_emit_signal(conn, path, OFONO_CALL_METER_INTERFACE,
+                       "NearMaximumWarning", DBUS_TYPE_INVALID);
+}
+
+int ofono_call_meter_driver_register(const struct ofono_call_meter_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_call_meter_driver_unregister(const struct ofono_call_meter_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void call_meter_unregister(struct ofono_atom *atom)
+{
+       struct ofono_call_meter *cm = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(cm->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_CALL_METER_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_CALL_METER_INTERFACE);
+}
+
+static void call_meter_remove(struct ofono_atom *atom)
+{
+       struct ofono_call_meter *cm = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cm == NULL)
+               return;
+
+       if (cm->driver && cm->driver->remove)
+               cm->driver->remove(cm);
+
+       g_free(cm);
+}
+
+struct ofono_call_meter *ofono_call_meter_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data)
+{
+       struct ofono_call_meter *cm;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cm = g_try_new0(struct ofono_call_meter, 1);
+
+       if (cm == NULL)
+               return NULL;
+
+       cm->atom = __ofono_modem_add_atom(modem,
+                                               OFONO_ATOM_TYPE_CALL_METER,
+                                               call_meter_remove, cm);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_call_meter_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cm, vendor, data) < 0)
+                       continue;
+
+               cm->driver = drv;
+               break;
+       }
+
+       return cm;
+}
+
+void ofono_call_meter_register(struct ofono_call_meter *cm)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cm->atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
+
+       if (!g_dbus_register_interface(conn, path, OFONO_CALL_METER_INTERFACE,
+                                       cm_methods, cm_signals, NULL, cm,
+                                       NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CALL_METER_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_CALL_METER_INTERFACE);
+
+       __ofono_atom_register(cm->atom, call_meter_unregister);
+}
+
+void ofono_call_meter_remove(struct ofono_call_meter *cm)
+{
+       __ofono_atom_free(cm->atom);
+}
+
+void ofono_call_meter_set_data(struct ofono_call_meter *cm, void *data)
+{
+       cm->driver_data = data;
+}
+
+void *ofono_call_meter_get_data(struct ofono_call_meter *cm)
+{
+       return cm->driver_data;
+}
diff --git a/src/call-settings.c b/src/call-settings.c
new file mode 100644 (file)
index 0000000..94e606c
--- /dev/null
@@ -0,0 +1,1490 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+
+#define CALL_SETTINGS_FLAG_CACHED 0x1
+
+static GSList *g_drivers = NULL;
+
+/* 27.007 Section 7.7 */
+enum clir_status {
+       CLIR_STATUS_NOT_PROVISIONED =           0,
+       CLIR_STATUS_PROVISIONED_PERMANENT =     1,
+       CLIR_STATUS_UNKNOWN =                   2,
+       CLIR_STATUS_TEMPORARY_RESTRICTED =      3,
+       CLIR_STATUS_TEMPORARY_ALLOWED =         4
+};
+
+/* 27.007 Section 7.6 */
+enum clip_status {
+       CLIP_STATUS_NOT_PROVISIONED =           0,
+       CLIP_STATUS_PROVISIONED =               1,
+       CLIP_STATUS_UNKNOWN =                   2
+};
+
+/* 27.007 Section 7.30 */
+enum cnap_status {
+       CNAP_STATUS_NOT_PROVISIONED =           0,
+       CNAP_STATUS_PROVISIONED =               1,
+       CNAP_STATUS_UNKNOWN =                   2
+};
+
+/* 27.007 Section 7.8 */
+enum colp_status {
+       COLP_STATUS_NOT_PROVISIONED =           0,
+       COLP_STATUS_PROVISIONED =               1,
+       COLP_STATUS_UNKNOWN =                   2
+};
+
+/* 27.007 Section 7.9 */
+enum cdip_status {
+       CDIP_STATUS_NOT_PROVISIONED =           0,
+       CDIP_STATUS_PROVISIONED =               1,
+       CDIP_STATUS_UNKNOWN =                   2
+};
+
+/* This is not defined in 27.007, but presumably the same as CLIP/COLP */
+enum colr_status {
+       COLR_STATUS_NOT_PROVISIONED =           0,
+       COLR_STATUS_PROVISIONED =               1,
+       COLR_STATUS_UNKNOWN =                   2
+};
+
+enum call_setting_type {
+       CALL_SETTING_TYPE_CLIP = 0,
+       CALL_SETTING_TYPE_CNAP,
+       CALL_SETTING_TYPE_CDIP,
+       CALL_SETTING_TYPE_COLP,
+       CALL_SETTING_TYPE_COLR,
+       CALL_SETTING_TYPE_CLIR,
+       CALL_SETTING_TYPE_CW
+};
+
+struct ofono_call_settings {
+       int clir;
+       int colr;
+       int clip;
+       int cnap;
+       int cdip;
+       int colp;
+       int clir_setting;
+       int cw;
+       int flags;
+       DBusMessage *pending;
+       int ss_req_type;
+       int ss_req_cls;
+       enum call_setting_type ss_setting;
+       struct ofono_ussd *ussd;
+       unsigned int ussd_watch;
+       const struct ofono_call_settings_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static const char *clip_status_to_string(int status)
+{
+       switch (status) {
+       case CLIP_STATUS_NOT_PROVISIONED:
+               return "disabled";
+       case CLIP_STATUS_PROVISIONED:
+               return "enabled";
+       }
+
+       return "unknown";
+}
+
+static const char *cdip_status_to_string(int status)
+{
+       switch (status) {
+       case CDIP_STATUS_NOT_PROVISIONED:
+               return "disabled";
+       case CDIP_STATUS_PROVISIONED:
+               return "enabled";
+       }
+
+       return "unknown";
+}
+
+static const char *cnap_status_to_string(int status)
+{
+       switch (status) {
+       case CNAP_STATUS_NOT_PROVISIONED:
+               return "disabled";
+       case CNAP_STATUS_PROVISIONED:
+               return "enabled";
+       }
+
+       return "unknown";
+}
+
+static const char *colp_status_to_string(int status)
+{
+       switch (status) {
+       case COLP_STATUS_NOT_PROVISIONED:
+               return "disabled";
+       case COLP_STATUS_PROVISIONED:
+               return "enabled";
+       }
+
+       return "unknown";
+}
+
+static const char *colr_status_to_string(int status)
+{
+       switch (status) {
+       case COLR_STATUS_NOT_PROVISIONED:
+               return "disabled";
+       case COLR_STATUS_PROVISIONED:
+               return "enabled";
+       }
+
+       return "unknown";
+}
+
+static const char *hide_callerid_to_string(int status)
+{
+       switch (status) {
+       case OFONO_CLIR_OPTION_DEFAULT:
+               return "default";
+       case OFONO_CLIR_OPTION_INVOCATION:
+               return "enabled";
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               return "disabled";
+       }
+
+       return "default";
+}
+
+static const char *clir_status_to_string(int status)
+{
+       switch (status) {
+       case CLIR_STATUS_NOT_PROVISIONED:
+               return "disabled";
+       case CLIR_STATUS_PROVISIONED_PERMANENT:
+               return "permanent";
+       case CLIR_STATUS_TEMPORARY_RESTRICTED:
+               return "on";
+       case CLIR_STATUS_TEMPORARY_ALLOWED:
+               return "off";
+       }
+
+       return "unknown";
+}
+
+static void set_clir_network(struct ofono_call_settings *cs, int clir)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *str;
+
+       if (cs->clir == clir)
+               return;
+
+       cs->clir = clir;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cs->atom);
+
+       str = clir_status_to_string(clir);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               "CallingLineRestriction",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_clir_override(struct ofono_call_settings *cs, int override)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *str;
+
+       if (cs->clir_setting == override)
+               return;
+
+       cs->clir_setting = override;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cs->atom);
+
+       str = hide_callerid_to_string(override);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               "HideCallerId",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_cdip(struct ofono_call_settings *cs, int cdip)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *str;
+
+       if (cs->cdip == cdip)
+               return;
+
+       cs->cdip = cdip;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cs->atom);
+
+       str = cdip_status_to_string(cdip);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               "CalledLinePresentation",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_clip(struct ofono_call_settings *cs, int clip)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *str;
+
+       if (cs->clip == clip)
+               return;
+
+       cs->clip = clip;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cs->atom);
+
+       str = clip_status_to_string(clip);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               "CallingLinePresentation",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_cnap(struct ofono_call_settings *cs, int cnap)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *str;
+
+       if (cs->cnap == cnap)
+               return;
+
+       cs->cnap = cnap;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cs->atom);
+
+       str = cnap_status_to_string(cnap);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               "CallingNamePresentation",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_colp(struct ofono_call_settings *cs, int colp)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *str;
+
+       if (cs->colp == colp)
+               return;
+
+       cs->colp = colp;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cs->atom);
+
+       str = colp_status_to_string(colp);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               "ConnectedLinePresentation",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_colr(struct ofono_call_settings *cs, int colr)
+{
+       DBusConnection *conn;
+       const char *path;
+       const char *str;
+
+       if (cs->colr == colr)
+               return;
+
+       cs->colr = colr;
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(cs->atom);
+
+       str = colr_status_to_string(colr);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               "ConnectedLineRestriction",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_cw(struct ofono_call_settings *cs, int new_cw, int mask)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cs->atom);
+       char buf[64];
+       int j;
+       const char *value;
+
+       for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) {
+               if ((j & mask) == 0)
+                       continue;
+
+               if ((cs->cw & j) == (new_cw & j))
+                       continue;
+
+               if (new_cw & j)
+                       value = "enabled";
+               else
+                       value = "disabled";
+
+               snprintf(buf, sizeof(buf), "%sCallWaiting",
+                               bearer_class_to_string(j));
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_SETTINGS_INTERFACE,
+                                               buf, DBUS_TYPE_STRING,
+                                               &value);
+       }
+
+       cs->cw = new_cw;
+}
+
+static void property_append_cw_conditions(DBusMessageIter *dict,
+                                               int conditions, int mask)
+{
+       int i;
+       char prop[128];
+       const char *value;
+
+       for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) {
+               if (!(mask & i))
+                       continue;
+
+               snprintf(prop, sizeof(prop), "%sCallWaiting",
+                               bearer_class_to_string(i));
+
+               if (conditions & i)
+                       value = "enabled";
+               else
+                       value = "disabled";
+
+               ofono_dbus_dict_append(dict, prop, DBUS_TYPE_STRING, &value);
+       }
+}
+
+static void generate_cw_ss_query_reply(struct ofono_call_settings *cs)
+{
+       const char *sig = "(sa{sv})";
+       const char *ss_type = ss_control_type_to_string(cs->ss_req_type);
+       const char *context = "CallWaiting";
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       DBusMessageIter vstruct;
+       DBusMessageIter dict;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(cs->pending);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var);
+
+       dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL,
+                                               &vstruct);
+
+       dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+                                       &ss_type);
+
+       dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       property_append_cw_conditions(&dict, cs->cw, cs->ss_req_cls);
+
+       dbus_message_iter_close_container(&vstruct, &dict);
+
+       dbus_message_iter_close_container(&var, &vstruct);
+
+       dbus_message_iter_close_container(&iter, &var);
+
+       __ofono_dbus_pending_reply(&cs->pending, reply);
+}
+
+static void cw_ss_query_callback(const struct ofono_error *error, int status,
+                                       void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("setting CW via SS failed");
+
+               cs->flags &= ~CALL_SETTINGS_FLAG_CACHED;
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+
+               return;
+       }
+
+       set_cw(cs, status, BEARER_CLASS_VOICE);
+
+       generate_cw_ss_query_reply(cs);
+}
+
+static void cw_ss_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("setting CW via SS failed");
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+
+               return;
+       }
+
+       cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT,
+                               cw_ss_query_callback, cs);
+}
+
+static gboolean cw_ss_control(int type,
+                               const char *sc, const char *sia,
+                               const char *sib, const char *sic,
+                               const char *dn, DBusMessage *msg, void *data)
+{
+       struct ofono_call_settings *cs = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       int cls = BEARER_CLASS_SS_DEFAULT;
+       DBusMessage *reply;
+
+       if (cs == NULL)
+               return FALSE;
+
+       if (strcmp(sc, "43"))
+               return FALSE;
+
+       if (__ofono_call_settings_is_busy(cs)) {
+               reply = __ofono_error_busy(msg);
+               goto error;
+       }
+
+       if (strlen(sib) || strlen(sib) || strlen(dn))
+               goto bad_format;
+
+       if ((type == SS_CONTROL_TYPE_QUERY && cs->driver->cw_query == NULL) ||
+               (type != SS_CONTROL_TYPE_QUERY && cs->driver->cw_set == NULL)) {
+               reply = __ofono_error_not_implemented(msg);
+               goto error;
+       }
+
+       if (strlen(sia) > 0) {
+               long service_code;
+               char *end;
+
+               service_code = strtoul(sia, &end, 10);
+
+               if (end == sia || *end != '\0')
+                       goto bad_format;
+
+               cls = mmi_service_code_to_bearer_class(service_code);
+               if (cls == 0)
+                       goto bad_format;
+       }
+
+       cs->ss_req_cls = cls;
+       cs->pending = dbus_message_ref(msg);
+
+       /* For the default case use the more readily accepted value */
+       if (cls == BEARER_CLASS_SS_DEFAULT)
+               cls = BEARER_CLASS_DEFAULT;
+
+       switch (type) {
+       case SS_CONTROL_TYPE_REGISTRATION:
+       case SS_CONTROL_TYPE_ACTIVATION:
+               cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
+               cs->driver->cw_set(cs, 1, cls, cw_ss_set_callback, cs);
+               break;
+
+       case SS_CONTROL_TYPE_QUERY:
+               cs->ss_req_type = SS_CONTROL_TYPE_QUERY;
+               /*
+                * Always query the entire set, SMS not applicable
+                * according to 22.004 Appendix A, so CLASS_DEFAULT
+                * is safe to use here
+                */
+               cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT,
+                                       cw_ss_query_callback, cs);
+               break;
+
+       case SS_CONTROL_TYPE_DEACTIVATION:
+       case SS_CONTROL_TYPE_ERASURE:
+               cs->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
+               cs->driver->cw_set(cs, 0, cls, cw_ss_set_callback, cs);
+               break;
+       }
+
+       return TRUE;
+
+bad_format:
+       reply = __ofono_error_invalid_format(msg);
+error:
+       g_dbus_send_message(conn, reply);
+       return TRUE;
+}
+
+static void generate_ss_query_reply(struct ofono_call_settings *cs,
+                                       const char *context, const char *value)
+{
+       const char *sig = "(ss)";
+       const char *ss_type = ss_control_type_to_string(cs->ss_req_type);
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       DBusMessageIter vstruct;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(cs->pending);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var);
+
+       dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL,
+                                               &vstruct);
+
+       dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+                                       &ss_type);
+
+       dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &value);
+
+       dbus_message_iter_close_container(&var, &vstruct);
+
+       dbus_message_iter_close_container(&iter, &var);
+
+       __ofono_dbus_pending_reply(&cs->pending, reply);
+}
+
+static void clip_cnap_colp_colr_ss_query_cb(const struct ofono_error *error,
+                                       int status, void *data)
+{
+       struct ofono_call_settings *cs = data;
+       const char *context;
+       const char *value;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error occurred during ss control query");
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+
+               return;
+       }
+
+       switch (cs->ss_setting) {
+       case CALL_SETTING_TYPE_CLIP:
+               set_clip(cs, status);
+               value = clip_status_to_string(status);
+               context = "CallingLinePresentation";
+               break;
+
+       case CALL_SETTING_TYPE_CNAP:
+               set_cnap(cs, status);
+               value = cnap_status_to_string(status);
+               context = "CallingNamePresentation";
+               break;
+
+
+       case CALL_SETTING_TYPE_COLP:
+               set_colp(cs, status);
+               value = colp_status_to_string(status);
+               context = "ConnectedLinePresentation";
+               break;
+
+       case CALL_SETTING_TYPE_COLR:
+               set_colr(cs, status);
+               value = colr_status_to_string(status);
+               context = "ConnectedLineRestriction";
+               break;
+
+       default:
+               __ofono_dbus_pending_reply(&cs->pending,
+                               __ofono_error_failed(cs->pending));
+               ofono_error("Unknown type during COLR/COLP/CLIP/CNAP ss");
+               return;
+       };
+
+       generate_ss_query_reply(cs, context, value);
+}
+
+static gboolean clip_cnap_colp_colr_ss(int type,
+                               const char *sc, const char *sia,
+                               const char *sib, const char *sic,
+                               const char *dn, DBusMessage *msg, void *data)
+{
+       struct ofono_call_settings *cs = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       void (*query_op)(struct ofono_call_settings *cs,
+                               ofono_call_settings_status_cb_t cb, void *data);
+
+       if (cs == NULL)
+               return FALSE;
+
+       if (__ofono_call_settings_is_busy(cs)) {
+               DBusMessage *reply = __ofono_error_busy(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       if (!strcmp(sc, "30")) {
+               cs->ss_setting = CALL_SETTING_TYPE_CLIP;
+               query_op = cs->driver->clip_query;
+       } else if (!strcmp(sc, "300")) {
+               cs->ss_setting = CALL_SETTING_TYPE_CNAP;
+               query_op = cs->driver->cnap_query;
+       } else if (!strcmp(sc, "76")) {
+               cs->ss_setting = CALL_SETTING_TYPE_COLP;
+               query_op = cs->driver->colp_query;
+       } else if (!strcmp(sc, "77")) {
+               cs->ss_setting = CALL_SETTING_TYPE_COLR;
+               query_op = cs->driver->colr_query;
+       } else {
+               return FALSE;
+       }
+
+       if (type != SS_CONTROL_TYPE_QUERY || strlen(sia) || strlen(sib) ||
+               strlen(sic) || strlen(dn)) {
+               DBusMessage *reply = __ofono_error_invalid_format(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       if (query_op == NULL) {
+               DBusMessage *reply = __ofono_error_not_implemented(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       DBG("Received CLIP/CNAP/COLR/COLP query ss control");
+
+       cs->pending = dbus_message_ref(msg);
+
+       query_op(cs, clip_cnap_colp_colr_ss_query_cb, cs);
+
+       return TRUE;
+}
+
+static void clir_ss_query_callback(const struct ofono_error *error,
+                                       int override, int network, void *data)
+{
+       struct ofono_call_settings *cs = data;
+       const char *value;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("setting clir via SS failed");
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+
+               return;
+       }
+
+       switch (network) {
+       case CLIR_STATUS_UNKNOWN:
+               value = "unknown";
+               break;
+
+       case CLIR_STATUS_PROVISIONED_PERMANENT:
+               value = "enabled";
+               break;
+
+       case CLIR_STATUS_NOT_PROVISIONED:
+               value = "disabled";
+               break;
+
+       case CLIR_STATUS_TEMPORARY_RESTRICTED:
+               if (override == OFONO_CLIR_OPTION_SUPPRESSION)
+                       value = "enabled";
+               else
+                       value = "disabled";
+               break;
+
+       case CLIR_STATUS_TEMPORARY_ALLOWED:
+               if (override == OFONO_CLIR_OPTION_INVOCATION)
+                       value = "enabled";
+               else
+                       value = "disabled";
+               break;
+       default:
+               value = "unknown";
+       };
+
+       generate_ss_query_reply(cs, "CallingLineRestriction", value);
+
+       set_clir_network(cs, network);
+       set_clir_override(cs, override);
+}
+
+static void clir_ss_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("setting clir via SS failed");
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+
+               return;
+       }
+
+       cs->driver->clir_query(cs, clir_ss_query_callback, cs);
+}
+
+static gboolean clir_ss_control(int type,
+                               const char *sc, const char *sia,
+                               const char *sib, const char *sic,
+                               const char *dn, DBusMessage *msg, void *data)
+{
+       struct ofono_call_settings *cs = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (cs == NULL)
+               return FALSE;
+
+       if (strcmp(sc, "31"))
+               return FALSE;
+
+       if (__ofono_call_settings_is_busy(cs)) {
+               DBusMessage *reply = __ofono_error_busy(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       /* This is the temporary form of CLIR, handled in voicecalls */
+       if (!strlen(sia) && !strlen(sib) & !strlen(sic) &&
+                       strlen(dn) && type != SS_CONTROL_TYPE_QUERY)
+               return FALSE;
+
+       if (strlen(sia) || strlen(sib) || strlen(sic) || strlen(dn)) {
+               DBusMessage *reply = __ofono_error_invalid_format(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       if (type == SS_CONTROL_TYPE_QUERY && cs->driver->clir_query == NULL) {
+               DBusMessage *reply = __ofono_error_not_implemented(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       if (type != SS_CONTROL_TYPE_QUERY && cs->driver->clir_set == NULL) {
+               DBusMessage *reply = __ofono_error_not_implemented(msg);
+               g_dbus_send_message(conn, reply);
+
+               return TRUE;
+       }
+
+       cs->ss_setting = CALL_SETTING_TYPE_CLIR;
+       cs->pending = dbus_message_ref(msg);
+
+       switch (type) {
+       case SS_CONTROL_TYPE_REGISTRATION:
+       case SS_CONTROL_TYPE_ACTIVATION:
+               cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
+               cs->driver->clir_set(cs, OFONO_CLIR_OPTION_SUPPRESSION,
+                                       clir_ss_set_callback, cs);
+               break;
+
+       case SS_CONTROL_TYPE_QUERY:
+               cs->ss_req_type = SS_CONTROL_TYPE_QUERY;
+               cs->driver->clir_query(cs, clir_ss_query_callback, cs);
+               break;
+
+       case SS_CONTROL_TYPE_DEACTIVATION:
+       case SS_CONTROL_TYPE_ERASURE:
+               cs->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
+               cs->driver->clir_set(cs, OFONO_CLIR_OPTION_INVOCATION,
+                                       clir_ss_set_callback, cs);
+               break;
+       };
+
+       return TRUE;
+}
+
+static void cs_register_ss_controls(struct ofono_call_settings *cs)
+{
+       __ofono_ussd_ssc_register(cs->ussd, "30", clip_cnap_colp_colr_ss,
+                                                               cs, NULL);
+       __ofono_ussd_ssc_register(cs->ussd, "31", clir_ss_control, cs, NULL);
+       __ofono_ussd_ssc_register(cs->ussd, "76", clip_cnap_colp_colr_ss,
+                                                               cs, NULL);
+       __ofono_ussd_ssc_register(cs->ussd, "300", clip_cnap_colp_colr_ss,
+                                                               cs, NULL);
+
+       __ofono_ussd_ssc_register(cs->ussd, "43", cw_ss_control, cs, NULL);
+
+       if (cs->driver->colr_query != NULL)
+               __ofono_ussd_ssc_register(cs->ussd, "77",
+                                       clip_cnap_colp_colr_ss, cs, NULL);
+}
+
+static void cs_unregister_ss_controls(struct ofono_call_settings *cs)
+{
+       __ofono_ussd_ssc_unregister(cs->ussd, "30");
+       __ofono_ussd_ssc_unregister(cs->ussd, "31");
+       __ofono_ussd_ssc_unregister(cs->ussd, "76");
+       __ofono_ussd_ssc_unregister(cs->ussd, "300");
+
+       __ofono_ussd_ssc_unregister(cs->ussd, "43");
+
+       if (cs->driver->colr_query != NULL)
+               __ofono_ussd_ssc_unregister(cs->ussd, "77");
+}
+
+gboolean __ofono_call_settings_is_busy(struct ofono_call_settings *cs)
+{
+       return cs->pending ? TRUE : FALSE;
+}
+
+static DBusMessage *generate_get_properties_reply(struct ofono_call_settings *cs,
+                                                       DBusMessage *msg)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *str;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       str = clip_status_to_string(cs->clip);
+       ofono_dbus_dict_append(&dict, "CallingLinePresentation",
+                               DBUS_TYPE_STRING, &str);
+
+       str = cnap_status_to_string(cs->cnap);
+       ofono_dbus_dict_append(&dict, "CallingNamePresentation",
+                               DBUS_TYPE_STRING, &str);
+
+       str = colp_status_to_string(cs->colp);
+       ofono_dbus_dict_append(&dict, "ConnectedLinePresentation",
+                               DBUS_TYPE_STRING, &str);
+
+       str = colr_status_to_string(cs->colr);
+       ofono_dbus_dict_append(&dict, "ConnectedLineRestriction",
+                               DBUS_TYPE_STRING, &str);
+
+       str = cdip_status_to_string(cs->cdip);
+       ofono_dbus_dict_append(&dict, "CalledLinePresentation",
+                               DBUS_TYPE_STRING, &str);
+
+       str = clir_status_to_string(cs->clir);
+       ofono_dbus_dict_append(&dict, "CallingLineRestriction",
+                               DBUS_TYPE_STRING, &str);
+
+       str = hide_callerid_to_string(cs->clir_setting);
+       ofono_dbus_dict_append(&dict, "HideCallerId", DBUS_TYPE_STRING, &str);
+
+       property_append_cw_conditions(&dict, cs->cw, BEARER_CLASS_VOICE);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void cs_clir_callback(const struct ofono_error *error,
+                               int override_setting, int network_setting,
+                               void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               goto out;
+
+       set_clir_network(cs, network_setting);
+       set_clir_override(cs, override_setting);
+
+       cs->flags |= CALL_SETTINGS_FLAG_CACHED;
+
+out:
+       if (cs->pending) {
+               DBusMessage *reply = generate_get_properties_reply(cs,
+                                                               cs->pending);
+               __ofono_dbus_pending_reply(&cs->pending, reply);
+       }
+}
+
+static void query_clir(struct ofono_call_settings *cs)
+{
+       if (cs->driver->clir_query == NULL) {
+               if (cs->pending) {
+                       DBusMessage *reply =
+                               generate_get_properties_reply(cs,
+                                                               cs->pending);
+                       __ofono_dbus_pending_reply(&cs->pending, reply);
+               }
+
+               return;
+       }
+
+       cs->driver->clir_query(cs, cs_clir_callback, cs);
+}
+
+static void cs_cdip_callback(const struct ofono_error *error,
+                               int state, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_cdip(cs, state);
+
+       query_clir(cs);
+}
+
+static void query_cdip(struct ofono_call_settings *cs)
+{
+       if (cs->driver->cdip_query == NULL) {
+               query_clir(cs);
+               return;
+       }
+
+       cs->driver->cdip_query(cs, cs_cdip_callback, cs);
+}
+
+
+static void cs_cnap_callback(const struct ofono_error *error,
+                               int state, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_cnap(cs, state);
+
+       query_cdip(cs);
+}
+
+static void query_cnap(struct ofono_call_settings *cs)
+{
+       if (cs->driver->cnap_query == NULL) {
+               query_cdip(cs);
+               return;
+       }
+
+       cs->driver->cnap_query(cs, cs_cnap_callback, cs);
+}
+
+static void cs_clip_callback(const struct ofono_error *error,
+                               int state, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_clip(cs, state);
+
+       query_cnap(cs);
+}
+
+static void query_clip(struct ofono_call_settings *cs)
+{
+       if (cs->driver->clip_query == NULL) {
+               query_clir(cs);
+               return;
+       }
+
+       cs->driver->clip_query(cs, cs_clip_callback, cs);
+}
+
+static void cs_colp_callback(const struct ofono_error *error,
+                               int state, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_colp(cs, state);
+
+       query_clip(cs);
+}
+
+static void query_colp(struct ofono_call_settings *cs)
+{
+       if (cs->driver->colp_query == NULL) {
+               query_clip(cs);
+               return;
+       }
+
+       cs->driver->colp_query(cs, cs_colp_callback, cs);
+}
+
+static void cs_colr_callback(const struct ofono_error *error,
+                               int state, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_colr(cs, state);
+
+       query_colp(cs);
+}
+
+static void query_colr(struct ofono_call_settings *cs)
+{
+       if (cs->driver->colr_query == NULL) {
+               query_colp(cs);
+               return;
+       }
+
+       cs->driver->colr_query(cs, cs_colr_callback, cs);
+}
+
+static void cs_cw_callback(const struct ofono_error *error, int status,
+                               void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               set_cw(cs, status, BEARER_CLASS_VOICE);
+
+       query_colr(cs);
+}
+
+static void query_cw(struct ofono_call_settings *cs)
+{
+       if (cs->driver->cw_query == NULL) {
+               query_colr(cs);
+               return;
+       }
+
+       cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT, cs_cw_callback, cs);
+}
+
+static DBusMessage *cs_get_properties(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (__ofono_call_settings_is_busy(cs) || __ofono_ussd_is_busy(cs->ussd))
+               return __ofono_error_busy(msg);
+
+       if (cs->flags & CALL_SETTINGS_FLAG_CACHED)
+               return generate_get_properties_reply(cs, msg);
+
+       /* Query the settings and report back */
+       cs->pending = dbus_message_ref(msg);
+
+       query_cw(cs);
+
+       return NULL;
+}
+
+static void clir_set_query_callback(const struct ofono_error *error,
+                                       int override_setting,
+                                       int network_setting, void *data)
+{
+       struct ofono_call_settings *cs = data;
+       DBusMessage *reply;
+
+       if (!__ofono_call_settings_is_busy(cs))
+               return;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("set clir successful, but the query was not");
+
+               cs->flags &= ~CALL_SETTINGS_FLAG_CACHED;
+
+               reply = __ofono_error_failed(cs->pending);
+               __ofono_dbus_pending_reply(&cs->pending, reply);
+               return;
+       }
+
+       reply = dbus_message_new_method_return(cs->pending);
+       __ofono_dbus_pending_reply(&cs->pending, reply);
+
+       set_clir_override(cs, override_setting);
+       set_clir_network(cs, network_setting);
+}
+
+static void clir_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("setting clir failed");
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+
+               return;
+       }
+
+       /* Assume that if we have clir_set, we have clir_query */
+       cs->driver->clir_query(cs, clir_set_query_callback, cs);
+}
+
+static DBusMessage *set_clir(DBusMessage *msg, struct ofono_call_settings *cs,
+                               const char *setting)
+{
+       int clir = -1;
+
+       if (cs->driver->clir_set == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (!strcmp(setting, "default"))
+               clir = CLIR_STATUS_NOT_PROVISIONED;
+       else if (!strcmp(setting, "enabled"))
+               clir = CLIR_STATUS_PROVISIONED_PERMANENT;
+       else if (!strcmp(setting, "disabled"))
+               clir = CLIR_STATUS_UNKNOWN;
+
+       if (clir == -1)
+               return __ofono_error_invalid_format(msg);
+
+       cs->pending = dbus_message_ref(msg);
+
+       cs->driver->clir_set(cs, clir, clir_set_callback, cs);
+
+       return NULL;
+}
+
+static void cw_set_query_callback(const struct ofono_error *error, int status,
+                               void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("CW set succeeded, but query failed!");
+
+               cs->flags &= ~CALL_SETTINGS_FLAG_CACHED;
+
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+               return;
+       }
+
+       __ofono_dbus_pending_reply(&cs->pending,
+                               dbus_message_new_method_return(cs->pending));
+
+       set_cw(cs, status, BEARER_CLASS_VOICE);
+}
+
+static void cw_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error occurred during CW set");
+
+               __ofono_dbus_pending_reply(&cs->pending,
+                                       __ofono_error_failed(cs->pending));
+
+               return;
+       }
+
+       cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT,
+                               cw_set_query_callback, cs);
+}
+
+static DBusMessage *set_cw_req(DBusMessage *msg, struct ofono_call_settings *cs,
+                               const char *setting, int cls)
+{
+       int cw;
+
+       if (cs->driver->cw_set == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (!strcmp(setting, "enabled"))
+               cw = 1;
+       else if (!strcmp(setting, "disabled"))
+               cw = 0;
+       else
+               return __ofono_error_invalid_format(msg);
+
+       cs->pending = dbus_message_ref(msg);
+
+       cs->driver->cw_set(cs, cw, cls, cw_set_callback, cs);
+
+       return NULL;
+}
+
+static gboolean is_cw_property(const char *property, int mask, int *out_cls)
+{
+       int i;
+       int len;
+       const char *prefix;
+
+       for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) {
+               if ((i & mask) == 0)
+                       continue;
+
+               prefix = bearer_class_to_string(i);
+
+               len = strlen(prefix);
+
+               if (strncmp(property, prefix, len))
+                       continue;
+
+               if (!strcmp(property+len, "CallWaiting")) {
+                       *out_cls = i;
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static DBusMessage *cs_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_settings *cs = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+       int cls;
+
+       if (__ofono_call_settings_is_busy(cs) || __ofono_ussd_is_busy(cs->ussd))
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (!strcmp(property, "HideCallerId")) {
+               const char *setting;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &setting);
+
+               return set_clir(msg, cs, setting);
+       } else if (is_cw_property(property, BEARER_CLASS_VOICE, &cls)) {
+               const char *setting;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &setting);
+
+               return set_cw_req(msg, cs, setting, cls);
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable cs_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        cs_get_properties,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "SetProperty",        "sv",   "",             cs_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable cs_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+int ofono_call_settings_driver_register(const struct ofono_call_settings_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_call_settings_driver_unregister(const struct ofono_call_settings_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void call_settings_unregister(struct ofono_atom *atom)
+{
+       struct ofono_call_settings *cs = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(cs->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cs->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_CALL_SETTINGS_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_CALL_SETTINGS_INTERFACE);
+
+       if (cs->ussd)
+               cs_unregister_ss_controls(cs);
+
+       if (cs->ussd_watch)
+               __ofono_modem_remove_atom_watch(modem, cs->ussd_watch);
+}
+
+static void call_settings_remove(struct ofono_atom *atom)
+{
+       struct ofono_call_settings *cs = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cs == NULL)
+               return;
+
+       if (cs->driver != NULL && cs->driver->remove != NULL)
+               cs->driver->remove(cs);
+
+       g_free(cs);
+}
+
+struct ofono_call_settings *ofono_call_settings_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data)
+{
+       struct ofono_call_settings *cs;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cs = g_try_new0(struct ofono_call_settings, 1);
+
+       if (cs == NULL)
+               return NULL;
+
+       /* Set all the settings to unknown state */
+       cs->clip = CLIP_STATUS_UNKNOWN;
+       cs->cnap = CNAP_STATUS_UNKNOWN;
+       cs->clir = CLIR_STATUS_UNKNOWN;
+       cs->colp = COLP_STATUS_UNKNOWN;
+       cs->colr = COLR_STATUS_UNKNOWN;
+       cs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CALL_SETTINGS,
+                                               call_settings_remove, cs);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_call_settings_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cs, vendor, data) < 0)
+                       continue;
+
+               cs->driver = drv;
+               break;
+       }
+
+       return cs;
+}
+
+static void ussd_watch(struct ofono_atom *atom,
+                       enum ofono_atom_watch_condition cond, void *data)
+{
+       struct ofono_call_settings *cs = data;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               cs->ussd = NULL;
+               return;
+       }
+
+       cs->ussd = __ofono_atom_get_data(atom);
+       cs_register_ss_controls(cs);
+}
+
+void ofono_call_settings_register(struct ofono_call_settings *cs)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cs->atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(cs->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CALL_SETTINGS_INTERFACE,
+                                       cs_methods, cs_signals, NULL, cs,
+                                       NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CALL_SETTINGS_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_CALL_SETTINGS_INTERFACE);
+
+       cs->ussd_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_USSD,
+                                       ussd_watch, cs, NULL);
+
+       __ofono_atom_register(cs->atom, call_settings_unregister);
+}
+
+void ofono_call_settings_remove(struct ofono_call_settings *cs)
+{
+       __ofono_atom_free(cs->atom);
+}
+
+void ofono_call_settings_set_data(struct ofono_call_settings *cs, void *data)
+{
+       cs->driver_data = data;
+}
+
+void *ofono_call_settings_get_data(struct ofono_call_settings *cs)
+{
+       return cs->driver_data;
+}
diff --git a/src/call-volume.c b/src/call-volume.c
new file mode 100644 (file)
index 0000000..a1c0392
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-volume.h>
+
+#include <gdbus.h>
+#include "ofono.h"
+#include "common.h"
+
+static GSList *g_drivers = NULL;
+
+struct ofono_call_volume {
+       DBusMessage *pending;
+       unsigned char speaker_volume;
+       unsigned char microphone_volume;
+       unsigned char pending_volume;
+       gboolean muted;
+       gboolean muted_pending;
+       const struct ofono_call_volume_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+void ofono_call_volume_set_speaker_volume(struct ofono_call_volume *cv,
+                                               unsigned char percent)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cv->atom);
+
+       cv->speaker_volume = percent;
+
+       if (__ofono_atom_get_registered(cv->atom) == FALSE)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_VOLUME_INTERFACE,
+                                               "SpeakerVolume",
+                                               DBUS_TYPE_BYTE, &percent);
+}
+
+void ofono_call_volume_set_microphone_volume(struct ofono_call_volume *cv,
+                                               unsigned char percent)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cv->atom);
+
+       cv->microphone_volume = percent;
+
+       if (__ofono_atom_get_registered(cv->atom) == FALSE)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_VOLUME_INTERFACE,
+                                               "MicrophoneVolume",
+                                               DBUS_TYPE_BYTE, &percent);
+}
+
+void ofono_call_volume_set_muted(struct ofono_call_volume *cv, int muted)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cv->atom);
+       dbus_bool_t m;
+
+       cv->muted = muted;
+
+       if (__ofono_atom_get_registered(cv->atom) == FALSE)
+               return;
+
+       m = muted;
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_VOLUME_INTERFACE,
+                                               "Muted", DBUS_TYPE_BOOLEAN, &m);
+}
+
+static DBusMessage *cv_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_call_volume *cv = data;
+       DBusMessage *reply;
+       DBusMessageIter iter, dict;
+       dbus_bool_t muted;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "SpeakerVolume", DBUS_TYPE_BYTE,
+                                       &cv->speaker_volume);
+
+       ofono_dbus_dict_append(&dict, "MicrophoneVolume",
+                                       DBUS_TYPE_BYTE, &cv->microphone_volume);
+
+       muted = cv->muted;
+       ofono_dbus_dict_append(&dict, "Muted", DBUS_TYPE_BOOLEAN, &muted);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void sv_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_volume *cv = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cv->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               __ofono_dbus_pending_reply(&cv->pending,
+                                       __ofono_error_failed(cv->pending));
+               return;
+       }
+
+       cv->speaker_volume = cv->pending_volume;
+
+       __ofono_dbus_pending_reply(&cv->pending,
+                               dbus_message_new_method_return(cv->pending));
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CALL_VOLUME_INTERFACE,
+                                       "SpeakerVolume",
+                                       DBUS_TYPE_BYTE, &cv->speaker_volume);
+}
+
+static void mv_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_volume *cv = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cv->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               __ofono_dbus_pending_reply(&cv->pending,
+                                       __ofono_error_failed(cv->pending));
+               return;
+       }
+
+       cv->microphone_volume = cv->pending_volume;
+
+       __ofono_dbus_pending_reply(&cv->pending,
+                               dbus_message_new_method_return(cv->pending));
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CALL_VOLUME_INTERFACE,
+                                       "MicrophoneVolume",
+                                       DBUS_TYPE_BYTE, &cv->microphone_volume);
+}
+
+static void muted_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_call_volume *cv = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cv->atom);
+       dbus_bool_t m;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               cv->muted_pending = cv->muted;
+               __ofono_dbus_pending_reply(&cv->pending,
+                                       __ofono_error_failed(cv->pending));
+               return;
+       }
+
+       cv->muted = cv->muted_pending;
+       m = cv->muted;
+
+       __ofono_dbus_pending_reply(&cv->pending,
+                               dbus_message_new_method_return(cv->pending));
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CALL_VOLUME_INTERFACE,
+                                               "Muted", DBUS_TYPE_BOOLEAN, &m);
+}
+
+static DBusMessage *cv_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_call_volume *cv = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+
+       if (cv->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (g_str_equal(property, "SpeakerVolume") == TRUE) {
+               unsigned char percent;
+
+               if (cv->driver->speaker_volume == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BYTE)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &percent);
+
+               if (percent > 100)
+                       return __ofono_error_invalid_format(msg);
+
+               if (percent == cv->speaker_volume)
+                       return dbus_message_new_method_return(msg);
+
+               cv->pending_volume = percent;
+               cv->pending = dbus_message_ref(msg);
+               cv->driver->speaker_volume(cv, percent, sv_set_callback, cv);
+
+               return NULL;
+       } else if (g_str_equal(property, "MicrophoneVolume") == TRUE) {
+               unsigned char percent;
+
+               if (cv->driver->microphone_volume == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BYTE)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &percent);
+
+               if (percent > 100)
+                       return __ofono_error_invalid_format(msg);
+
+               if (percent == cv->microphone_volume)
+                       return dbus_message_new_method_return(msg);
+
+               cv->pending_volume = percent;
+               cv->pending = dbus_message_ref(msg);
+               cv->driver->microphone_volume(cv, percent, mv_set_callback, cv);
+
+               return NULL;
+       } else if (g_str_equal(property, "Muted") == TRUE) {
+               dbus_bool_t muted;
+
+               if (cv->driver->mute == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &muted);
+
+               if (muted == (dbus_bool_t) cv->muted)
+                       return dbus_message_new_method_return(msg);
+
+               cv->muted_pending = muted;
+               cv->pending = dbus_message_ref(msg);
+               cv->driver->mute(cv, muted, muted_set_callback, cv);
+
+               return NULL;
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable cv_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        cv_get_properties },
+       { "SetProperty",        "sv",   "",             cv_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable cv_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static void call_volume_remove(struct ofono_atom *atom)
+{
+       struct ofono_call_volume *cv = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cv == NULL)
+               return;
+
+       if (cv->driver != NULL && cv->driver->remove != NULL)
+               cv->driver->remove(cv);
+
+       g_free(cv);
+}
+
+struct ofono_call_volume *ofono_call_volume_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_call_volume *cv;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cv = g_try_new0(struct ofono_call_volume, 1);
+       if (cv == NULL)
+               return NULL;
+
+       cv->atom = __ofono_modem_add_atom(modem,
+                                       OFONO_ATOM_TYPES_CALL_VOLUME,
+                                       call_volume_remove, cv);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_call_volume_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cv, vendor, data) < 0)
+                       continue;
+
+               cv->driver = drv;
+               break;
+       }
+
+       return cv;
+}
+
+static void call_volume_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       ofono_modem_remove_interface(modem, OFONO_CALL_VOLUME_INTERFACE);
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_CALL_VOLUME_INTERFACE);
+}
+
+void ofono_call_volume_register(struct ofono_call_volume *cv)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cv->atom);
+       const char *path = __ofono_atom_get_path(cv->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CALL_VOLUME_INTERFACE,
+                                       cv_methods, cv_signals, NULL,
+                                       cv, NULL)) {
+               ofono_error("Could not create %s interface",
+                                       OFONO_CALL_VOLUME_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_CALL_VOLUME_INTERFACE);
+
+       __ofono_atom_register(cv->atom, call_volume_unregister);
+}
+
+int ofono_call_volume_driver_register(const struct ofono_call_volume_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_call_volume_driver_unregister(
+                               const struct ofono_call_volume_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+void ofono_call_volume_remove(struct ofono_call_volume *cv)
+{
+       __ofono_atom_free(cv->atom);
+}
+
+void ofono_call_volume_set_data(struct ofono_call_volume *cv, void *data)
+{
+       cv->driver_data = data;
+}
+
+void *ofono_call_volume_get_data(struct ofono_call_volume *cv)
+{
+       return cv->driver_data;
+}
diff --git a/src/cbs.c b/src/cbs.c
new file mode 100644 (file)
index 0000000..d898823
--- /dev/null
+++ b/src/cbs.c
@@ -0,0 +1,1125 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "util.h"
+#include "smsutil.h"
+#include "simutil.h"
+#include "storage.h"
+
+#define SETTINGS_STORE "cbs"
+#define SETTINGS_GROUP "Settings"
+
+static GSList *g_drivers = NULL;
+
+enum etws_topic_type {
+       ETWS_TOPIC_TYPE_EARTHQUAKE =            4352,
+       ETWS_TOPIC_TYPE_TSUNAMI =               4353,
+       ETWS_TOPIC_TYPE_EARTHQUAKE_TSUNAMI =    4354,
+       ETWS_TOPIC_TYPE_TEST =                  4355,
+       ETWS_TOPIC_TYPE_EMERGENCY =             4356,
+};
+
+struct ofono_cbs {
+       DBusMessage *pending;
+       struct cbs_assembly *assembly;
+       GSList *topics;
+       GSList *new_topics;
+       struct ofono_sim *sim;
+       struct ofono_sim_context *sim_context;
+       struct ofono_stk *stk;
+       struct ofono_netreg *netreg;
+       unsigned int netreg_watch;
+       unsigned int location_watch;
+       unsigned short efcbmi_length;
+       GSList *efcbmi_contents;
+       unsigned short efcbmir_length;
+       GSList *efcbmir_contents;
+       unsigned short efcbmid_length;
+       GSList *efcbmid_contents;
+       gboolean efcbmid_update;
+       guint reset_source;
+       int lac;
+       int ci;
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       ofono_bool_t powered;
+       GKeyFile *settings;
+       char *imsi;
+       const struct ofono_cbs_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static void cbs_dispatch_base_station_id(struct ofono_cbs *cbs, const char *id)
+{
+       DBG("Base station id: %s", id);
+
+       if (cbs->netreg == NULL)
+               return;
+
+       if (cbs->reset_source) {
+               g_source_remove(cbs->reset_source);
+               cbs->reset_source = 0;
+       }
+
+       __ofono_netreg_set_base_station_name(cbs->netreg, id);
+}
+
+static void cbs_dispatch_emergency(struct ofono_cbs *cbs, const char *message,
+                                       enum etws_topic_type topic,
+                                       gboolean alert, gboolean popup)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cbs->atom);
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t boolean;
+       const char *emergency_str;
+
+       if (topic == ETWS_TOPIC_TYPE_TEST) {
+               ofono_error("Explicitly ignoring ETWS Test messages");
+               return;
+       }
+
+       switch (topic) {
+       case ETWS_TOPIC_TYPE_EARTHQUAKE:
+               emergency_str = "Earthquake";
+               break;
+       case ETWS_TOPIC_TYPE_TSUNAMI:
+               emergency_str = "Tsunami";
+               break;
+       case ETWS_TOPIC_TYPE_EARTHQUAKE_TSUNAMI:
+               emergency_str = "Earthquake+Tsunami";
+               break;
+       case ETWS_TOPIC_TYPE_EMERGENCY:
+               emergency_str = "Other";
+               break;
+       default:
+               return;
+       };
+
+       signal = dbus_message_new_signal(path, OFONO_CELL_BROADCAST_INTERFACE,
+                                               "EmergencyBroadcast");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                               &dict);
+
+       ofono_dbus_dict_append(&dict, "EmergencyType",
+                               DBUS_TYPE_STRING, &emergency_str);
+
+       boolean = alert;
+       ofono_dbus_dict_append(&dict, "EmergencyAlert",
+                               DBUS_TYPE_BOOLEAN, &boolean);
+
+       boolean = popup;
+       ofono_dbus_dict_append(&dict, "Popup", DBUS_TYPE_BOOLEAN, &boolean);
+
+       dbus_message_iter_close_container(&iter, &dict);
+       g_dbus_send_message(conn, signal);
+}
+
+static void cbs_dispatch_text(struct ofono_cbs *cbs, enum sms_class cls,
+                               unsigned short channel, const char *message)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(cbs->atom);
+
+       g_dbus_emit_signal(conn, path, OFONO_CELL_BROADCAST_INTERFACE,
+                               "IncomingBroadcast",
+                               DBUS_TYPE_STRING, &message,
+                               DBUS_TYPE_UINT16, &channel,
+                               DBUS_TYPE_INVALID);
+}
+
+void ofono_cbs_notify(struct ofono_cbs *cbs, const unsigned char *pdu,
+                               int pdu_len)
+{
+       struct cbs c;
+       enum sms_class cls;
+       gboolean udhi;
+       gboolean comp;
+       GSList *cbs_list;
+       enum sms_charset charset;
+       char *message;
+       char iso639_lang[3];
+
+       if (cbs->assembly == NULL)
+               return;
+
+       if (!cbs_decode(pdu, pdu_len, &c)) {
+               ofono_error("Unable to decode CBS PDU");
+               return;
+       }
+
+       if (cbs_topic_in_range(c.message_identifier, cbs->efcbmid_contents)) {
+               if (cbs->sim == NULL)
+                       return;
+
+               if (!__ofono_sim_service_available(cbs->sim,
+                                       SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_CB,
+                                       SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_CB))
+                       return;
+
+               if (cbs->stk)
+                       __ofono_cbs_sim_download(cbs->stk, &c);
+
+               return;
+       }
+
+       if (!cbs->powered) {
+               ofono_error("Ignoring CBS because powered is off");
+               return;
+       }
+
+       if (!cbs_dcs_decode(c.dcs, &udhi, &cls, &charset, &comp, NULL, NULL)) {
+               ofono_error("Unknown / Reserved DCS.  Ignoring");
+               return;
+       }
+
+       if (udhi) {
+               ofono_error("CBS messages with UDH not supported");
+               return;
+       }
+
+       if (charset == SMS_CHARSET_8BIT) {
+               ofono_error("Datagram CBS not supported");
+               return;
+       }
+
+       if (comp) {
+               ofono_error("CBS messages with compression not supported");
+               return;
+       }
+
+       cbs_list = cbs_assembly_add_page(cbs->assembly, &c);
+
+       if (cbs_list == NULL)
+               return;
+
+       message = cbs_decode_text(cbs_list, iso639_lang);
+
+       if (message == NULL)
+               goto out;
+
+       if (c.message_identifier >= ETWS_TOPIC_TYPE_EARTHQUAKE &&
+                       c.message_identifier <= ETWS_TOPIC_TYPE_EMERGENCY) {
+               gboolean alert = FALSE;
+               gboolean popup = FALSE;
+
+               /* 3GPP 23.041 9.4.1.2.1: Alert is encoded in bit 9 */
+               if (c.message_code & (1 << 9))
+                       alert = TRUE;
+
+               /* 3GPP 23.041 9.4.1.2.1: Popup is encoded in bit 8 */
+               if (c.message_code & (1 << 8))
+                       popup = TRUE;
+
+               cbs_dispatch_emergency(cbs, message,
+                                       c.message_identifier, alert, popup);
+               goto out;
+       }
+
+       /*
+        * 3GPP 23.041: NOTE 5:        Code 00 is intended for use by the
+        * network operators for base station IDs.
+        */
+       if (c.gs == CBS_GEO_SCOPE_CELL_IMMEDIATE) {
+               cbs_dispatch_base_station_id(cbs, message);
+               goto out;
+       }
+
+       cbs_dispatch_text(cbs, cls, c.message_identifier, message);
+
+out:
+       g_free(message);
+       g_slist_foreach(cbs_list, (GFunc)g_free, NULL);
+       g_slist_free(cbs_list);
+}
+
+static DBusMessage *cbs_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_cbs *cbs = data;
+       DBusMessage *reply;
+       DBusMessageIter iter, dict;
+       char *topics;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN,
+                               &cbs->powered);
+
+       topics = cbs_topic_ranges_to_string(cbs->topics);
+       ofono_dbus_dict_append(&dict, "Topics", DBUS_TYPE_STRING, &topics);
+       g_free(topics);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static char *cbs_topics_to_str(struct ofono_cbs *cbs, GSList *user_topics)
+{
+       GSList *topics = NULL;
+       char *topic_str;
+       struct cbs_topic_range etws_range = { 4352, 4356 };
+
+       if (user_topics != NULL)
+               topics = g_slist_concat(topics,
+                                       g_slist_copy(user_topics));
+
+       if (cbs->efcbmid_contents != NULL)
+               topics = g_slist_concat(topics,
+                                       g_slist_copy(cbs->efcbmid_contents));
+
+       topics = g_slist_append(topics, &etws_range);
+
+       topic_str = cbs_topic_ranges_to_string(topics);
+       g_slist_free(topics);
+
+       return topic_str;
+}
+
+static void cbs_set_topics_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_cbs *cbs = data;
+       const char *path = __ofono_atom_get_path(cbs->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *reply;
+       char *topics;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               g_slist_foreach(cbs->new_topics, (GFunc)g_free, NULL);
+               g_slist_free(cbs->new_topics);
+               cbs->new_topics = NULL;
+
+               DBG("Setting Cell Broadcast topics failed");
+               __ofono_dbus_pending_reply(&cbs->pending,
+                                       __ofono_error_failed(cbs->pending));
+               return;
+       }
+
+       g_slist_foreach(cbs->topics, (GFunc)g_free, NULL);
+       g_slist_free(cbs->topics);
+       cbs->topics = cbs->new_topics;
+       cbs->new_topics = NULL;
+
+       reply = dbus_message_new_method_return(cbs->pending);
+       __ofono_dbus_pending_reply(&cbs->pending, reply);
+
+       topics = cbs_topic_ranges_to_string(cbs->topics);
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CELL_BROADCAST_INTERFACE,
+                                               "Topics",
+                                               DBUS_TYPE_STRING, &topics);
+
+       if (cbs->settings) {
+               g_key_file_set_string(cbs->settings, SETTINGS_GROUP,
+                                       "Topics", topics);
+               storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings);
+       }
+       g_free(topics);
+}
+
+static DBusMessage *cbs_set_topics(struct ofono_cbs *cbs, const char *value,
+                                       DBusMessage *msg)
+{
+       GSList *topics;
+       char *topic_str;
+       struct ofono_error error;
+
+       topics = cbs_extract_topic_ranges(value);
+
+       if (topics == NULL && value[0] != '\0')
+               return __ofono_error_invalid_format(msg);
+
+       if (cbs->driver->set_topics == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       cbs->new_topics = topics;
+
+       cbs->pending = dbus_message_ref(msg);
+
+       if (!cbs->powered) {
+               error.type = OFONO_ERROR_TYPE_NO_ERROR;
+               cbs_set_topics_cb(&error, cbs);
+               return NULL;
+       }
+
+       topic_str = cbs_topics_to_str(cbs, topics);
+       cbs->driver->set_topics(cbs, topic_str, cbs_set_topics_cb, cbs);
+       g_free(topic_str);
+
+       return NULL;
+}
+
+static void cbs_set_powered_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_cbs *cbs = data;
+       const char *path = __ofono_atom_get_path(cbs->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Setting Cell Broadcast topics failed");
+
+               if (cbs->pending == NULL)
+                       return;
+
+               __ofono_dbus_pending_reply(&cbs->pending,
+                                       __ofono_error_failed(cbs->pending));
+               return;
+       }
+
+       cbs->powered = !cbs->powered;
+
+       if (cbs->settings) {
+               g_key_file_set_boolean(cbs->settings, SETTINGS_GROUP,
+                                       "Powered", cbs->powered);
+               storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings);
+       }
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CELL_BROADCAST_INTERFACE,
+                                               "Powered",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &cbs->powered);
+
+       if (cbs->pending == NULL)
+               return;
+
+       reply = dbus_message_new_method_return(cbs->pending);
+       __ofono_dbus_pending_reply(&cbs->pending, reply);
+}
+
+static DBusMessage *cbs_set_powered(struct ofono_cbs *cbs, gboolean value,
+                                       DBusMessage *msg)
+{
+       const char *path = __ofono_atom_get_path(cbs->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       char *topic_str;
+
+       if (cbs->powered == value)
+               goto reply;
+
+       if (cbs->driver->set_topics == NULL ||
+                       cbs->driver->clear_topics == NULL)
+               goto done;
+
+       if (msg)
+               cbs->pending = dbus_message_ref(msg);
+
+       if (value) {
+               topic_str = cbs_topics_to_str(cbs, cbs->topics);
+               cbs->driver->set_topics(cbs, topic_str,
+                                       cbs_set_powered_cb, cbs);
+               g_free(topic_str);
+       } else {
+               cbs->driver->clear_topics(cbs, cbs_set_powered_cb, cbs);
+       }
+
+       return NULL;
+
+done:
+       cbs->powered = value;
+
+       if (cbs->settings) {
+               g_key_file_set_boolean(cbs->settings, SETTINGS_GROUP,
+                                       "Powered", cbs->powered);
+               storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings);
+       }
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_CELL_BROADCAST_INTERFACE,
+                                               "Powered",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &cbs->powered);
+
+reply:
+       if (msg)
+               return dbus_message_new_method_return(msg);
+
+       return NULL;
+}
+
+static DBusMessage *cbs_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_cbs *cbs = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+
+       if (cbs->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (!strcmp(property, "Powered")) {
+               dbus_bool_t value;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               return cbs_set_powered(cbs, value, msg);
+       }
+
+       if (!strcmp(property, "Topics")) {
+               const char *value;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               return cbs_set_topics(cbs, value, msg);
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable cbs_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        cbs_get_properties },
+       { "SetProperty",        "sv",   "",             cbs_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable cbs_signals[] = {
+       { "PropertyChanged",    "sv"            },
+       { "IncomingBroadcast",  "sq"            },
+       { "EmergencyBroadcast", "sa{sv}"        },
+       { }
+};
+
+int ofono_cbs_driver_register(const struct ofono_cbs_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_cbs_driver_unregister(const struct ofono_cbs_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void cbs_unregister(struct ofono_atom *atom)
+{
+       struct ofono_cbs *cbs = __ofono_atom_get_data(atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       g_dbus_unregister_interface(conn, path, OFONO_CELL_BROADCAST_INTERFACE);
+       ofono_modem_remove_interface(modem, OFONO_CELL_BROADCAST_INTERFACE);
+
+       if (cbs->topics) {
+               g_slist_foreach(cbs->topics, (GFunc) g_free, NULL);
+               g_slist_free(cbs->topics);
+               cbs->topics = NULL;
+       }
+
+       if (cbs->new_topics) {
+               g_slist_foreach(cbs->new_topics, (GFunc) g_free, NULL);
+               g_slist_free(cbs->new_topics);
+               cbs->new_topics = NULL;
+       }
+
+       if (cbs->efcbmid_length) {
+               cbs->efcbmid_length = 0;
+               g_slist_foreach(cbs->efcbmid_contents, (GFunc) g_free, NULL);
+               g_slist_free(cbs->efcbmid_contents);
+               cbs->efcbmid_contents = NULL;
+       }
+
+       if (cbs->sim_context) {
+               ofono_sim_context_free(cbs->sim_context);
+               cbs->sim_context = NULL;
+       }
+
+       cbs->sim = NULL;
+       cbs->stk = NULL;
+
+       if (cbs->reset_source) {
+               g_source_remove(cbs->reset_source);
+               cbs->reset_source = 0;
+
+               if (cbs->netreg)
+                       __ofono_netreg_set_base_station_name(cbs->netreg, NULL);
+       }
+
+       cbs->powered = FALSE;
+
+       if (cbs->settings) {
+               storage_close(cbs->imsi, SETTINGS_STORE, cbs->settings, TRUE);
+
+               g_free(cbs->imsi);
+               cbs->imsi = NULL;
+               cbs->settings = NULL;
+       }
+
+       if (cbs->netreg_watch) {
+               if (cbs->location_watch) {
+                       __ofono_netreg_remove_status_watch(cbs->netreg,
+                                                       cbs->location_watch);
+                       cbs->location_watch = 0;
+               }
+
+               __ofono_modem_remove_atom_watch(modem, cbs->netreg_watch);
+               cbs->netreg_watch = 0;
+               cbs->netreg = NULL;
+       }
+}
+
+static void cbs_remove(struct ofono_atom *atom)
+{
+       struct ofono_cbs *cbs = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cbs == NULL)
+               return;
+
+       if (cbs->driver != NULL && cbs->driver->remove != NULL)
+               cbs->driver->remove(cbs);
+
+       cbs_assembly_free(cbs->assembly);
+       cbs->assembly = NULL;
+
+       g_free(cbs);
+}
+
+struct ofono_cbs *ofono_cbs_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_cbs *cbs;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cbs = g_try_new0(struct ofono_cbs, 1);
+
+       if (cbs == NULL)
+               return NULL;
+
+       cbs->assembly = cbs_assembly_new();
+       cbs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CBS,
+                                               cbs_remove, cbs);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_cbs_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cbs, vendor, data) < 0)
+                       continue;
+
+               cbs->driver = drv;
+               break;
+       }
+
+       return cbs;
+}
+
+static void cbs_got_file_contents(struct ofono_cbs *cbs)
+{
+       gboolean powered;
+       GSList *initial_topics = NULL;
+       char *topics_str;
+       GError *error = NULL;
+
+       if (cbs->topics == NULL) {
+               if (cbs->efcbmi_contents != NULL)
+                       initial_topics = g_slist_concat(initial_topics,
+                                       g_slist_copy(cbs->efcbmi_contents));
+
+               if (cbs->efcbmir_contents != NULL)
+                       initial_topics = g_slist_concat(initial_topics,
+                                       g_slist_copy(cbs->efcbmir_contents));
+
+               cbs->topics = cbs_optimize_ranges(initial_topics);
+               g_slist_free(initial_topics);
+
+               topics_str = cbs_topic_ranges_to_string(cbs->topics);
+               g_key_file_set_string(cbs->settings, SETTINGS_GROUP,
+                                       "Topics", topics_str);
+               g_free(topics_str);
+               storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings);
+       }
+
+       if (cbs->efcbmi_length) {
+               cbs->efcbmi_length = 0;
+               g_slist_foreach(cbs->efcbmi_contents, (GFunc) g_free, NULL);
+               g_slist_free(cbs->efcbmi_contents);
+               cbs->efcbmi_contents = NULL;
+       }
+
+       if (cbs->efcbmir_length) {
+               cbs->efcbmir_length = 0;
+               g_slist_foreach(cbs->efcbmir_contents, (GFunc) g_free, NULL);
+               g_slist_free(cbs->efcbmir_contents);
+               cbs->efcbmir_contents = NULL;
+       }
+
+       powered = g_key_file_get_boolean(cbs->settings, SETTINGS_GROUP,
+                                               "Powered", &error);
+
+       if (error) {
+               g_error_free(error);
+               powered = TRUE;
+               g_key_file_set_boolean(cbs->settings, SETTINGS_GROUP,
+                                       "Powered", powered);
+       }
+
+       cbs_set_powered(cbs, powered, NULL);
+}
+
+static void sim_cbmi_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_cbs *cbs = userdata;
+       unsigned short mi;
+       int i;
+       char *str;
+
+       if (!ok)
+               return;
+
+       if ((length % 2) == 1 || length < 2)
+               return;
+
+       cbs->efcbmi_length = length;
+
+       for (i = 0; i < length; i += 2) {
+               struct cbs_topic_range *range;
+
+               if (data[i] == 0xff && data[i+1] == 0xff)
+                       continue;
+
+               mi = (data[i] << 8) + data[i+1];
+
+               if (mi > 999)
+                       continue;
+
+               range = g_new0(struct cbs_topic_range, 1);
+               range->min = mi;
+               range->max = mi;
+
+               cbs->efcbmi_contents = g_slist_prepend(cbs->efcbmi_contents,
+                                                       range);
+       }
+
+       if (cbs->efcbmi_contents == NULL)
+               return;
+
+       str = cbs_topic_ranges_to_string(cbs->efcbmi_contents);
+       DBG("Got cbmi: %s", str);
+       g_free(str);
+}
+
+static void sim_cbmir_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_cbs *cbs = userdata;
+       int i;
+       unsigned short min;
+       unsigned short max;
+       char *str;
+
+       if (!ok)
+               return;
+
+       if ((length % 4) != 0)
+               return;
+
+       cbs->efcbmir_length = length;
+
+       for (i = 0; i < length; i += 4) {
+               struct cbs_topic_range *range;
+
+               if (data[i] == 0xff && data[i+1] == 0xff &&
+                               data[i+2] == 0xff && data[i+3] == 0xff)
+                       continue;
+
+               min = (data[i] << 8) + data[i+1];
+               max = (data[i+2] << 8) + data[i+3];
+
+               if (min > 999 || max > 999 || min > max)
+                       continue;
+
+               range = g_new0(struct cbs_topic_range, 1);
+               range->min = min;
+               range->max = max;
+
+               cbs->efcbmir_contents = g_slist_prepend(cbs->efcbmir_contents,
+                                                       range);
+       }
+
+       if (cbs->efcbmir_contents == NULL)
+               return;
+
+       str = cbs_topic_ranges_to_string(cbs->efcbmir_contents);
+       DBG("Got cbmir: %s", str);
+       g_free(str);
+}
+
+static void sim_cbmid_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_cbs *cbs = userdata;
+       unsigned short mi;
+       int i;
+       char *str;
+       GSList *contents = NULL;
+
+       if (!ok)
+               goto done;
+
+       if ((length % 2) == 1 || length < 2)
+               goto done;
+
+       cbs->efcbmid_length = length;
+
+       for (i = 0; i < length; i += 2) {
+               struct cbs_topic_range *range;
+
+               if (data[i] == 0xff && data[i+1] == 0xff)
+                       continue;
+
+               mi = (data[i] << 8) + data[i+1];
+
+               range = g_new0(struct cbs_topic_range, 1);
+               range->min = mi;
+               range->max = mi;
+
+               contents = g_slist_prepend(contents, range);
+       }
+
+       if (contents == NULL)
+               goto done;
+
+       cbs->efcbmid_contents = g_slist_reverse(contents);
+
+       str = cbs_topic_ranges_to_string(cbs->efcbmid_contents);
+       DBG("Got cbmid: %s", str);
+       g_free(str);
+
+done:
+       if (cbs->efcbmid_update) {
+               if (cbs->powered == TRUE) {
+                       char *topic_str = cbs_topics_to_str(cbs, cbs->topics);
+                       cbs->driver->set_topics(cbs, topic_str,
+                                               cbs_set_powered_cb, cbs);
+                       g_free(topic_str);
+               }
+
+               cbs->efcbmid_update = FALSE;
+       } else
+               cbs_got_file_contents(cbs);
+}
+
+static void cbs_efcbmid_changed(int id, void *userdata)
+{
+       struct ofono_cbs *cbs = userdata;
+
+       if (cbs->efcbmid_length) {
+               cbs->efcbmid_length = 0;
+               g_slist_foreach(cbs->efcbmid_contents, (GFunc) g_free, NULL);
+               g_slist_free(cbs->efcbmid_contents);
+               cbs->efcbmid_contents = NULL;
+       }
+
+       cbs->efcbmid_update = TRUE;
+
+       ofono_sim_read(cbs->sim_context, SIM_EFCBMID_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_cbmid_read_cb, cbs);
+}
+
+static void cbs_got_imsi(struct ofono_cbs *cbs)
+{
+       const char *imsi = ofono_sim_get_imsi(cbs->sim);
+       char *topics_str;
+
+       DBG("Got IMSI: %s", imsi);
+
+       cbs->settings = storage_open(imsi, SETTINGS_STORE);
+       if (cbs->settings == NULL)
+               return;
+
+       cbs->imsi = g_strdup(imsi);
+
+       cbs->topics = NULL;
+
+       topics_str = g_key_file_get_string(cbs->settings, SETTINGS_GROUP,
+                                               "Topics", NULL);
+       if (topics_str)
+               cbs->topics = cbs_extract_topic_ranges(topics_str);
+
+       /*
+        * If stored value is invalid or no stored value, bootstrap
+        * topics list from SIM contents
+        */
+       if (topics_str == NULL ||
+                       (cbs->topics == NULL && topics_str[0] != '\0')) {
+               ofono_sim_read(cbs->sim_context, SIM_EFCBMI_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               sim_cbmi_read_cb, cbs);
+               ofono_sim_read(cbs->sim_context, SIM_EFCBMIR_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               sim_cbmir_read_cb, cbs);
+       }
+
+       if (topics_str)
+               g_free(topics_str);
+
+       ofono_sim_read(cbs->sim_context, SIM_EFCBMID_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_cbmid_read_cb, cbs);
+       ofono_sim_add_file_watch(cbs->sim_context, SIM_EFCBMID_FILEID,
+                                       cbs_efcbmid_changed, cbs, NULL);
+}
+
+static gboolean reset_base_station_name(gpointer user)
+{
+       struct ofono_cbs *cbs = user;
+
+       cbs->reset_source = 0;
+
+       if (cbs->netreg == NULL)
+               goto out;
+
+       __ofono_netreg_set_base_station_name(cbs->netreg, NULL);
+
+out:
+       return FALSE;
+}
+
+static void cbs_location_changed(int status, int lac, int ci, int tech,
+                                       const char *mcc, const char *mnc,
+                                       void *data)
+{
+       struct ofono_cbs *cbs = data;
+       gboolean plmn_changed = FALSE;
+       gboolean lac_changed = FALSE;
+       gboolean ci_changed = FALSE;
+
+       DBG("%d, %d, %d, %d, %s%s", status, lac, ci, tech, mcc, mnc);
+
+       if (mcc == NULL || mnc == NULL) {
+               if (cbs->mcc[0] == '\0' && cbs->mnc[0] == '\0')
+                       return;
+
+               memset(cbs->mcc, 0, sizeof(cbs->mcc));
+               memset(cbs->mnc, 0, sizeof(cbs->mnc));
+
+               plmn_changed = TRUE;
+               goto out;
+       }
+
+       if (strcmp(cbs->mcc, mcc) || strcmp(cbs->mnc, mnc)) {
+               memcpy(cbs->mcc, mcc, sizeof(cbs->mcc));
+               memcpy(cbs->mnc, mnc, sizeof(cbs->mnc));
+
+               plmn_changed = TRUE;
+               goto out;
+       }
+
+       if (cbs->lac != lac) {
+               cbs->lac = lac;
+
+               lac_changed = TRUE;
+               goto out;
+       }
+
+       if (cbs->ci != ci) {
+               cbs->ci = ci;
+
+               ci_changed = TRUE;
+               goto out;
+       }
+
+       return;
+
+out:
+       DBG("%d, %d, %d", plmn_changed, lac_changed, ci_changed);
+
+       /*
+        * In order to minimize signal transmissions we wait about X seconds
+        * before reseting the base station id.  The hope is that we receive
+        * another cell broadcast with the new base station name within
+        * that time
+        */
+       if (lac_changed || ci_changed) {
+               cbs->reset_source =
+                       g_timeout_add_seconds(3, reset_base_station_name, cbs);
+       }
+
+       cbs_assembly_location_changed(cbs->assembly, plmn_changed,
+                                       lac_changed, ci_changed);
+}
+
+static void netreg_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct ofono_cbs *cbs = data;
+       const char *mcc;
+       const char *mnc;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               cbs->location_watch = 0;
+               cbs->netreg = 0;
+               return;
+       }
+
+       cbs->netreg = __ofono_atom_get_data(atom);
+       cbs->location_watch = __ofono_netreg_add_status_watch(cbs->netreg,
+                                       cbs_location_changed, cbs, NULL);
+
+       mcc = ofono_netreg_get_mcc(cbs->netreg);
+       mnc = ofono_netreg_get_mnc(cbs->netreg);
+
+       if (mcc && mnc) {
+               memcpy(cbs->mcc, mcc, sizeof(cbs->mcc));
+               memcpy(cbs->mnc, mnc, sizeof(cbs->mnc));
+       } else {
+               memset(cbs->mcc, 0, sizeof(cbs->mcc));
+               memset(cbs->mnc, 0, sizeof(cbs->mnc));
+       }
+
+       cbs->lac = ofono_netreg_get_location(cbs->netreg);
+       cbs->ci = ofono_netreg_get_cellid(cbs->netreg);
+
+       /*
+        * Clear out the cbs assembly just in case, worst case
+        * we will receive the cell broadcasts again
+        */
+       cbs_assembly_location_changed(cbs->assembly, TRUE, TRUE, TRUE);
+}
+
+void ofono_cbs_register(struct ofono_cbs *cbs)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cbs->atom);
+       const char *path = __ofono_atom_get_path(cbs->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CELL_BROADCAST_INTERFACE,
+                                       cbs_methods, cbs_signals,
+                                       NULL, cbs, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CELL_BROADCAST_INTERFACE);
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_CELL_BROADCAST_INTERFACE);
+
+       cbs->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+       if (cbs->sim) {
+               cbs->sim_context = ofono_sim_context_create(cbs->sim);
+
+               if (ofono_sim_get_state(cbs->sim) == OFONO_SIM_STATE_READY)
+                       cbs_got_imsi(cbs);
+       }
+
+       cbs->stk = __ofono_atom_find(OFONO_ATOM_TYPE_STK, modem);
+
+       cbs->netreg_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_NETREG,
+                                       netreg_watch, cbs, NULL);
+
+       __ofono_atom_register(cbs->atom, cbs_unregister);
+}
+
+void ofono_cbs_remove(struct ofono_cbs *cbs)
+{
+       __ofono_atom_free(cbs->atom);
+}
+
+void ofono_cbs_set_data(struct ofono_cbs *cbs, void *data)
+{
+       cbs->driver_data = data;
+}
+
+void *ofono_cbs_get_data(struct ofono_cbs *cbs)
+{
+       return cbs->driver_data;
+}
diff --git a/src/cdma-connman.c b/src/cdma-connman.c
new file mode 100644 (file)
index 0000000..0c8b061
--- /dev/null
@@ -0,0 +1,693 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+#include "common.h"
+
+static GSList *g_drivers;
+
+struct cdma_connman_settings {
+       char *interface;
+       gboolean static_ip;
+       char *ip;
+       char *netmask;
+       char *gateway;
+       char **dns;
+};
+
+struct ofono_cdma_connman {
+       ofono_bool_t powered;
+       ofono_bool_t dormant;
+       struct cdma_connman_settings *settings;
+       DBusMessage *pending;
+       const struct ofono_cdma_connman_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       char username[OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH + 1];
+       char password[OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH + 1];
+};
+
+static void cdma_connman_settings_free(struct cdma_connman_settings *settings)
+{
+       DBG("");
+
+       g_free(settings->interface);
+       g_free(settings->ip);
+       g_free(settings->netmask);
+       g_free(settings->gateway);
+       g_strfreev(settings->dns);
+
+       g_free(settings);
+}
+
+static void cdma_connman_ifupdown(const char *interface, ofono_bool_t active)
+{
+       struct ifreq ifr;
+       int sk;
+
+       DBG("");
+
+       if (interface == NULL)
+               return;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0)
+               goto done;
+
+       if (active == TRUE) {
+               if (ifr.ifr_flags & IFF_UP)
+                       goto done;
+               ifr.ifr_flags |= IFF_UP;
+       } else {
+               if (!(ifr.ifr_flags & IFF_UP))
+                       goto done;
+               ifr.ifr_flags &= ~IFF_UP;
+       }
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
+               ofono_error("Failed to change interface flags");
+
+done:
+       close(sk);
+}
+
+static void cdma_connman_settings_append_variant(
+                                       struct cdma_connman_settings *settings,
+                                       DBusMessageIter *iter)
+{
+       DBusMessageIter variant;
+       DBusMessageIter array;
+       char typesig[5];
+       char arraysig[6];
+       const char *method;
+
+       DBG("");
+
+       arraysig[0] = DBUS_TYPE_ARRAY;
+       arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+       arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
+       arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT;
+       arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
+       arraysig[5] = typesig[4] = '\0';
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               arraysig, &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                                               typesig, &array);
+
+       if (settings == NULL)
+               goto done;
+
+       ofono_dbus_dict_append(&array, "Interface",
+                               DBUS_TYPE_STRING, &settings->interface);
+
+       if (settings->static_ip == TRUE)
+               method = "static";
+       else
+               method = "dhcp";
+
+       ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method);
+
+       if (settings->ip)
+               ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
+                                       &settings->ip);
+
+       if (settings->netmask)
+               ofono_dbus_dict_append(&array, "Netmask", DBUS_TYPE_STRING,
+                                       &settings->netmask);
+
+       if (settings->gateway)
+               ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING,
+                                       &settings->gateway);
+
+       if (settings->dns)
+               ofono_dbus_dict_append_array(&array, "DomainNameServers",
+                                               DBUS_TYPE_STRING,
+                                               &settings->dns);
+
+done:
+       dbus_message_iter_close_container(&variant, &array);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+static void cdma_connman_settings_signal(struct ofono_cdma_connman *cm)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       const char *prop = "Settings";
+
+       DBG("");
+
+       path = __ofono_atom_get_path(cm->atom);
+
+       signal = dbus_message_new_signal(path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &prop);
+
+       cdma_connman_settings_append_variant(cm->settings, &iter);
+
+       g_dbus_send_message(conn, signal);
+}
+
+static void cdma_connman_settings_update(struct ofono_cdma_connman *cm,
+                                       const char *interface,
+                                       ofono_bool_t static_ip,
+                                       const char *ip, const char *netmask,
+                                       const char *gateway, const char **dns)
+{
+       DBG("");
+
+       if (cm->settings)
+               cdma_connman_settings_free(cm->settings);
+
+       cm->settings = g_try_new0(struct cdma_connman_settings, 1);
+       if (cm->settings == NULL)
+               return;
+
+       cm->settings->interface = g_strdup(interface);
+       cm->settings->static_ip = static_ip;
+       cm->settings->ip = g_strdup(ip);
+       cm->settings->netmask = g_strdup(netmask);
+       cm->settings->gateway = g_strdup(gateway);
+       cm->settings->dns = g_strdupv((char **)dns);
+
+       cdma_connman_ifupdown(interface, TRUE);
+
+       cdma_connman_settings_signal(cm);
+}
+
+static void cdma_connman_settings_reset(struct ofono_cdma_connman *cm)
+{
+       char *interface;
+
+       DBG("");
+
+       if (cm->settings == NULL)
+               return;
+
+       interface = cm->settings->interface;
+       cm->settings->interface = NULL;
+
+       cdma_connman_settings_free(cm->settings);
+       cm->settings = NULL;
+
+       cdma_connman_settings_signal(cm);
+
+       cdma_connman_ifupdown(interface, FALSE);
+
+       g_free(interface);
+}
+
+static void activate_callback(const struct ofono_error *error,
+                               const char *interface,
+                               ofono_bool_t static_ip,
+                               const char *ip, const char *netmask,
+                               const char *gateway, const char **dns,
+                               void *data)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_cdma_connman *cm = data;
+       dbus_bool_t value;
+       const char *path;
+
+       DBG("%p %s", cm, interface);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Activating packet data service failed with error: %s",
+                               telephony_error_to_str(error));
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               return;
+       }
+
+       cm->powered = TRUE;
+       __ofono_dbus_pending_reply(&cm->pending,
+                               dbus_message_new_method_return(cm->pending));
+
+       /*
+        * If we don't have the interface, don't bother emitting any settings,
+        * as nobody can make use of them
+        */
+       if (interface != NULL)
+               cdma_connman_settings_update(cm, interface, static_ip,
+                                               ip, netmask, gateway, dns);
+
+       path = __ofono_atom_get_path(cm->atom);
+       value = cm->powered;
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void deactivate_callback(const struct ofono_error *error, void *data)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_cdma_connman *cm = data;
+       dbus_bool_t value;
+       const char *path;
+
+       DBG("");
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Deactivating packet data service failed with error: %s",
+                               telephony_error_to_str(error));
+               __ofono_dbus_pending_reply(&cm->pending,
+                                       __ofono_error_failed(cm->pending));
+               return;
+       }
+
+       cm->powered = FALSE;
+       __ofono_dbus_pending_reply(&cm->pending,
+                               dbus_message_new_method_return(cm->pending));
+
+       cdma_connman_settings_reset(cm);
+
+       path = __ofono_atom_get_path(cm->atom);
+       value = cm->powered;
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void cdma_connman_settings_append_properties(
+                                               struct ofono_cdma_connman *cm,
+                                               DBusMessageIter *dict)
+{
+       DBusMessageIter entry;
+       const char *key = "Settings";
+
+       DBG("");
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       cdma_connman_settings_append_variant(cm->settings, &entry);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static ofono_bool_t network_registered(struct ofono_cdma_connman *cm)
+{
+       int status;
+       struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
+       struct ofono_cdma_netreg *cdma_netreg;
+
+       cdma_netreg = __ofono_atom_find(OFONO_ATOM_TYPE_CDMA_NETREG, modem);
+       if (cdma_netreg == NULL)
+               return FALSE;
+
+       status = ofono_cdma_netreg_get_status(cdma_netreg);
+
+       switch (status) {
+       case NETWORK_REGISTRATION_STATUS_REGISTERED:
+       case NETWORK_REGISTRATION_STATUS_ROAMING:
+               return TRUE;
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+static DBusMessage *cdma_connman_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_connman *cm = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t value;
+
+       DBG("");
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       value = cm->powered;
+       ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);
+
+       value = cm->dormant;
+       ofono_dbus_dict_append(&dict, "Dormant", DBUS_TYPE_BOOLEAN, &value);
+
+       if (cm->settings)
+               cdma_connman_settings_append_properties(cm, &dict);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *cdma_connman_set_username(struct ofono_cdma_connman *cm,
+                                       DBusConnection *conn, DBusMessage *msg,
+                                       const char *username)
+{
+       const char *path;
+
+       if (strlen(username) > OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (g_str_equal(username, cm->username))
+               return dbus_message_new_method_return(msg);
+
+       strcpy(cm->username, username);
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       path = __ofono_atom_get_path(cm->atom);
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               "Username", DBUS_TYPE_STRING, &username);
+
+       return NULL;
+}
+
+static DBusMessage *cdma_connman_set_password(struct ofono_cdma_connman *cm,
+                                       DBusConnection *conn, DBusMessage *msg,
+                                       const char *password)
+{
+       const char *path;
+
+       if (strlen(password) > OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (g_str_equal(password, cm->password))
+               return dbus_message_new_method_return(msg);
+
+       strcpy(cm->password, password);
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       path = __ofono_atom_get_path(cm->atom);
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               "Password", DBUS_TYPE_STRING, &password);
+
+       return NULL;
+}
+
+static DBusMessage *cdma_connman_set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_connman *cm = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+       dbus_bool_t value;
+       const char *str;
+
+       DBG("");
+
+       if (cm->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (!strcmp(property, "Powered")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (cm->powered == (ofono_bool_t) value)
+                       return dbus_message_new_method_return(msg);
+
+               if (cm->driver == NULL || cm->driver->activate == NULL ||
+                               cm->driver->deactivate == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (network_registered(cm) == FALSE)
+                       return __ofono_error_not_registered(msg);
+
+               cm->pending = dbus_message_ref(msg);
+
+               if (value)
+                       cm->driver->activate(cm, cm->username, cm->password,
+                                               activate_callback, cm);
+               else
+                       cm->driver->deactivate(cm, deactivate_callback, cm);
+
+               return NULL;
+       } else if (!strcmp(property, "Username")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+               return cdma_connman_set_username(cm, conn, msg, str);
+       } else if (!strcmp(property, "Password")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+               return cdma_connman_set_password(cm, conn, msg, str);
+       }
+
+       /* TODO: Dormant property. Not yet supported. */
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable cdma_connman_methods[] = {
+       { "GetProperties",      "",     "a{sv}",
+                                               cdma_connman_get_properties },
+       { "SetProperty",        "sv",   "",     cdma_connman_set_property,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable cdma_connman_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+int ofono_cdma_connman_driver_register(
+                               const struct ofono_cdma_connman_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_cdma_connman_driver_unregister(
+                               const struct ofono_cdma_connman_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+void ofono_cdma_connman_deactivated(struct ofono_cdma_connman *cm)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       ofono_bool_t value;
+       const char *path;
+
+       if (cm == NULL)
+               return;
+
+       cdma_connman_settings_reset(cm);
+       cm->powered = FALSE;
+       value = cm->powered;
+       path = __ofono_atom_get_path(cm->atom);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN, &value);
+}
+
+void ofono_cdma_connman_dormant_notify(struct ofono_cdma_connman *cm,
+                                       ofono_bool_t dormant)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+
+       if (cm == NULL)
+               return;
+
+       cm->dormant = dormant;
+       path = __ofono_atom_get_path(cm->atom);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               "Dormant", DBUS_TYPE_BOOLEAN, &dormant);
+}
+
+static void cdma_connman_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       DBG("");
+
+       g_dbus_unregister_interface(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
+       ofono_modem_remove_interface(modem,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
+}
+
+static void cdma_connman_remove(struct ofono_atom *atom)
+{
+       struct ofono_cdma_connman *cm = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cm == NULL)
+               return;
+
+       if (cm->driver && cm->driver->remove)
+               cm->driver->remove(cm);
+
+       g_free(cm);
+}
+
+struct ofono_cdma_connman *ofono_cdma_connman_create(
+                                               struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data)
+{
+       struct ofono_cdma_connman *cm;
+       GSList *l;
+
+       DBG("");
+
+       if (driver == NULL)
+               return NULL;
+
+       cm = g_try_new0(struct ofono_cdma_connman, 1);
+       if (cm == NULL)
+               return NULL;
+
+       cm->atom = __ofono_modem_add_atom(modem,
+                                       OFONO_ATOM_TYPE_CDMA_CONNMAN,
+                                       cdma_connman_remove, cm);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_cdma_connman_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cm, vendor, data) < 0)
+                       continue;
+
+               cm->driver = drv;
+               break;
+       }
+
+       return cm;
+}
+
+void ofono_cdma_connman_register(struct ofono_cdma_connman *cm)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom);
+       const char *path = __ofono_atom_get_path(cm->atom);
+
+       DBG("");
+
+       if (!g_dbus_register_interface(conn, path,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE,
+                               cdma_connman_methods, cdma_connman_signals,
+                               NULL, cm, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
+               return;
+       }
+
+       ofono_modem_add_interface(modem,
+                               OFONO_CDMA_CONNECTION_MANAGER_INTERFACE);
+
+       __ofono_atom_register(cm->atom, cdma_connman_unregister);
+}
+
+void ofono_cdma_connman_remove(struct ofono_cdma_connman *cm)
+{
+       __ofono_atom_free(cm->atom);
+}
+
+void ofono_cdma_connman_set_data(struct ofono_cdma_connman *cm, void *data)
+{
+       cm->driver_data = data;
+}
+
+void *ofono_cdma_connman_get_data(struct ofono_cdma_connman *cm)
+{
+       return cm->driver_data;
+}
diff --git a/src/cdma-netreg.c b/src/cdma-netreg.c
new file mode 100644 (file)
index 0000000..1a88d33
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "ofono.h"
+
+static GSList *g_drivers;
+
+struct ofono_cdma_netreg {
+       enum cdma_netreg_status status;
+       int strength;
+       int hdr_strength;
+       const struct ofono_cdma_netreg_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       char *provider_name;
+       char *sid;
+};
+
+static const char *cdma_netreg_status_to_string(enum cdma_netreg_status status)
+{
+       switch (status) {
+       case CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED:
+               return "unregistered";
+       case CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED:
+               return "registered";
+       case CDMA_NETWORK_REGISTRATION_STATUS_ROAMING:
+               return "roaming";
+       }
+
+       return "";
+}
+
+static DBusMessage *network_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_netreg *cdma_netreg = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       const char *status = cdma_netreg_status_to_string(cdma_netreg->status);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status);
+
+       if (cdma_netreg->strength != -1) {
+               unsigned char strength = cdma_netreg->strength;
+
+               ofono_dbus_dict_append(&dict, "Strength", DBUS_TYPE_BYTE,
+                                       &strength);
+       }
+
+       if (cdma_netreg->hdr_strength != -1) {
+               unsigned char strength = cdma_netreg->hdr_strength;
+
+               ofono_dbus_dict_append(&dict, "DataStrength", DBUS_TYPE_BYTE,
+                                       &strength);
+       }
+
+       if (cdma_netreg->sid)
+               ofono_dbus_dict_append(&dict, "SystemIdentifier",
+                                               DBUS_TYPE_STRING,
+                                               &cdma_netreg->sid);
+
+       if (cdma_netreg->provider_name)
+               ofono_dbus_dict_append(&dict, "Name", DBUS_TYPE_STRING,
+                                               &cdma_netreg->provider_name);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static GDBusMethodTable cdma_netreg_manager_methods[] = {
+       { "GetProperties",  "",  "a{sv}",       network_get_properties },
+       { }
+};
+
+static GDBusSignalTable cdma_netreg_manager_signals[] = {
+       { }
+};
+
+static void serving_system_callback(const struct ofono_error *error,
+                                       const char *sid, void *data)
+{
+       struct ofono_cdma_netreg *cdma_netreg = data;
+       const char *path = __ofono_atom_get_path(cdma_netreg->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (cdma_netreg->status != CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED
+                       && cdma_netreg->status !=
+                               CDMA_NETWORK_REGISTRATION_STATUS_ROAMING)
+               return;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during serving system query");
+               return;
+       }
+
+       DBG("Serving system Identifier: %s", sid);
+
+       if (cdma_netreg->sid != NULL && !strcmp(cdma_netreg->sid, sid))
+               return;
+
+       g_free(cdma_netreg->provider_name);
+       g_free(cdma_netreg->sid);
+       cdma_netreg->provider_name = NULL;
+       cdma_netreg->sid = g_strdup(sid);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE,
+                               "SystemIdentifier", DBUS_TYPE_STRING,
+                               &cdma_netreg->sid);
+
+       if (__ofono_cdma_provision_get_name(sid,
+                               &cdma_netreg->provider_name) == FALSE) {
+               ofono_warn("Provider name not found");
+               return;
+       }
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE,
+                               "Name", DBUS_TYPE_STRING,
+                               &cdma_netreg->provider_name);
+}
+
+static void set_registration_status(struct ofono_cdma_netreg *cdma_netreg,
+                                               enum cdma_netreg_status status)
+{
+       const char *str_status = cdma_netreg_status_to_string(status);
+       const char *path = __ofono_atom_get_path(cdma_netreg->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       cdma_netreg->status = status;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE,
+                               "Status", DBUS_TYPE_STRING,
+                               &str_status);
+
+       if (cdma_netreg->status == CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED
+                       || cdma_netreg->status ==
+                               CDMA_NETWORK_REGISTRATION_STATUS_ROAMING)
+               if (cdma_netreg->driver->serving_system != NULL)
+                       cdma_netreg->driver->serving_system(cdma_netreg,
+                               serving_system_callback, cdma_netreg);
+}
+
+void ofono_cdma_netreg_status_notify(struct ofono_cdma_netreg *cdma_netreg,
+                                       enum cdma_netreg_status status)
+{
+       if (cdma_netreg == NULL)
+               return;
+
+       if (cdma_netreg->status != status)
+               set_registration_status(cdma_netreg, status);
+}
+
+static void strength_notify_common(struct ofono_cdma_netreg *netreg,
+                                       int strength, const char *property,
+                                       int *dest)
+{
+       if (netreg == NULL)
+               return;
+
+       if (*dest == strength)
+               return;
+
+       /*
+        * Theoretically we can get signal strength even when not registered
+        * to any network.  However, what do we do with it in that case?
+        */
+       if (netreg->status == CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED)
+               return;
+
+       *dest = strength;
+
+       if (strength != -1) {
+               DBusConnection *conn = ofono_dbus_get_connection();
+               const char *path = __ofono_atom_get_path(netreg->atom);
+               unsigned char val = strength;
+
+               ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE,
+                               property, DBUS_TYPE_BYTE, &val);
+       }
+}
+
+void ofono_cdma_netreg_strength_notify(struct ofono_cdma_netreg *netreg,
+                                       int strength)
+{
+       return strength_notify_common(netreg, strength,
+                                       "Strength", &netreg->strength);
+}
+
+void ofono_cdma_netreg_data_strength_notify(struct ofono_cdma_netreg *netreg,
+                                               int data_strength)
+{
+       return strength_notify_common(netreg, data_strength,
+                                       "DataStrength", &netreg->hdr_strength);
+}
+
+int ofono_cdma_netreg_get_status(struct ofono_cdma_netreg *netreg)
+{
+       if (netreg == NULL)
+               return -1;
+
+       return netreg->status;
+}
+
+int ofono_cdma_netreg_driver_register(const struct ofono_cdma_netreg_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *)d);
+
+       return 0;
+}
+
+void ofono_cdma_netreg_driver_unregister(
+                               const struct ofono_cdma_netreg_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *)d);
+}
+
+static void cdma_netreg_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       g_dbus_unregister_interface(conn, path,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE);
+
+       ofono_modem_remove_interface(modem,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE);
+}
+
+static void cdma_netreg_remove(struct ofono_atom *atom)
+{
+       struct ofono_cdma_netreg *cdma_netreg = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cdma_netreg == NULL)
+               return;
+
+       if (cdma_netreg->driver && cdma_netreg->driver->remove)
+               cdma_netreg->driver->remove(cdma_netreg);
+
+       g_free(cdma_netreg->sid);
+       g_free(cdma_netreg->provider_name);
+       g_free(cdma_netreg);
+}
+
+struct ofono_cdma_netreg *ofono_cdma_netreg_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data)
+{
+       struct ofono_cdma_netreg *cdma_netreg;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cdma_netreg = g_try_new0(struct ofono_cdma_netreg, 1);
+       if (cdma_netreg == NULL)
+               return NULL;
+
+       cdma_netreg->status = CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+       cdma_netreg->strength = -1;
+       cdma_netreg->hdr_strength = -1;
+
+       cdma_netreg->atom = __ofono_modem_add_atom(modem,
+                                       OFONO_ATOM_TYPE_CDMA_NETREG,
+                                       cdma_netreg_remove, cdma_netreg);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_cdma_netreg_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cdma_netreg, vendor, data) < 0)
+                       continue;
+
+               cdma_netreg->driver = drv;
+               break;
+       }
+
+       return cdma_netreg;
+}
+
+void ofono_cdma_netreg_register(struct ofono_cdma_netreg *cdma_netreg)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cdma_netreg->atom);
+       const char *path = __ofono_atom_get_path(cdma_netreg->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE,
+                               cdma_netreg_manager_methods,
+                               cdma_netreg_manager_signals,
+                               NULL, cdma_netreg, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE);
+               return;
+       }
+
+       ofono_modem_add_interface(modem,
+                               OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE);
+
+       __ofono_atom_register(cdma_netreg->atom, cdma_netreg_unregister);
+}
+
+void ofono_cdma_netreg_remove(struct ofono_cdma_netreg *cdma_netreg)
+{
+       __ofono_atom_free(cdma_netreg->atom);
+}
+
+void ofono_cdma_netreg_set_data(struct ofono_cdma_netreg *cdma_netreg,
+                                       void *data)
+{
+       cdma_netreg->driver_data = data;
+}
+
+void *ofono_cdma_netreg_get_data(struct ofono_cdma_netreg *cdma_netreg)
+{
+       return cdma_netreg->driver_data;
+}
diff --git a/src/cdma-provision.c b/src/cdma-provision.c
new file mode 100644 (file)
index 0000000..33b31b0
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include "ofono.h"
+
+static GSList *g_drivers = NULL;
+
+ofono_bool_t __ofono_cdma_provision_get_name(const char *sid, char **name)
+{
+       GSList *d;
+
+       if (sid == NULL || strlen(sid) == 0)
+               return FALSE;
+
+       for (d = g_drivers; d != NULL; d = d->next) {
+               const struct ofono_cdma_provision_driver *driver = d->data;
+
+               if (driver->get_provider_name == NULL)
+                       continue;
+
+               DBG("Calling cdma provision plugin '%s'", driver->name);
+
+               if (driver->get_provider_name(sid, name) < 0)
+                       continue;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_cdma_provision_driver *plugin1 = a;
+       const struct ofono_cdma_provision_driver *plugin2 = b;
+
+       return plugin2->priority - plugin1->priority;
+}
+
+int ofono_cdma_provision_driver_register(
+               const struct ofono_cdma_provision_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       g_drivers = g_slist_insert_sorted(g_drivers, (void *) driver,
+                                               compare_priority);
+       return 0;
+}
+
+void ofono_cdma_provision_driver_unregister(
+               const struct ofono_cdma_provision_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       g_drivers = g_slist_remove(g_drivers, driver);
+}
diff --git a/src/cdma-sms.c b/src/cdma-sms.c
new file mode 100644 (file)
index 0000000..12ea57e
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+#include <sys/time.h>
+
+#include "ofono.h"
+
+#include "cdma-smsutil.h"
+
+static GSList *g_drivers;
+
+struct ofono_cdma_sms {
+       const struct ofono_cdma_sms_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static GDBusMethodTable cdma_sms_manager_methods[] = {
+       /* TODO */
+       { }
+};
+
+static GDBusSignalTable cdma_sms_manager_signals[] = {
+       { "IncomingMessage",    "sa{sv}"        },
+       /* TODO */
+       { }
+};
+
+static void cdma_dispatch_text_message(struct ofono_cdma_sms *cdma_sms,
+                                       const char *message,
+                                       const char *oaddr)
+{
+       const char *path = __ofono_atom_get_path(cdma_sms->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *signal_name;
+
+       /* TODO: Support ImmediateMessage */
+       signal_name = "IncomingMessage";
+
+       signal = dbus_message_new_signal(path,
+                                       OFONO_CDMA_MESSAGE_MANAGER_INTERFACE,
+                                       signal_name);
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &oaddr);
+
+       /* TODO: Other properties not supported yet */
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(conn, signal);
+
+       /*TODO: Add the message to history*/
+}
+
+static void ofono_cdma_sms_process_wmt_deliver(struct ofono_cdma_sms *cdma_sms,
+                                               const struct cdma_sms *incoming)
+{
+       char *message;
+       const char *oaddr;
+       const struct cdma_sms_ud *ud;
+
+       ud = &incoming->p2p_msg.bd.wmt_deliver.ud;
+
+       /*
+        * If incoming message does not contain USER DATA, still
+        * send indication to upper layer but with empty string.
+        */
+       if (check_bitmap(incoming->p2p_msg.bd.subparam_bitmap,
+                               CDMA_SMS_SUBPARAM_ID_USER_DATA) == FALSE)
+               message = g_new0(char, 1);
+       else
+               message = cdma_sms_decode_text(ud);
+
+       if (message == NULL)
+               return;
+
+       oaddr = cdma_sms_address_to_string(&incoming->p2p_msg.oaddr);
+       if (oaddr == NULL) {
+               g_free(message);
+               return;
+       }
+
+       cdma_dispatch_text_message(cdma_sms, message, oaddr);
+
+       g_free(message);
+}
+
+static void ofono_cdma_sms_process_wmt(struct ofono_cdma_sms *cdma_sms,
+                                       struct cdma_sms *incoming)
+{
+       /* TODO: Add duplicate detection support */
+
+       switch (incoming->p2p_msg.bd.id.msg_type) {
+       case CDMA_SMS_MSG_TYPE_RESERVED:
+               break;
+       case CDMA_SMS_MSG_TYPE_DELIVER:
+               ofono_cdma_sms_process_wmt_deliver(cdma_sms, incoming);
+               break;
+       case CDMA_SMS_MSG_TYPE_SUBMIT:
+       case CDMA_SMS_MSG_TYPE_CANCEL:
+       case CDMA_SMS_MSG_TYPE_DELIVER_ACK:
+       case CDMA_SMS_MSG_TYPE_USER_ACK:
+       case CDMA_SMS_MSG_TYPE_READ_ACK:
+       case CDMA_SMS_MSG_TYPE_DELIVER_REPORT:
+       case CDMA_SMS_MSG_TYPE_SUBMIT_REPORT:
+               /* TODO */
+               break;
+       }
+}
+
+static void ofono_cdma_sms_process_p2p(struct ofono_cdma_sms *cdma_sms,
+                                       struct cdma_sms *incoming)
+{
+       switch (incoming->p2p_msg.teleservice_id) {
+       case CDMA_SMS_TELESERVICE_ID_CMT91:
+       case CDMA_SMS_TELESERVICE_ID_WPT:
+               break; /* TODO: Not supported yet */
+       case CDMA_SMS_TELESERVICE_ID_WMT:
+               ofono_cdma_sms_process_wmt(cdma_sms, incoming);
+               break;
+       case CDMA_SMS_TELESERVICE_ID_VMN:
+       case CDMA_SMS_TELESERVICE_ID_WAP:
+       case CDMA_SMS_TELESERVICE_ID_WEMT:
+       case CDMA_SMS_TELESERVICE_ID_SCPT:
+       case CDMA_SMS_TELESERVICE_ID_CATPT:
+               break; /* TODO: Not supported yet */
+       }
+}
+
+void ofono_cdma_sms_deliver_notify(struct ofono_cdma_sms *cdma_sms,
+                                       unsigned char *pdu, int tpdu_len)
+{
+       static struct cdma_sms s;
+
+       DBG("tpdu len %d", tpdu_len);
+
+       memset(&s, 0, sizeof(struct cdma_sms));
+
+       if (cdma_sms_decode(pdu, tpdu_len, &s) == FALSE)
+               return;
+
+       switch (s.type) {
+       case CDMA_SMS_TP_MSG_TYPE_P2P:
+               ofono_cdma_sms_process_p2p(cdma_sms, &s);
+               break;
+       case CDMA_SMS_TP_MSG_TYPE_BCAST:
+       case CDMA_SMS_TP_MSG_TYPE_ACK:
+               /*
+                * TODO: Support SMS Broadcast Message and SMS
+                * Acknowledge Message.
+                */
+               break;
+       }
+}
+
+int ofono_cdma_sms_driver_register(const struct ofono_cdma_sms_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *)d);
+
+       return 0;
+}
+
+void ofono_cdma_sms_driver_unregister(const struct ofono_cdma_sms_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *)d);
+}
+
+static void cdma_sms_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+
+       ofono_modem_remove_interface(modem,
+                                       OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+}
+
+static void cdma_sms_remove(struct ofono_atom *atom)
+{
+       struct ofono_cdma_sms *cdma_sms = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (cdma_sms == NULL)
+               return;
+
+       if (cdma_sms->driver && cdma_sms->driver->remove)
+               cdma_sms->driver->remove(cdma_sms);
+
+       g_free(cdma_sms);
+}
+
+/*
+ * Create a CDMA SMS driver
+ *
+ * This creates a CDMA SMS driver that is hung off a @modem
+ * object. However, for the driver to be used by the system, it has to
+ * be registered with the oFono core using ofono_sms_register().
+ *
+ * This is done once the modem driver determines that SMS is properly
+ * supported by the hardware.
+ */
+struct ofono_cdma_sms *ofono_cdma_sms_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data)
+{
+       struct ofono_cdma_sms *cdma_sms;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       cdma_sms = g_try_new0(struct ofono_cdma_sms, 1);
+       if (cdma_sms == NULL)
+               return NULL;
+
+       cdma_sms->atom = __ofono_modem_add_atom(modem,
+                                               OFONO_ATOM_TYPE_CDMA_SMS,
+                                               cdma_sms_remove, cdma_sms);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_cdma_sms_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(cdma_sms, vendor, data) < 0)
+                       continue;
+
+               cdma_sms->driver = drv;
+               break;
+       }
+
+       return cdma_sms;
+}
+
+/*
+ * Indicate oFono that a CDMA SMS driver is ready for operation
+ *
+ * This is called after ofono_cdma_sms_create() was done and the modem
+ * driver determined that a modem supports SMS correctly. Once this
+ * call succeeds, the D-BUS interface for SMS goes live.
+ */
+void ofono_cdma_sms_register(struct ofono_cdma_sms *cdma_sms)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(cdma_sms->atom);
+       const char *path = __ofono_atom_get_path(cdma_sms->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CDMA_MESSAGE_MANAGER_INTERFACE,
+                                       cdma_sms_manager_methods,
+                                       cdma_sms_manager_signals,
+                                       NULL, cdma_sms, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+
+       __ofono_atom_register(cdma_sms->atom, cdma_sms_unregister);
+}
+
+void ofono_cdma_sms_remove(struct ofono_cdma_sms *cdma_sms)
+{
+       __ofono_atom_free(cdma_sms->atom);
+}
+
+void ofono_cdma_sms_set_data(struct ofono_cdma_sms *cdma_sms, void *data)
+{
+       cdma_sms->driver_data = data;
+}
+
+void *ofono_cdma_sms_get_data(struct ofono_cdma_sms *cdma_sms)
+{
+       return cdma_sms->driver_data;
+}
diff --git a/src/cdma-smsutil.c b/src/cdma-smsutil.c
new file mode 100644 (file)
index 0000000..e36f2e3
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "cdma-smsutil.h"
+
+#define uninitialized_var(x) x = x
+
+enum cdma_sms_rec_flag {
+       CDMA_SMS_REC_FLAG_MANDATORY =   1,
+};
+
+typedef gboolean (*rec_handler)(const guint8 *, guint8, void *);
+
+struct simple_iter {
+       guint8 max;
+       const guint8 *pdu;
+       guint8 pos;
+       guint8 id;
+       guint8 len;
+       const guint8 *data;
+};
+
+static void simple_iter_init(struct simple_iter *iter,
+                               const guint8 *pdu, guint8 len)
+{
+       iter->pdu = pdu;
+       iter->max = len;
+       iter->pos = 0;
+       iter->id = 0;
+       iter->len = 0;
+       iter->data = NULL;
+}
+
+static gboolean simple_iter_next(struct simple_iter *iter)
+{
+       const guint8 *pdu = iter->pdu + iter->pos;
+       const guint8 *end = iter->pdu + iter->max;
+       guint8 id;
+       guint8 len;
+
+       if (pdu == end)
+               return FALSE;
+
+       id = *pdu;
+       pdu++;
+
+       if (pdu == end)
+               return FALSE;
+
+       len = *pdu++;
+
+       if (pdu + len > end)
+               return FALSE;
+
+       iter->id = id;
+       iter->len = len;
+       iter->data = pdu;
+
+       iter->pos = pdu + len - iter->pdu;
+
+       return TRUE;
+}
+
+static guint8 simple_iter_get_id(struct simple_iter *iter)
+{
+       return iter->id;
+}
+
+static guint8 simple_iter_get_length(struct simple_iter *iter)
+{
+       return iter->len;
+}
+
+static const guint8 *simple_iter_get_data(struct simple_iter *iter)
+{
+       return iter->data;
+}
+
+static inline void set_bitmap(guint32 *bitmap, guint8 pos)
+{
+       *bitmap = *bitmap | (1 << pos);
+}
+
+/* Unpacks the byte stream. The field has to be <= 8 bits. */
+static guint8 bit_field_unpack(const guint8 *buf, guint16 offset, guint8 nbit)
+{
+       guint8 bit_pos;
+       guint8 val = 0;
+       const guint8 *pdu;
+
+       pdu = buf + (offset >> 3);
+       bit_pos = 8 - (offset & 0x7);
+
+       /* Field to be extracted is within current byte */
+       if (nbit <= bit_pos)
+               return (*pdu >> (bit_pos - nbit)) & ((1 << nbit) - 1);
+
+       /* Field to be extracted crossing two bytes */
+       val = *pdu & ((1 << bit_pos) - 1);
+       nbit -= bit_pos;
+       pdu++;
+
+       return (val << nbit) | (*pdu >> (8 - nbit));
+}
+
+/* Convert CDMA DTMF digits into a string */
+static gboolean dtmf_to_ascii(char *buf, const guint8 *addr,
+                                       guint8 num_fields)
+{
+       /*
+        * Mapping from binary DTMF code to the digit it represents.
+        * As defined in Table 2.7.1.3.2.4-4 of 3GPP2 C.S0005-E v2.0.
+        * Note, 0 is NOT a valid value and not mapped to
+        * any valid DTMF digit.
+        */
+       static const char dtmf_digits[13] = {0, '1', '2', '3', '4', '5', '6',
+                                               '7', '8', '9', '0', '*', '#'};
+       guint8 index;
+       guint8 value;
+
+       for (index = 0; index < num_fields; index++) {
+               if (addr[index] == 0 || addr[index] > 12)
+                       return FALSE;  /* Invalid digit in address field */
+
+               value = addr[index];
+               buf[index] = dtmf_digits[value];
+       }
+
+       buf[index] = 0; /* Make it NULL terminated string */
+
+       return TRUE;
+}
+
+const char *cdma_sms_address_to_string(const struct cdma_sms_address *addr)
+{
+       static char buf[CDMA_SMS_MAX_ADDR_FIELDS + 1];
+
+       /* TODO: Only support CDMA_SMS_DIGIT_MODE_4BIT_DTMF currently */
+       switch (addr->digit_mode) {
+       case CDMA_SMS_DIGIT_MODE_4BIT_DTMF:
+               if (dtmf_to_ascii(buf, addr->address,
+                                       addr->num_fields) == TRUE)
+                       return buf;
+               else
+                       return NULL;
+       case CDMA_SMS_DIGIT_MODE_8BIT_ASCII:
+               return NULL;
+       }
+
+       return NULL;
+}
+
+/* Decode Teleservice ID */
+static gboolean cdma_sms_decode_teleservice(const guint8 *buf, guint8 len,
+                                                               void *data)
+{
+       enum cdma_sms_teleservice_id *id = data;
+
+       *id = bit_field_unpack(buf, 0, 8) << 8 |
+                               bit_field_unpack(buf, 8, 8);
+
+       switch (*id) {
+       case CDMA_SMS_TELESERVICE_ID_CMT91:
+       case CDMA_SMS_TELESERVICE_ID_WPT:
+       case CDMA_SMS_TELESERVICE_ID_WMT:
+       case CDMA_SMS_TELESERVICE_ID_VMN:
+       case CDMA_SMS_TELESERVICE_ID_WAP:
+       case CDMA_SMS_TELESERVICE_ID_WEMT:
+       case CDMA_SMS_TELESERVICE_ID_SCPT:
+       case CDMA_SMS_TELESERVICE_ID_CATPT:
+               return TRUE;
+       }
+
+       return FALSE; /* Invalid teleservice type */
+}
+
+/* Decode Address parameter record */
+static gboolean cdma_sms_decode_addr(const guint8 *buf, guint8 len,
+                                                       void *data)
+{
+       struct cdma_sms_address *addr = data;
+       guint16 bit_offset = 0;
+       guint8  chari_len;
+       guint16 total_num_bits = len * 8;
+       guint8  index;
+
+       addr->digit_mode = bit_field_unpack(buf, bit_offset, 1);
+       bit_offset += 1;
+
+       addr->number_mode = bit_field_unpack(buf, bit_offset, 1);
+       bit_offset += 1;
+
+       if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_8BIT_ASCII) {
+               if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT)
+                       addr->digi_num_type =
+                               bit_field_unpack(buf, bit_offset, 3);
+               else
+                       addr->data_nw_num_type =
+                               bit_field_unpack(buf, bit_offset, 3);
+
+               bit_offset += 3;
+
+               if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT) {
+                       if (bit_offset + 4 > total_num_bits)
+                               return FALSE;
+
+                       addr->number_plan =
+                               bit_field_unpack(buf, bit_offset, 4);
+                       bit_offset += 4;
+               }
+       }
+
+       if (bit_offset + 8 > total_num_bits)
+               return FALSE;
+
+       addr->num_fields = bit_field_unpack(buf, bit_offset, 8);
+       bit_offset += 8;
+
+       if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_4BIT_DTMF)
+               chari_len = 4;
+       else
+               chari_len = 8;
+
+       if ((bit_offset + chari_len * addr->num_fields) > total_num_bits)
+               return FALSE;
+
+       for (index = 0; index < addr->num_fields; index++) {
+               addr->address[index] = bit_field_unpack(buf,
+                                                       bit_offset,
+                                                       chari_len);
+               bit_offset += chari_len;
+       }
+
+       return TRUE;
+}
+
+static char *decode_text_7bit_ascii(const struct cdma_sms_ud *ud)
+{
+       char *buf;
+
+       buf = g_new(char, ud->num_fields + 1);
+       if (buf == NULL)
+               return NULL;
+
+       memcpy(buf, ud->chari, ud->num_fields);
+       buf[ud->num_fields] = 0; /* Make it NULL terminated string */
+
+       return buf;
+}
+
+char *cdma_sms_decode_text(const struct cdma_sms_ud *ud)
+{
+       switch (ud->msg_encoding) {
+       case CDMA_SMS_MSG_ENCODING_OCTET:
+       case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG:
+               return NULL; /* TODO */
+       case CDMA_SMS_MSG_ENCODING_7BIT_ASCII:
+               return decode_text_7bit_ascii(ud);
+       case CDMA_SMS_MSG_ENCODING_IA5:
+       case CDMA_SMS_MSG_ENCODING_UNICODE:
+       case CDMA_SMS_MSG_ENCODING_SHIFT_JIS:
+       case CDMA_SMS_MSG_ENCODING_KOREAN:
+       case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW:
+       case CDMA_SMS_MSG_ENCODING_LATIN:
+       case CDMA_SMS_MSG_ENCODING_GSM_7BIT:
+       case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING:
+               return NULL; /* TODO */
+       }
+
+       return NULL;
+}
+
+/* Decode User Data */
+static gboolean cdma_sms_decode_ud(const guint8 *buf, guint8 len, void *data)
+{
+       guint16 bit_offset = 0;
+       guint8  chari_len = 0;
+       guint16 total_num_bits = len * 8;
+       guint8  index;
+       enum cdma_sms_msg_encoding  msg_encoding;
+       struct cdma_sms_ud *ud = data;
+
+       if (total_num_bits < 13)
+               return FALSE;
+
+       msg_encoding = bit_field_unpack(buf, bit_offset, 5);
+       ud->msg_encoding =  msg_encoding;
+       bit_offset += 5;
+
+       if (msg_encoding == CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG ||
+               msg_encoding == CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING) {
+               /*
+                * Skip message type field for now.
+                * TODO: Add support for message type field.
+                */
+               bit_offset += 8;
+       }
+
+       if (bit_offset + 8 > total_num_bits)
+               return FALSE;
+
+       ud->num_fields = bit_field_unpack(buf, bit_offset, 8);
+       bit_offset += 8;
+
+       switch (msg_encoding) {
+       case CDMA_SMS_MSG_ENCODING_OCTET:
+               chari_len = 8;
+               break;
+       case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG:
+               return FALSE; /* TODO */
+       case CDMA_SMS_MSG_ENCODING_7BIT_ASCII:
+       case CDMA_SMS_MSG_ENCODING_IA5:
+               chari_len = 7;
+               break;
+       case CDMA_SMS_MSG_ENCODING_UNICODE:
+       case CDMA_SMS_MSG_ENCODING_SHIFT_JIS:
+       case CDMA_SMS_MSG_ENCODING_KOREAN:
+               return FALSE; /* TODO */
+       case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW:
+       case CDMA_SMS_MSG_ENCODING_LATIN:
+               chari_len = 8;
+               break;
+       case CDMA_SMS_MSG_ENCODING_GSM_7BIT:
+               chari_len = 7;
+               break;
+       case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING:
+               return FALSE; /* TODO */
+       }
+
+       /* TODO: Add support for all other encoding types */
+       if (chari_len == 0)
+               return FALSE;
+
+       if (bit_offset + chari_len * ud->num_fields > total_num_bits)
+               return FALSE;
+
+       for (index = 0; index < ud->num_fields; index++) {
+               ud->chari[index] = bit_field_unpack(buf,
+                                               bit_offset,
+                                               chari_len);
+               bit_offset += chari_len;
+       }
+
+       return TRUE;
+}
+
+/* Decode Message Identifier */
+static gboolean cdma_sms_decode_message_id(const guint8 *buf, guint8 len,
+                                               void *data)
+{
+       struct cdma_sms_identifier *id = data;
+
+       if (len != 3)
+               return FALSE;
+
+       id->msg_type = bit_field_unpack(buf, 0, 4);
+
+       if (id->msg_type <= 0 ||
+                       id->msg_type > CDMA_SMS_MSG_TYPE_SUBMIT_REPORT)
+               return FALSE; /* Invalid message type */
+
+       id->msg_id = (bit_field_unpack(buf, 4, 8) << 8) |
+                       bit_field_unpack(buf, 12, 8);
+
+       id->header_ind = bit_field_unpack(buf, 20, 1);
+
+       return TRUE;
+}
+
+static gboolean find_and_decode(struct simple_iter *iter, guint8 rec_id,
+                                       rec_handler handler, void *data)
+{
+       guint8 id;
+       guint8 len;
+       const guint8 *buf;
+
+       while (simple_iter_next(iter) == TRUE) {
+               id = simple_iter_get_id(iter);
+               if (id != rec_id)
+                       continue;
+
+               len = simple_iter_get_length(iter);
+               buf = simple_iter_get_data(iter);
+
+               return handler(buf, len, data);
+       }
+
+       return FALSE;
+}
+
+static rec_handler subparam_handler_for_id(enum cdma_sms_subparam_id id)
+{
+       switch (id) {
+       case CDMA_SMS_SUBPARAM_ID_MESSAGE_ID:
+               return cdma_sms_decode_message_id;
+       case CDMA_SMS_SUBPARAM_ID_USER_DATA:
+               return cdma_sms_decode_ud;
+       case CDMA_SMS_SUBPARAM_ID_USER_RESPONSE_CODE:
+       case CDMA_SMS_SUBPARAM_ID_MC_TIME_STAMP:
+       case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_ABSOLUTE:
+       case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_RELATIVE:
+       case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE:
+       case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_RELATIVE:
+       case CDMA_SMS_SUBPARAM_ID_PRIORITY_INDICATOR:
+       case CDMA_SMS_SUBPARAM_ID_PRIVACY_INDICATOR:
+       case CDMA_SMS_SUBPARAM_ID_REPLY_OPTION:
+       case CDMA_SMS_SUBPARAM_ID_NUMBER_OF_MESSAGES:
+       case CDMA_SMS_SUBPARAM_ID_ALERT_ON_MESSAGE_DELIVERY:
+       case CDMA_SMS_SUBPARAM_ID_LANGUAGE_INDICATOR:
+       case CDMA_SMS_SUBPARAM_ID_CALL_BACK_NUMBER:
+       case CDMA_SMS_SUBPARAM_ID_MESSAGE_DISPLAY_MODE:
+       case CDMA_SMS_SUBPARAM_ID_MULTIPLE_ENCODING_USER_DATA:
+       case CDMA_SMS_SUBPARAM_ID_MESSAGE_DEPOSIT_INDEX:
+       case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_DATA:
+       case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_RESULT:
+       case CDMA_SMS_SUBPARAM_ID_MESSAGE_STATUS:
+       case CDMA_SMS_SUBPARAM_ID_TP_FAILURE_CAUSE:
+       case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN:
+       case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN_ACK:
+               return NULL; /* TODO */
+       }
+
+       return NULL;
+}
+
+struct subparam_handler_entry {
+       enum cdma_sms_subparam_id id;
+       int flags;
+       gboolean found;
+       void *data;
+};
+
+static gboolean decode_subparams(struct simple_iter *iter, guint32 *bitmap,
+                                       void *data, ...)
+{
+       GSList *entries = NULL;
+       GSList *l;
+       va_list args;
+       gboolean decode_result = TRUE;
+
+       va_start(args, data);
+
+       while (data != NULL) {
+               struct subparam_handler_entry *entry;
+
+               entry = g_new0(struct subparam_handler_entry, 1);
+
+               entry->data = data;
+               entry->id = va_arg(args, enum cdma_sms_subparam_id);
+               entry->flags = va_arg(args, int);
+
+               data = va_arg(args, void *);
+               entries = g_slist_prepend(entries, entry);
+       }
+
+       va_end(args);
+
+       entries = g_slist_reverse(entries);
+
+       l = entries;
+       while (simple_iter_next(iter) == TRUE) {
+               rec_handler handler;
+               struct subparam_handler_entry *entry;
+               guint8 subparam_len;
+               const guint8 *subparam_buf;
+               GSList *l2;
+
+               for (l2 = l; l2; l2 = l2->next) {
+                       entry = l2->data;
+
+                       if (simple_iter_get_id(iter) == entry->id)
+                               break;
+               }
+
+               /* Ignore unexpected subparameter record */
+               if (l2 == NULL)
+                       continue;
+
+               entry->found = TRUE;
+
+               subparam_len = simple_iter_get_length(iter);
+               subparam_buf = simple_iter_get_data(iter);
+
+               handler = subparam_handler_for_id(entry->id);
+
+               decode_result = handler(subparam_buf,
+                                       subparam_len,
+                                       entry->data);
+               if (decode_result == FALSE)
+                       break; /* Stop if decoding failed */
+
+               set_bitmap(bitmap, entry->id);
+       }
+
+       for (; l; l = l->next) {
+               struct subparam_handler_entry *entry = l->data;
+
+               if ((entry->flags & CDMA_SMS_REC_FLAG_MANDATORY) &&
+                       (entry->found == FALSE)) {
+                       decode_result = FALSE;
+                       break;
+               }
+       }
+
+       g_slist_foreach(entries, (GFunc) g_free, NULL);
+       g_slist_free(entries);
+
+       return decode_result;
+}
+
+/* Decode WMT */
+static gboolean cdma_sms_decode_wmt(struct simple_iter *iter,
+                                       struct cdma_sms_bearer_data *bd)
+{
+       switch (bd->id.msg_type) {
+       case CDMA_SMS_MSG_TYPE_RESERVED:
+               return FALSE; /* Invalid */
+       case CDMA_SMS_MSG_TYPE_DELIVER:
+               /*
+                * WMT DELIVER, table 4.3.4-1 of C.S0015-B v2.0
+                * TODO: Not all optional subparameters supported.
+                */
+               return decode_subparams(iter,
+                                       &bd->subparam_bitmap,
+                                       &bd->wmt_deliver.ud,
+                                       CDMA_SMS_SUBPARAM_ID_USER_DATA,
+                                       0,
+                                       NULL);
+               break;
+       case CDMA_SMS_MSG_TYPE_SUBMIT:
+       case CDMA_SMS_MSG_TYPE_CANCEL:
+               return FALSE; /* Invalid for MT WMT */
+       case CDMA_SMS_MSG_TYPE_DELIVER_ACK:
+       case CDMA_SMS_MSG_TYPE_USER_ACK:
+       case CDMA_SMS_MSG_TYPE_READ_ACK:
+               return FALSE; /* TODO: Not supported yet */
+       case CDMA_SMS_MSG_TYPE_DELIVER_REPORT:
+       case CDMA_SMS_MSG_TYPE_SUBMIT_REPORT:
+               return FALSE; /* Invalid for MT WMT */
+       }
+
+       return FALSE;
+}
+
+static gboolean p2p_decode_bearer_data(const guint8 *buf, guint8 len,
+                                       enum cdma_sms_teleservice_id tele_id,
+                                       struct cdma_sms_bearer_data *bd)
+{
+       struct simple_iter iter;
+
+       simple_iter_init(&iter, buf, len);
+
+       /* Message Identifier is mandatory, * Section 4 of C.S0015-B v2.0 */
+       if (find_and_decode(&iter,
+                               CDMA_SMS_SUBPARAM_ID_MESSAGE_ID,
+                               cdma_sms_decode_message_id,
+                               &bd->id) != TRUE)
+               return FALSE;
+
+       set_bitmap(&bd->subparam_bitmap, CDMA_SMS_SUBPARAM_ID_MESSAGE_ID);
+
+       simple_iter_init(&iter, buf, len);
+
+       switch (tele_id) {
+       case CDMA_SMS_TELESERVICE_ID_CMT91:
+       case CDMA_SMS_TELESERVICE_ID_WPT:
+               return FALSE; /* TODO */
+       case CDMA_SMS_TELESERVICE_ID_WMT:
+               return cdma_sms_decode_wmt(&iter, bd);
+       case CDMA_SMS_TELESERVICE_ID_VMN:
+       case CDMA_SMS_TELESERVICE_ID_WAP:
+       case CDMA_SMS_TELESERVICE_ID_WEMT:
+       case CDMA_SMS_TELESERVICE_ID_SCPT:
+       case CDMA_SMS_TELESERVICE_ID_CATPT:
+               return FALSE; /* TODO */
+       }
+
+       return FALSE;
+}
+
+/* Decode Bearer Data */
+static gboolean cdma_sms_decode_bearer_data(const guint8 *buf, guint8 len,
+                                                               void *data)
+{
+       struct cdma_sms *msg = data;
+
+       switch (msg->type) {
+       case CDMA_SMS_TP_MSG_TYPE_P2P:
+               return p2p_decode_bearer_data(buf, len,
+                                               msg->p2p_msg.teleservice_id,
+                                               &msg->p2p_msg.bd);
+       case CDMA_SMS_TP_MSG_TYPE_BCAST:
+               return FALSE; /* TODO */
+       case CDMA_SMS_TP_MSG_TYPE_ACK:
+               return FALSE; /* Invalid */
+       }
+
+       return FALSE;
+}
+
+static rec_handler param_handler_for_id(enum cdma_sms_param_id id,
+                                               struct cdma_sms *incoming,
+                                               void **data)
+{
+       if (incoming->type != CDMA_SMS_TP_MSG_TYPE_P2P)
+               return NULL; /* TODO: Other types not supported yet */
+
+       switch (id) {
+       case CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER:
+               *data = &incoming->p2p_msg.teleservice_id;
+               return cdma_sms_decode_teleservice;
+       case CDMA_SMS_PARAM_ID_SERVICE_CATEGORY:
+               return NULL; /* TODO */
+       case CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS:
+               *data = &incoming->p2p_msg.oaddr;
+               return cdma_sms_decode_addr;
+       case CDMA_SMS_PARAM_ID_ORIGINATING_SUBADDRESS:
+       case CDMA_SMS_PARAM_ID_DESTINATION_ADDRESS:
+       case CDMA_SMS_PARAM_ID_DESTINATION_SUBADDRESS:
+       case CDMA_SMS_PARAM_ID_BEARER_REPLY_OPTION:
+       case CDMA_SMS_PARAM_ID_CAUSE_CODE:
+               return NULL; /* TODO */
+       case CDMA_SMS_PARAM_ID_BEARER_DATA:
+               *data = incoming;
+               return cdma_sms_decode_bearer_data;
+       }
+
+       return NULL;
+}
+
+static gboolean cdma_sms_p2p_decode(const guint8 *pdu, guint8 len,
+                                       struct cdma_sms *incoming)
+{
+       struct simple_iter iter;
+
+       simple_iter_init(&iter, pdu, len);
+
+       /*
+        * Teleservice Identifier is mandatory,
+        * Table 3.4.2.1-1 of C.S0015-B v2.0
+        */
+       if (find_and_decode(&iter,
+                               CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER,
+                               cdma_sms_decode_teleservice,
+                               &incoming->p2p_msg.teleservice_id) != TRUE)
+               return FALSE;
+
+       set_bitmap(&incoming->p2p_msg.param_bitmap,
+                       CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER);
+
+       simple_iter_init(&iter, pdu, len);
+
+       while (simple_iter_next(&iter) == TRUE) {
+               rec_handler handler;
+               enum cdma_sms_param_id rec_id;
+               guint8 rec_len;
+               const guint8 *rec_buf;
+               void *uninitialized_var(dataobj);
+
+               rec_id = simple_iter_get_id(&iter);
+               if (rec_id == CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER)
+                       continue;
+
+               rec_len = simple_iter_get_length(&iter);
+               rec_buf = simple_iter_get_data(&iter);
+
+               handler = param_handler_for_id(rec_id, incoming, &dataobj);
+               if (handler != NULL) {
+                       if (handler(rec_buf, rec_len, dataobj) == FALSE)
+                               return FALSE;
+
+                       set_bitmap(&incoming->p2p_msg.param_bitmap, rec_id);
+               }
+       }
+
+       /*
+        * Originating Address is mandatory field,
+        * Table 3.4.2.1-1 of C.S0015-B v2.0
+        */
+       if ((incoming->p2p_msg.param_bitmap &
+                       (1 << CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS)) == 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+gboolean cdma_sms_decode(const guint8 *pdu, guint8 len,
+                               struct cdma_sms *incoming)
+{
+       incoming->type = bit_field_unpack(pdu, 0, 8);
+       pdu += 1;
+       len -= 1;
+
+       switch (incoming->type) {
+       case CDMA_SMS_TP_MSG_TYPE_P2P:
+               return cdma_sms_p2p_decode(pdu, len, incoming);
+       case CDMA_SMS_TP_MSG_TYPE_BCAST:
+       case CDMA_SMS_TP_MSG_TYPE_ACK:
+               /* TODO: Not supported yet */
+               return FALSE;
+       }
+
+       return FALSE;
+}
diff --git a/src/cdma-smsutil.h b/src/cdma-smsutil.h
new file mode 100644 (file)
index 0000000..21b8480
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define CDMA_SMS_MAX_ADDR_FIELDS 256
+#define CDMA_SMS_UD_LEN 512
+
+/* 3GPP2 C.S0015-B v2.0, Table 3.4-1 */
+enum cdma_sms_tp_msg_type {
+       CDMA_SMS_TP_MSG_TYPE_P2P =      0,
+       CDMA_SMS_TP_MSG_TYPE_BCAST =    1,
+       CDMA_SMS_TP_MSG_TYPE_ACK =      2
+};
+
+/*
+ * 3GPP2 X.S0004-550-E, Section 2.256
+ * Only supported by 3GPP2 C.S0015-B v2.0 Section 3.4.3.1 listed.
+ */
+enum cdma_sms_teleservice_id {
+       CDMA_SMS_TELESERVICE_ID_CMT91 = 4096,
+       CDMA_SMS_TELESERVICE_ID_WPT =   4097,
+       CDMA_SMS_TELESERVICE_ID_WMT =   4098,
+       CDMA_SMS_TELESERVICE_ID_VMN =   4099,
+       CDMA_SMS_TELESERVICE_ID_WAP =   4100,
+       CDMA_SMS_TELESERVICE_ID_WEMT =  4101,
+       CDMA_SMS_TELESERVICE_ID_SCPT =  4102,
+       CDMA_SMS_TELESERVICE_ID_CATPT = 4103
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */
+enum cdma_sms_num_mode {
+       CDMA_SMS_NUM_MODE_DIGIT =       0,
+       CDMA_SMS_NUM_MODE_DATA_NW =     1
+};
+
+/* 3GPP2 C.S0005-E v2.0 Table 2.7.1.3.2.4-2 */
+enum cdma_sms_digi_num_type {
+       CDMA_SMS_DIGI_NUM_TYPE_UNKNOWN =        0,
+       CDMA_SMS_DIGI_NUM_TYPE_INTERNATIONAL =  1,
+       CDMA_SMS_DIGI_NUM_TYPE_NATIONAL =       2,
+       CDMA_SMS_DIGI_NUM_TYPE_NETWORK =        3,
+       CDMA_SMS_DIGI_NUM_TYPE_SUBSCRIBER =     4,
+       CDMA_SMS_DIGI_NUM_TYPE_RESERVED1 =      5,
+       CDMA_SMS_DIGI_NUM_TYPE_ABBREVIATED =    6,
+       CDMA_SMS_DIGI_NUM_TYPE_RESERVED2 =      7
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 3.4.3.3-1 */
+enum cdma_sms_data_nw_num_type {
+       CDMA_SMS_DATA_NW_NUM_TYPE_UNKNOWN =                     0,
+       CDMA_SMS_DATA_NW_NUM_TYPE_INTERNET_PROTOCOL =           1,
+       CDMA_SMS_DATA_NW_NUM_TYPE_INTERNET_EMAIL_ADDRESS =      2,
+       /* All Other Values Reserved */
+};
+
+/* 3GPP2 C.S0005-E v2.0 Table 2.7.1.3.2.4-3 */
+enum cdma_sms_numbering_plan {
+       CDMA_SMS_NUMBERING_PLAN_UNKNOWN =       0,
+       CDMA_SMS_NUMBERING_PLAN_ISDN =          1,
+       CDMA_SMS_NUMBERING_PLAN_DATA =          3,
+       CDMA_SMS_NUMBERING_PLAN_TELEX =         4,
+       CDMA_SMS_NUMBERING_PLAN_PRIVATE =       9,
+       CDMA_SMS_NUMBERING_PLAN_RESERVED =      15
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 4.5.1-1 */
+enum cdma_sms_msg_type {
+       CDMA_SMS_MSG_TYPE_RESERVED =            0,
+       CDMA_SMS_MSG_TYPE_DELIVER =             1,
+       CDMA_SMS_MSG_TYPE_SUBMIT =              2,
+       CDMA_SMS_MSG_TYPE_CANCEL =              3,
+       CDMA_SMS_MSG_TYPE_DELIVER_ACK =         4,
+       CDMA_SMS_MSG_TYPE_USER_ACK =            5,
+       CDMA_SMS_MSG_TYPE_READ_ACK =            6,
+       CDMA_SMS_MSG_TYPE_DELIVER_REPORT =      7,
+       CDMA_SMS_MSG_TYPE_SUBMIT_REPORT =       8,
+};
+
+/* C.R1001-G_v1.0 Table 9.1-1 */
+enum cdma_sms_msg_encoding {
+       CDMA_SMS_MSG_ENCODING_OCTET =                   0,
+       CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG =   1,
+       CDMA_SMS_MSG_ENCODING_7BIT_ASCII =              2,
+       CDMA_SMS_MSG_ENCODING_IA5 =                     3,
+       CDMA_SMS_MSG_ENCODING_UNICODE =                 4,
+       CDMA_SMS_MSG_ENCODING_SHIFT_JIS =               5,
+       CDMA_SMS_MSG_ENCODING_KOREAN =                  6,
+       CDMA_SMS_MSG_ENCODING_LATIN_HEBREW =            7,
+       CDMA_SMS_MSG_ENCODING_LATIN =                   8,
+       CDMA_SMS_MSG_ENCODING_GSM_7BIT =                9,
+       CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING =         10
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 3.4.3-1 */
+enum cdma_sms_param_id {
+       CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER  =     0x00,
+       CDMA_SMS_PARAM_ID_SERVICE_CATEGORY =            0x01,
+       CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS =         0x02,
+       CDMA_SMS_PARAM_ID_ORIGINATING_SUBADDRESS =      0x03,
+       CDMA_SMS_PARAM_ID_DESTINATION_ADDRESS =         0x04,
+       CDMA_SMS_PARAM_ID_DESTINATION_SUBADDRESS =      0x05,
+       CDMA_SMS_PARAM_ID_BEARER_REPLY_OPTION =         0x06,
+       CDMA_SMS_PARAM_ID_CAUSE_CODE =                  0x07,
+       CDMA_SMS_PARAM_ID_BEARER_DATA =                 0x08
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 4.5-1 */
+enum cdma_sms_subparam_id {
+       CDMA_SMS_SUBPARAM_ID_MESSAGE_ID =                       0x00,
+       CDMA_SMS_SUBPARAM_ID_USER_DATA =                        0x01,
+       CDMA_SMS_SUBPARAM_ID_USER_RESPONSE_CODE =               0x02,
+       CDMA_SMS_SUBPARAM_ID_MC_TIME_STAMP =                    0x03,
+       CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_ABSOLUTE =         0x04,
+       CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_RELATIVE =         0x05,
+       CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE =  0x06,
+       CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_RELATIVE =  0x07,
+       CDMA_SMS_SUBPARAM_ID_PRIORITY_INDICATOR =               0x08,
+       CDMA_SMS_SUBPARAM_ID_PRIVACY_INDICATOR =                0x09,
+       CDMA_SMS_SUBPARAM_ID_REPLY_OPTION =                     0x0A,
+       CDMA_SMS_SUBPARAM_ID_NUMBER_OF_MESSAGES =               0x0B,
+       CDMA_SMS_SUBPARAM_ID_ALERT_ON_MESSAGE_DELIVERY =        0x0C,
+       CDMA_SMS_SUBPARAM_ID_LANGUAGE_INDICATOR =               0x0D,
+       CDMA_SMS_SUBPARAM_ID_CALL_BACK_NUMBER =                 0x0E,
+       CDMA_SMS_SUBPARAM_ID_MESSAGE_DISPLAY_MODE =             0x0F,
+       CDMA_SMS_SUBPARAM_ID_MULTIPLE_ENCODING_USER_DATA =      0x10,
+       CDMA_SMS_SUBPARAM_ID_MESSAGE_DEPOSIT_INDEX =            0x11,
+       CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_DATA =    0x12,
+       CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_RESULT =  0x13,
+       CDMA_SMS_SUBPARAM_ID_MESSAGE_STATUS =                   0x14,
+       CDMA_SMS_SUBPARAM_ID_TP_FAILURE_CAUSE =                 0x15,
+       CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN =                     0x16,
+       CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN_ACK =                 0x17
+};
+
+/* 3GPP2 C.R1001-G Table 9.3.1-1 and 9.3.3-1 */
+enum cdma_sms_service_cat {
+       CDMA_SMS_SERVICE_CAT_EMERGENCY_BROADCAST =              0x0001,
+       CDMA_SMS_SERVICE_CAT_ADMINISTRATIVE =                   0x0002,
+       CDMA_SMS_SERVICE_CAT_MAINTENANCE =                      0x0003,
+       CDMA_SMS_SERVICE_CAT_GEN_NEWS_LOCAL =                   0x0004,
+       CDMA_SMS_SERVICE_CAT_GEN_NEWS_REGIONAL =                0x0005,
+       CDMA_SMS_SERVICE_CAT_GEN_NEWS_NATIONAL =                0x0006,
+       CDMA_SMS_SERVICE_CAT_GEN_NEWS_INT =                     0x0007,
+       CDMA_SMS_SERVICE_CAT_FIN_NEWS_LOCAL =                   0x0008,
+       CDMA_SMS_SERVICE_CAT_FIN_NEWS_REGIONAL =                0x0009,
+       CDMA_SMS_SERVICE_CAT_FIN_NEWS_NATIONAL =                0x000A,
+       CDMA_SMS_SERVICE_CAT_FIN_NEWS_INT =                     0x000B,
+       CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_LOCAL =                0x000C,
+       CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_REGIONAL =             0x000D,
+       CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_NATIONAL =             0x000E,
+       CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_INT =                  0x000F,
+       CDMA_SMS_SERVICE_CAT_ENT_NEWS_LOCAL =                   0x0010,
+       CDMA_SMS_SERVICE_CAT_ENT_NEWS_REGIONAL =                0x0011,
+       CDMA_SMS_SERVICE_CAT_ENT_NEWS_NATIONAL =                0x0012,
+       CDMA_SMS_SERVICE_CAT_ENT_NEWS_INT =                     0x0013,
+       CDMA_SMS_SERVICE_CAT_LOCAL_WEATHER =                    0x0014,
+       CDMA_SMS_SERVICE_CAT_TRAFFIC_REPORT =                   0x0015,
+       CDMA_SMS_SERVICE_CAT_FLIGHT_SCHED =                     0x0016,
+       CDMA_SMS_SERVICE_CAT_RESTAURANT =                       0x0017,
+       CDMA_SMS_SERVICE_CAT_LODGINGS =                         0x0018,
+       CDMA_SMS_SERVICE_CAT_RETAIL_DIR =                       0x0019,
+       CDMA_SMS_SERVICE_CAT_ADVERTISEMENTS =                   0x001A,
+       CDMA_SMS_SERVICE_CAT_STOCK_QUOTES =                     0x001B,
+       CDMA_SMS_SERVICE_CAT_EMPLOYMENT =                       0x001C,
+       CDMA_SMS_SERVICE_CAT_HOSPITAL =                         0x001D,
+       CDMA_SMS_SERVICE_CAT_TECH_NEWS =                        0x001E,
+       CDMA_SMS_SERVICE_CAT_MULTICATEGORY =                    0x001F,
+       CDMA_SMS_SERVICE_CAT_CAPT =                             0x0020,
+       CDMA_SMS_SERVICE_CAT_PRESIDENTIAL_ALERT =               0x1000,
+       CDMA_SMS_SERVICE_CAT_EXTREME_THREAT =                   0x1001,
+       CDMA_SMS_SERVICE_CAT_SEVERE_THREAT =                    0x1002,
+       CDMA_SMS_SERVICE_CAT_AMBER =                            0x1003,
+       CDMA_SMS_SERVICE_CAT_CMAS_TEST =                        0x1004
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */
+enum cdma_sms_digit_mode {
+       CDMA_SMS_DIGIT_MODE_4BIT_DTMF =         0,
+       CDMA_SMS_DIGIT_MODE_8BIT_ASCII =        1
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */
+struct cdma_sms_address {
+       enum cdma_sms_digit_mode digit_mode;
+       enum cdma_sms_num_mode number_mode;
+       union {
+               enum cdma_sms_digi_num_type digi_num_type;
+               enum cdma_sms_data_nw_num_type data_nw_num_type;
+       };
+       enum cdma_sms_numbering_plan number_plan;
+       guint8 num_fields;
+       guint8 address[CDMA_SMS_MAX_ADDR_FIELDS];
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.6 */
+struct cdma_sms_cause_code {
+       guint8 reply_seq;
+       guint8 error_class;
+       guint8 cause_code;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 4.5.1 */
+struct cdma_sms_identifier {
+       enum cdma_sms_msg_type msg_type;
+       guint16 msg_id;
+       gboolean header_ind;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 4.5.2 */
+struct cdma_sms_ud {
+       enum cdma_sms_msg_encoding msg_encoding;
+       guint8 num_fields;
+       guint8 chari[CDMA_SMS_UD_LEN];
+};
+
+/*
+ * 3GPP2 C.S0015-B v2.0 Table 4.3.4-1.
+ * TODO: Not all subparameter records defined
+ *       and supported yet.
+ */
+struct cdma_sms_wmt_deliver {
+       struct cdma_sms_ud ud;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 4.5 */
+struct cdma_sms_bearer_data {
+       guint32 subparam_bitmap;
+       struct cdma_sms_identifier id;
+       union {
+               struct cdma_sms_wmt_deliver wmt_deliver;
+       };
+};
+
+/*
+ * 3GPP2 C.S0015-B v2.0 Table 3.4.2.1-1.
+ * TODO: Not all parameter records defined
+ *       and supported yet.
+ */
+struct cdma_sms_p2p_msg {
+       guint32 param_bitmap;
+       enum cdma_sms_teleservice_id teleservice_id;
+       struct cdma_sms_address oaddr;
+       struct cdma_sms_bearer_data bd;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 3.4.2.2-1 */
+struct cdma_sms_broadcast_msg {
+       enum cdma_sms_service_cat service_category;
+       struct cdma_sms_bearer_data bd;
+};
+
+/*
+ * 3GPP2 C.S0015-B v2.0 Table 3.4.2.3-1
+ * TODO: Not all parameter records defined
+ *       and supported yet.
+ */
+struct cdma_sms_ack_msg {
+       struct cdma_sms_address daddr;
+       struct cdma_sms_cause_code cause_code;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.1 */
+struct cdma_sms {
+       enum cdma_sms_tp_msg_type type;
+       union {
+               struct cdma_sms_p2p_msg p2p_msg;
+               struct cdma_sms_broadcast_msg broadcast_msg;
+               struct cdma_sms_ack_msg ack_msg;
+       };
+};
+
+static inline gboolean check_bitmap(guint32 bitmap, guint32 pos)
+{
+       guint32 mask = 0x1 << pos;
+
+       return bitmap & mask ? TRUE : FALSE;
+}
+
+gboolean cdma_sms_decode(const guint8 *pdu, guint8 len,
+                               struct cdma_sms *out);
+char *cdma_sms_decode_text(const struct cdma_sms_ud *ud);
+const char *cdma_sms_address_to_string(const struct cdma_sms_address *addr);
diff --git a/src/cdma-voicecall.c b/src/cdma-voicecall.c
new file mode 100644 (file)
index 0000000..f7e3b67
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+
+static GSList *g_drivers;
+
+struct ofono_cdma_voicecall {
+       struct ofono_cdma_phone_number phone_number;
+       struct ofono_cdma_phone_number waiting_number;
+       int direction;
+       enum cdma_call_status status;
+       time_t start_time;
+       DBusMessage *pending;
+       const struct ofono_cdma_voicecall_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static const char *disconnect_reason_to_string(enum ofono_disconnect_reason r)
+{
+       switch (r) {
+       case OFONO_DISCONNECT_REASON_LOCAL_HANGUP:
+               return "local";
+       case OFONO_DISCONNECT_REASON_REMOTE_HANGUP:
+               return "remote";
+       default:
+               return "network";
+       }
+}
+
+static const char *cdma_call_status_to_string(enum cdma_call_status status)
+{
+       switch (status) {
+       case CDMA_CALL_STATUS_ACTIVE:
+               return "active";
+       case CDMA_CALL_STATUS_DIALING:
+               return "dialing";
+       case CDMA_CALL_STATUS_ALERTING:
+               return "alerting";
+       case CDMA_CALL_STATUS_INCOMING:
+               return "incoming";
+       case CDMA_CALL_STATUS_DISCONNECTED:
+               return "disconnected";
+       }
+
+       return NULL;
+}
+
+static const char *time_to_str(const time_t *t)
+{
+       static char buf[128];
+       struct tm tm;
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime_r(t, &tm));
+       buf[127] = '\0';
+
+       return buf;
+}
+
+static void generic_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+       DBusMessage *reply;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               reply = dbus_message_new_method_return(vc->pending);
+       else
+               reply = __ofono_error_failed(vc->pending);
+
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+}
+
+static void append_voicecall_properties(struct ofono_cdma_voicecall *vc,
+                                       DBusMessageIter *dict)
+{
+       const char *status;
+       const char *lineid;
+       const char *waiting_call;
+       dbus_bool_t call_waiting = FALSE;
+
+       status = cdma_call_status_to_string(vc->status);
+       ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &status);
+
+       lineid = cdma_phone_number_to_string(&vc->phone_number);
+       ofono_dbus_dict_append(dict, "LineIdentification",
+                                       DBUS_TYPE_STRING, &lineid);
+
+       if (vc->waiting_number.number[0] != '\0') {
+               waiting_call = cdma_phone_number_to_string(&vc->waiting_number);
+               ofono_dbus_dict_append(dict, "CallWaitingNumber",
+                                       DBUS_TYPE_STRING, &waiting_call);
+               call_waiting = TRUE;
+       }
+
+       ofono_dbus_dict_append(dict, "CallWaiting",
+                                       DBUS_TYPE_BOOLEAN, &call_waiting);
+
+       if (vc->status == CDMA_CALL_STATUS_ACTIVE) {
+               const char *timestr = time_to_str(&vc->start_time);
+
+               ofono_dbus_dict_append(dict, "StartTime", DBUS_TYPE_STRING,
+                                       &timestr);
+       }
+}
+
+static DBusMessage *voicecall_manager_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       reply = dbus_message_new_method_return(msg);
+
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       append_voicecall_properties(vc, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void voicecall_emit_disconnect_reason(struct ofono_cdma_voicecall *vc,
+                                       enum ofono_disconnect_reason reason)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(vc->atom);
+       const char *reason_str;
+
+       reason_str = disconnect_reason_to_string(reason);
+
+       g_dbus_emit_signal(conn, path, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE,
+                               "DisconnectReason",
+                               DBUS_TYPE_STRING, &reason_str,
+                               DBUS_TYPE_INVALID);
+}
+
+static void voicecall_set_call_status(struct ofono_cdma_voicecall *vc,
+                                               enum cdma_call_status status)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(vc->atom);
+       const char *status_str;
+       enum cdma_call_status old_status;
+
+       DBG("status: %s", cdma_call_status_to_string(status));
+
+       if (vc->status == status)
+               return;
+
+       old_status = vc->status;
+
+       vc->status = status;
+
+       status_str = cdma_call_status_to_string(status);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CDMA_VOICECALL_MANAGER_INTERFACE,
+                                       "State", DBUS_TYPE_STRING,
+                                       &status_str);
+
+       if (status == CDMA_CALL_STATUS_ACTIVE &&
+                       old_status == CDMA_CALL_STATUS_DIALING) {
+               const char *timestr;
+
+               vc->start_time = time(NULL);
+               timestr = time_to_str(&vc->start_time);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CDMA_VOICECALL_MANAGER_INTERFACE,
+                                       "StartTime", DBUS_TYPE_STRING,
+                                       &timestr);
+       }
+
+       /* TODO: Properly signal property changes here */
+       if (status == CDMA_CALL_STATUS_DISCONNECTED) {
+               memset(&vc->phone_number, 0,
+                               sizeof(struct ofono_cdma_phone_number));
+
+               memset(&vc->waiting_number, 0,
+                       sizeof(struct ofono_cdma_phone_number));
+       }
+}
+
+static void voicecall_set_call_lineid(struct ofono_cdma_voicecall *vc)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path  = __ofono_atom_get_path(vc->atom);
+       const char *lineid_str;
+
+       /* For MO calls, LineID is the dialed phone number */
+       lineid_str = cdma_phone_number_to_string(&vc->phone_number);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CDMA_VOICECALL_MANAGER_INTERFACE,
+                                       "LineIdentification",
+                                       DBUS_TYPE_STRING, &lineid_str);
+}
+
+static void manager_dial_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               reply = __ofono_error_failed(vc->pending);
+               __ofono_dbus_pending_reply(&vc->pending, reply);
+
+               return;
+       }
+
+       voicecall_set_call_lineid(vc);
+       vc->direction = CALL_DIRECTION_MOBILE_ORIGINATED;
+       voicecall_set_call_status(vc, CDMA_CALL_STATUS_DIALING);
+
+       reply = dbus_message_new_method_return(vc->pending);
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+}
+
+static DBusMessage *voicecall_manager_dial(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+       const char *number;
+
+       if (vc->pending)
+               return __ofono_error_busy(msg);
+
+       if (vc->status != CDMA_CALL_STATUS_DISCONNECTED)
+               return __ofono_error_failed(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!valid_cdma_phone_number_format(number))
+               return __ofono_error_invalid_format(msg);
+
+       if (vc->driver->dial == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       string_to_cdma_phone_number(number, &vc->phone_number);
+       vc->driver->dial(vc, &vc->phone_number, manager_dial_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *voicecall_manager_hangup(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+
+       if (vc->pending)
+               return __ofono_error_busy(msg);
+
+       if (vc->driver->hangup == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->status == CDMA_CALL_STATUS_DISCONNECTED)
+               return __ofono_error_failed(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->hangup(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *voicecall_manager_answer(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+
+       if (vc->pending)
+               return __ofono_error_busy(msg);
+
+       if (vc->driver->answer == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->status != CDMA_CALL_STATUS_INCOMING)
+               return __ofono_error_failed(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->answer(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *voicecall_manager_flash(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+       const char *string;
+
+       if (vc->pending)
+               return __ofono_error_busy(msg);
+
+       if (vc->driver->send_flash == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &string,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->send_flash(vc, string, generic_callback, vc);
+
+       return NULL;
+}
+
+static ofono_bool_t is_valid_tones(const char *tones)
+{
+       int len;
+       int i;
+
+       if (tones == NULL)
+               return FALSE;
+
+       len = strlen(tones);
+       if (len == 0)
+               return FALSE;
+
+       for (i = 0; i < len; i++) {
+               if (g_ascii_isdigit(tones[i]) || tones[i] == '*' ||
+                               tones[i] == '#')
+                       continue;
+               else
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static DBusMessage *voicecall_manager_tone(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_cdma_voicecall *vc = data;
+       const char *tones;
+
+       if (vc->pending)
+               return __ofono_error_busy(msg);
+
+       if (vc->driver->send_tones == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->status != CDMA_CALL_STATUS_ACTIVE)
+               return __ofono_error_failed(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &tones,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (is_valid_tones(tones) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->send_tones(vc,  tones, generic_callback, vc);
+
+       return NULL;
+}
+
+static GDBusMethodTable manager_methods[] = {
+       { "GetProperties",    "",    "a{sv}",
+                                       voicecall_manager_get_properties },
+       { "Dial",             "s",  "o",        voicecall_manager_dial,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Hangup",           "",    "",         voicecall_manager_hangup,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Answer",           "",    "",         voicecall_manager_answer,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SendFlash",      "s",    "",         voicecall_manager_flash,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SendTones",     "s",    "",        voicecall_manager_tone,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { "DisconnectReason",   "s" },
+       { }
+};
+
+void ofono_cdma_voicecall_disconnected(struct ofono_cdma_voicecall *vc,
+                                       enum ofono_disconnect_reason reason,
+                                       const struct ofono_error *error)
+{
+       DBG("Got disconnection event for reason: %d", reason);
+
+       if (reason != OFONO_DISCONNECT_REASON_UNKNOWN)
+               voicecall_emit_disconnect_reason(vc, reason);
+
+       voicecall_set_call_status(vc, CDMA_CALL_STATUS_DISCONNECTED);
+}
+
+int ofono_cdma_voicecall_driver_register(
+                               const struct ofono_cdma_voicecall_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *)d);
+
+       return 0;
+}
+
+void ofono_cdma_voicecall_driver_unregister(
+                               const struct ofono_cdma_voicecall_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *)d);
+}
+
+static void cdma_voicecall_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       g_dbus_unregister_interface(conn, path,
+                               OFONO_CDMA_VOICECALL_MANAGER_INTERFACE);
+       ofono_modem_remove_interface(modem,
+                               OFONO_CDMA_VOICECALL_MANAGER_INTERFACE);
+}
+
+static void voicecall_manager_remove(struct ofono_atom *atom)
+{
+       struct ofono_cdma_voicecall *vc = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (vc == NULL)
+               return;
+
+       if (vc->driver && vc->driver->remove)
+               vc->driver->remove(vc);
+
+       g_free(vc);
+}
+
+struct ofono_cdma_voicecall *ofono_cdma_voicecall_create(
+                                               struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data)
+{
+       struct ofono_cdma_voicecall *vc;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       vc = g_try_new0(struct ofono_cdma_voicecall, 1);
+       if (vc == NULL)
+               return NULL;
+
+       vc->status = CDMA_CALL_STATUS_DISCONNECTED;
+
+       vc->atom = __ofono_modem_add_atom(modem,
+                                       OFONO_ATOM_TYPE_CDMA_VOICECALL_MANAGER,
+                                       voicecall_manager_remove, vc);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_cdma_voicecall_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(vc, vendor, data) < 0)
+                       continue;
+
+               vc->driver = drv;
+               break;
+       }
+
+       return vc;
+}
+
+void ofono_cdma_voicecall_register(struct ofono_cdma_voicecall *vc)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       const char *path = __ofono_atom_get_path(vc->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CDMA_VOICECALL_MANAGER_INTERFACE,
+                                       manager_methods, manager_signals, NULL,
+                                       vc, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CDMA_VOICECALL_MANAGER_INTERFACE);
+               return;
+       }
+
+       ofono_modem_add_interface(modem,
+                               OFONO_CDMA_VOICECALL_MANAGER_INTERFACE);
+
+       __ofono_atom_register(vc->atom, cdma_voicecall_unregister);
+}
+
+void ofono_cdma_voicecall_remove(struct ofono_cdma_voicecall *vc)
+{
+       __ofono_atom_free(vc->atom);
+}
+
+void ofono_cdma_voicecall_set_data(struct ofono_cdma_voicecall *vc, void *data)
+{
+       vc->driver_data = data;
+}
+
+void *ofono_cdma_voicecall_get_data(struct ofono_cdma_voicecall *vc)
+{
+       return vc->driver_data;
+}
diff --git a/src/common.c b/src/common.c
new file mode 100644 (file)
index 0000000..62f2c5d
--- /dev/null
@@ -0,0 +1,737 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/types.h>
+#include "common.h"
+#include "util.h"
+
+struct error_entry {
+       int error;
+       const char *str;
+};
+
+/*
+ * 0-127 from 24.011 Annex E2
+ * 127-255 23.040 Section 9.2.3.22
+ * Rest are from 27.005 Section 3.2.5
+ */
+struct error_entry cms_errors[] = {
+       { 1,    "Unassigned number" },
+       { 8,    "Operator determined barring" },
+       { 10,   "Call barred" },
+       { 21,   "Short message transfer rejected" },
+       { 27,   "Destination out of service" },
+       { 28,   "Unindentified subscriber" },
+       { 29,   "Facility rejected" },
+       { 30,   "Unknown subscriber" },
+       { 38,   "Network out of order" },
+       { 41,   "Temporary failure" },
+       { 42,   "Congestion" },
+       { 47,   "Recources unavailable" },
+       { 50,   "Requested facility not subscribed" },
+       { 69,   "Requested facility not implemented" },
+       { 81,   "Invalid short message transfer reference value" },
+       { 95,   "Invalid message, unspecified" },
+       { 96,   "Invalid mandatory information" },
+       { 97,   "Message type non existent or not implemented" },
+       { 98,   "Message not compatible with short message protocol state" },
+       { 99,   "Information element non-existent or not implemented" },
+       { 111,  "Protocol error, unspecified" },
+       { 127,  "Internetworking error, unspecified" },
+       { 128,  "Telematic internetworking not supported" },
+       { 129,  "Short message type 0 not supported" },
+       { 130,  "Cannot replace short message" },
+       { 143,  "Unspecified TP-PID error" },
+       { 144,  "Data code scheme not supported" },
+       { 145,  "Message class not supported" },
+       { 159,  "Unspecified TP-DCS error" },
+       { 160,  "Command cannot be actioned" },
+       { 161,  "Command unsupported" },
+       { 175,  "Unspecified TP-Command error" },
+       { 176,  "TPDU not supported" },
+       { 192,  "SC busy" },
+       { 193,  "No SC subscription" },
+       { 194,  "SC System failure" },
+       { 195,  "Invalid SME address" },
+       { 196,  "Destination SME barred" },
+       { 197,  "SM Rejected-Duplicate SM" },
+       { 198,  "TP-VPF not supported" },
+       { 199,  "TP-VP not supported" },
+       { 208,  "(U)SIM SMS Storage full" },
+       { 209,  "No SMS Storage capability in SIM" },
+       { 210,  "Error in MS" },
+       { 211,  "Memory capacity exceeded" },
+       { 212,  "Sim application toolkit busy" },
+       { 213,  "SIM data download error" },
+       { 255,  "Unspecified error cause" },
+       { 300,  "ME Failure" },
+       { 301,  "SMS service of ME reserved" },
+       { 302,  "Operation not allowed" },
+       { 303,  "Operation not supported" },
+       { 304,  "Invalid PDU mode parameter" },
+       { 305,  "Invalid Text mode parameter" },
+       { 310,  "(U)SIM not inserted" },
+       { 311,  "(U)SIM PIN required" },
+       { 312,  "PH-(U)SIM PIN required" },
+       { 313,  "(U)SIM failure" },
+       { 314,  "(U)SIM busy" },
+       { 315,  "(U)SIM wrong" },
+       { 316,  "(U)SIM PUK required" },
+       { 317,  "(U)SIM PIN2 required" },
+       { 318,  "(U)SIM PUK2 required" },
+       { 320,  "Memory failure" },
+       { 321,  "Invalid memory index" },
+       { 322,  "Memory full" },
+       { 330,  "SMSC address unknown" },
+       { 331,  "No network service" },
+       { 332,  "Network timeout" },
+       { 340,  "No +CNMA expected" },
+       { 500,  "Unknown error" },
+};
+
+/* 27.007, Section 9 */
+struct error_entry cme_errors[] = {
+       { 0,    "Phone failure" },
+       { 1,    "No connection to phone" },
+       { 2,    "Phone adapter link reserved" },
+       { 3,    "Operation not allowed" },
+       { 4,    "Operation not supported" },
+       { 5,    "PH_SIM PIN required" },
+       { 6,    "PH_FSIM PIN required" },
+       { 7,    "PH_FSIM PUK required" },
+       { 10,   "SIM not inserted" },
+       { 11,   "SIM PIN required" },
+       { 12,   "SIM PUK required" },
+       { 13,   "SIM failure" },
+       { 14,   "SIM busy" },
+       { 15,   "SIM wrong" },
+       { 16,   "Incorrect password" },
+       { 17,   "SIM PIN2 required" },
+       { 18,   "SIM PUK2 required" },
+       { 20,   "Memory full" },
+       { 21,   "Invalid index" },
+       { 22,   "Not found" },
+       { 23,   "Memory failure" },
+       { 24,   "Text string too long" },
+       { 25,   "Invalid characters in text string" },
+       { 26,   "Dial string too long" },
+       { 27,   "Invalid characters in dial string" },
+       { 30,   "No network service" },
+       { 31,   "Network timeout" },
+       { 32,   "Network not allowed, emergency calls only" },
+       { 40,   "Network personalization PIN required" },
+       { 41,   "Network personalization PUK required" },
+       { 42,   "Network subset personalization PIN required" },
+       { 43,   "Network subset personalization PUK required" },
+       { 44,   "Service provider personalization PIN required" },
+       { 45,   "Service provider personalization PUK required" },
+       { 46,   "Corporate personalization PIN required" },
+       { 47,   "Corporate personalization PUK required" },
+       { 48,   "PH-SIM PUK required" },
+       { 100,  "Unknown error" },
+       { 103,  "Illegal MS" },
+       { 106,  "Illegal ME" },
+       { 107,  "GPRS services not allowed" },
+       { 111,  "PLMN not allowed" },
+       { 112,  "Location area not allowed" },
+       { 113,  "Roaming not allowed in this location area" },
+       { 126,  "Operation temporary not allowed" },
+       { 132,  "Service operation not supported" },
+       { 133,  "Requested service option not subscribed" },
+       { 134,  "Service option temporary out of order" },
+       { 148,  "Unspecified GPRS error" },
+       { 149,  "PDP authentication failure" },
+       { 150,  "Invalid mobile class" },
+       { 256,  "Operation temporarily not allowed" },
+       { 257,  "Call barred" },
+       { 258,  "Phone is busy" },
+       { 259,  "User abort" },
+       { 260,  "Invalid dial string" },
+       { 261,  "SS not executed" },
+       { 262,  "SIM Blocked" },
+       { 263,  "Invalid block" },
+       { 772,  "SIM powered down" },
+};
+
+/* 24.008 Annex H */
+struct error_entry ceer_errors[] = {
+       { 1,    "Unassigned number" },
+       { 3,    "No route to destination" },
+       { 6,    "Channel unacceptable" },
+       { 8,    "Operator determined barring" },
+       { 16,   "Normal call clearing" },
+       { 17,   "User busy" },
+       { 18,   "No user responding" },
+       { 19,   "User alerting, no answer" },
+       { 21,   "Call rejected" },
+       { 22,   "Number changed" },
+       { 25,   "Pre-emption" },
+       { 26,   "Non-selected user clearing" },
+       { 27,   "Destination out of order" },
+       { 28,   "Invalid number format (incomplete number)" },
+       { 29,   "Facility rejected" },
+       { 30,   "Response to STATUS ENQUIRY" },
+       { 31,   "Normal, unspecified" },
+       { 34,   "No circuit/channel available" },
+       { 38,   "Network out of order" },
+       { 41,   "Temporary failure" },
+       { 42,   "Switching equipment congestion" },
+       { 43,   "Access information discared" },
+       { 44,   "Requested circuit/channel not available" },
+       { 47,   "Resource unavailable (unspecified)" },
+       { 49,   "Quality of service unavailable" },
+       { 50,   "Requested facility not subscribed" },
+       { 55,   "Incoming calls barred within the CUG" },
+       { 57,   "Bearer capability not authorized" },
+       { 58,   "Bearar capability not presently available" },
+       { 63,   "Service or option not available, unspecified" },
+       { 65,   "Bearer service not implemented" },
+       { 68,   "ACM equal to or greater than ACMmax" },
+       { 69,   "Requested facility not implemented" },
+       { 70,   "Only restricted digital information bearer capability is available" },
+       { 79,   "Service or option not implemented, unspecified" },
+       { 81,   "Invalid transaction identifier value" },
+       { 87,   "User not member of CUG" },
+       { 88,   "Incompatible destination" },
+       { 91,   "Invalid transit network selection" },
+       { 95,   "Semantically incorrect message" },
+       { 96,   "Invalid mandatory information"},
+       { 97,   "Message type non-existent or not implemented" },
+       { 98,   "Message type not compatible with protocol state" },
+       { 99,   "Information element non-existent or not implemented" },
+       { 100,  "Conditional IE error" },
+       { 101,  "Message not compatible with protocol state" },
+       { 102,  "Recovery on timer expirty" },
+       { 111,  "Protocol error, unspecified" },
+       { 127,  "Interworking, unspecified" },
+};
+
+gboolean valid_number_format(const char *number, int length)
+{
+       int len = strlen(number);
+       int begin = 0;
+       int i;
+
+       if (!len)
+               return FALSE;
+
+       if (number[0] == '+')
+               begin = 1;
+
+       if (begin == len)
+               return FALSE;
+
+       if ((len - begin) > length)
+               return FALSE;
+
+       for (i = begin; i < len; i++) {
+               if (number[i] >= '0' && number[i] <= '9')
+                       continue;
+
+               if (number[i] == '*' || number[i] == '#')
+                       continue;
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+/*
+ * According to 3GPP TS 24.011 or 3GPP TS 31.102, some
+ * addresses (or numbers), like Service Centre address,
+ * Destination address, or EFADN (Abbreviated dialling numbers),
+ * are up 20 digits.
+ */
+gboolean valid_phone_number_format(const char *number)
+{
+       return valid_number_format(number, 20);
+}
+
+gboolean valid_long_phone_number_format(const char *number)
+{
+       return valid_number_format(number, OFONO_MAX_PHONE_NUMBER_LENGTH);
+}
+
+gboolean valid_cdma_phone_number_format(const char *number)
+{
+       int len = strlen(number);
+       int i;
+
+       if (!len)
+               return FALSE;
+
+       if (len > OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH)
+               return FALSE;
+
+       for (i = 0; i < len; i++) {
+               if (number[i] >= '0' && number[i] <= '9')
+                       continue;
+
+               if (number[i] == '*' || number[i] == '#')
+                       continue;
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+const char *telephony_error_to_str(const struct ofono_error *error)
+{
+       struct error_entry *e;
+       int maxentries;
+       int i;
+
+       switch (error->type) {
+       case OFONO_ERROR_TYPE_CME:
+               e = cme_errors;
+               maxentries = sizeof(cme_errors) / sizeof(struct error_entry);
+               break;
+       case OFONO_ERROR_TYPE_CMS:
+               e = cms_errors;
+               maxentries = sizeof(cms_errors) / sizeof(struct error_entry);
+               break;
+       case OFONO_ERROR_TYPE_CEER:
+               e = ceer_errors;
+               maxentries = sizeof(ceer_errors) / sizeof(struct error_entry);
+               break;
+       default:
+               return "Unknown error type";
+       }
+
+       for (i = 0; i < maxentries; i++)
+               if (e[i].error == error->error)
+                       return e[i].str;
+
+       return "Unknown error";
+}
+
+int mmi_service_code_to_bearer_class(int code)
+{
+       int cls = 0;
+
+       /*
+        * Teleservices according to 22.004
+        * 1 - Voice
+        * 2 - SMS
+        * 3,4,5 - Unallocated
+        * 6 - Fax
+        * 7 - All Data Async
+        * 8 - All Data Sync
+        * 12 - Voice Group
+        */
+
+       switch (code) {
+       /* 22.030: 1 to 6, 12 */
+       case 10:
+               cls = BEARER_CLASS_VOICE | BEARER_CLASS_FAX | BEARER_CLASS_SMS;
+               break;
+       /* 22.030: 1 */
+       case 11:
+               cls = BEARER_CLASS_VOICE;
+               break;
+       /* 22.030: 2-6 */
+       case 12:
+               cls = BEARER_CLASS_SMS | BEARER_CLASS_FAX;
+               break;
+       /* 22.030: 6 */
+       case 13:
+               cls = BEARER_CLASS_FAX;
+               break;
+       /* 22.030: 2 */
+       case 16:
+               cls = BEARER_CLASS_SMS;
+               break;
+       /* TODO: Voice Group Call & Broadcast VGCS & VBS */
+       case 17:
+       case 18:
+               break;
+       /* 22.030: 1, 3 to 6, 12 */
+       case 19:
+               cls = BEARER_CLASS_VOICE | BEARER_CLASS_FAX;
+               break;
+       /*
+        * 22.030: 7-11
+        * 22.004 only defines BS 7 (Data Sync) & BS 8 (Data Async)
+        * and PAD and Packet bearer services are deprecated.  Still,
+        * AT modems rely on these to differentiate between sending
+        * a 'All Sync' or 'All Data Sync' message types.  In theory
+        * both message types cover the same bearer services, but we
+        * must still send these for conformance reasons.
+        */
+       case 20:
+               cls = BEARER_CLASS_DATA_ASYNC | BEARER_CLASS_DATA_SYNC |
+                       BEARER_CLASS_PAD | BEARER_CLASS_PACKET;
+               break;
+       /* According to 22.030: All Async (7) */
+       case 21:
+               cls = BEARER_CLASS_DATA_ASYNC | BEARER_CLASS_PAD;
+               break;
+       /* According to 22.030: All Data Async (7)*/
+       case 25:
+               cls = BEARER_CLASS_DATA_ASYNC;
+               break;
+       /* According to 22.030: All Sync (8) */
+       case 22:
+               cls = BEARER_CLASS_DATA_SYNC | BEARER_CLASS_PACKET;
+               break;
+       /* According to 22.030: All Data Sync (8) */
+       case 24:
+               cls = BEARER_CLASS_DATA_SYNC;
+               break;
+       /* According to 22.030: Telephony & All Sync services (1, 8) */
+       case 26:
+               cls = BEARER_CLASS_VOICE | BEARER_CLASS_DATA_SYNC |
+                       BEARER_CLASS_PACKET;
+               break;
+       default:
+               break;
+       }
+
+       return cls;
+}
+
+const char *phone_number_to_string(const struct ofono_phone_number *ph)
+{
+       static char buffer[OFONO_MAX_PHONE_NUMBER_LENGTH + 2];
+
+       if (ph->type == 145 && (strlen(ph->number) > 0) &&
+                       ph->number[0] != '+') {
+               buffer[0] = '+';
+               strncpy(buffer + 1, ph->number, OFONO_MAX_PHONE_NUMBER_LENGTH);
+               buffer[OFONO_MAX_PHONE_NUMBER_LENGTH + 1] = '\0';
+       } else {
+               strncpy(buffer, ph->number, OFONO_MAX_PHONE_NUMBER_LENGTH + 1);
+               buffer[OFONO_MAX_PHONE_NUMBER_LENGTH + 1] = '\0';
+       }
+
+       return buffer;
+}
+
+void string_to_phone_number(const char *str, struct ofono_phone_number *ph)
+{
+       if (str[0] == '+') {
+               strcpy(ph->number, str+1);
+               ph->type = 145; /* International */
+       } else {
+               strcpy(ph->number, str);
+               ph->type = 129; /* Local */
+       }
+}
+
+const char *cdma_phone_number_to_string(
+                               const struct ofono_cdma_phone_number *ph)
+{
+       static char buffer[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH + 1];
+
+       strncpy(buffer, ph->number, OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH);
+       buffer[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH] = '\0';
+
+       return buffer;
+}
+
+void string_to_cdma_phone_number(const char *str,
+                                       struct ofono_cdma_phone_number *ph)
+{
+       strcpy(ph->number, str);
+}
+
+gboolean valid_ussd_string(const char *str, gboolean call_in_progress)
+{
+       int len = strlen(str);
+
+       if (!len)
+               return FALSE;
+
+       /*
+        * Return true if an MMI input string is to be sent as USSD.
+        *
+        * According to 3GPP TS 22.030, after checking the well-known
+        * supplementary service control, SIM control and manufacturer
+        * defined control codes, the terminal should check if the input
+        * should be sent as USSD according to the following rules:
+        *
+        * 1) Terminated by '#'
+        * 2) A short string of 1 or 2 digits
+        *
+        * As an exception, if a 2 digit string starts with a '1' and
+        * there are no calls in progress then this string is treated as
+        * a call setup request instead.
+        */
+
+       if (str[len-1] == '#')
+               return TRUE;
+
+       if (!call_in_progress && len == 2 && str[0] != '1')
+               return FALSE;
+
+       if (len <= 2)
+               return TRUE;
+
+       return FALSE;
+}
+
+const char *ss_control_type_to_string(enum ss_control_type type)
+{
+       switch (type) {
+       case SS_CONTROL_TYPE_ACTIVATION:
+               return "activation";
+       case SS_CONTROL_TYPE_REGISTRATION:
+               return "registration";
+       case SS_CONTROL_TYPE_QUERY:
+               return "interrogation";
+       case SS_CONTROL_TYPE_DEACTIVATION:
+               return "deactivation";
+       case SS_CONTROL_TYPE_ERASURE:
+               return "erasure";
+       }
+
+       return NULL;
+}
+
+#define NEXT_FIELD(str, dest)                  \
+       do {                                    \
+               dest = str;                     \
+                                               \
+               str = strchrnul(str, '*');      \
+               if (*str) {                     \
+                       *str = '\0';            \
+                       str += 1;               \
+               }                               \
+       } while (0)                             \
+
+/*
+ * Note: The str will be modified, so in case of error you should
+ * throw it away and start over
+ */
+gboolean parse_ss_control_string(char *str, int *ss_type,
+                                       char **sc, char **sia,
+                                       char **sib, char **sic,
+                                       char **sid, char **dn)
+{
+       int len = strlen(str);
+       int cur = 0;
+       char *c;
+       unsigned int i;
+       gboolean ret = FALSE;
+
+       /* Minimum is {*,#}SC# */
+       if (len < 4)
+               goto out;
+
+       if (str[0] != '*' && str[0] != '#')
+               goto out;
+
+       cur = 1;
+
+       if (str[1] != '*' && str[1] != '#' && str[1] > '9' && str[1] < '0')
+               goto out;
+
+       if (str[0] == '#' && str[1] == '*')
+               goto out;
+
+       if (str[1] == '#' || str[1] == '*')
+               cur = 2;
+
+       if (str[0] == '*' && str[1] == '*')
+               *ss_type = SS_CONTROL_TYPE_REGISTRATION;
+       else if (str[0] == '#' && str[1] == '#')
+               *ss_type = SS_CONTROL_TYPE_ERASURE;
+       else if (str[0] == '*' && str[1] == '#')
+               *ss_type = SS_CONTROL_TYPE_QUERY;
+       else if (str[0] == '*')
+               *ss_type = SS_CONTROL_TYPE_ACTIVATION;
+       else
+               *ss_type = SS_CONTROL_TYPE_DEACTIVATION;
+
+       /* Must have at least one other '#' */
+       c = strrchr(str+cur, '#');
+
+       if (c == NULL)
+               goto out;
+
+       *dn = c+1;
+       *c = '\0';
+
+       if (strlen(*dn) > 0 && !valid_phone_number_format(*dn))
+               goto out;
+
+       c = str+cur;
+
+       NEXT_FIELD(c, *sc);
+
+       /*
+        * According to 22.030 SC is 2 or 3 digits, there can be
+        * an optional digit 'n' if this is a call setup string,
+        * however 22.030 does not define any SC of length 3
+        * with an 'n' present
+        */
+       if (strlen(*sc) < 2 || strlen(*sc) > 3)
+               goto out;
+
+       for (i = 0; i < strlen(*sc); i++)
+               if (!g_ascii_isdigit((*sc)[i]))
+                       goto out;
+
+       NEXT_FIELD(c, *sia);
+       NEXT_FIELD(c, *sib);
+       NEXT_FIELD(c, *sic);
+       NEXT_FIELD(c, *sid);
+
+       if (*c == '\0')
+               ret = TRUE;
+
+out:
+       return ret;
+}
+
+static const char *bearer_class_lut[] = {
+       "Voice",
+       "Data",
+       "Fax",
+       "Sms",
+       "DataSync",
+       "DataAsync",
+       "DataPad",
+       "DataPacket"
+};
+
+const char *bearer_class_to_string(enum bearer_class cls)
+{
+       switch (cls) {
+       case BEARER_CLASS_VOICE:
+               return bearer_class_lut[0];
+       case BEARER_CLASS_DATA:
+               return bearer_class_lut[1];
+       case BEARER_CLASS_FAX:
+               return bearer_class_lut[2];
+       case BEARER_CLASS_SMS:
+               return bearer_class_lut[3];
+       case BEARER_CLASS_DATA_SYNC:
+               return bearer_class_lut[4];
+       case BEARER_CLASS_DATA_ASYNC:
+               return bearer_class_lut[5];
+       case BEARER_CLASS_PACKET:
+               return bearer_class_lut[6];
+       case BEARER_CLASS_PAD:
+               return bearer_class_lut[7];
+       case BEARER_CLASS_DEFAULT:
+       case BEARER_CLASS_SS_DEFAULT:
+               break;
+       };
+
+       return NULL;
+}
+
+const char *registration_status_to_string(int status)
+{
+       switch (status) {
+       case NETWORK_REGISTRATION_STATUS_NOT_REGISTERED:
+               return "unregistered";
+       case NETWORK_REGISTRATION_STATUS_REGISTERED:
+               return "registered";
+       case NETWORK_REGISTRATION_STATUS_SEARCHING:
+               return "searching";
+       case NETWORK_REGISTRATION_STATUS_DENIED:
+               return "denied";
+       case NETWORK_REGISTRATION_STATUS_UNKNOWN:
+               return "unknown";
+       case NETWORK_REGISTRATION_STATUS_ROAMING:
+               return "roaming";
+       }
+
+       return "";
+}
+
+const char *registration_tech_to_string(int tech)
+{
+       switch (tech) {
+       case ACCESS_TECHNOLOGY_GSM:
+               return "gsm";
+       case ACCESS_TECHNOLOGY_GSM_COMPACT:
+               return "gsm";
+       case ACCESS_TECHNOLOGY_UTRAN:
+               return "umts";
+       case ACCESS_TECHNOLOGY_GSM_EGPRS:
+               return "edge";
+       case ACCESS_TECHNOLOGY_UTRAN_HSDPA:
+               return "hspa";
+       case ACCESS_TECHNOLOGY_UTRAN_HSUPA:
+               return "hspa";
+       case ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA:
+               return "hspa";
+       case ACCESS_TECHNOLOGY_EUTRAN:
+               return "lte";
+       default:
+               return "";
+       }
+}
+
+gboolean is_valid_apn(const char *apn)
+{
+       int i;
+       int last_period = 0;
+
+       if (apn[0] == '.' || apn[0] == '\0')
+               return FALSE;
+
+       for (i = 0; apn[i] != '\0'; i++) {
+               if (g_ascii_isalnum(apn[i]))
+                       continue;
+
+               if (apn[i] == '-')
+                       continue;
+
+               if (apn[i] == '.' && (i - last_period) > 1) {
+                       last_period = i;
+                       continue;
+               }
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+const char *ofono_uuid_to_str(const struct ofono_uuid *uuid)
+{
+       static char buf[OFONO_SHA1_UUID_LEN * 2 + 1];
+
+       return encode_hex_own_buf(uuid->uuid, OFONO_SHA1_UUID_LEN, 0, buf);
+}
+
+void ofono_call_init(struct ofono_call *call)
+{
+       memset(call, 0, sizeof(struct ofono_call));
+       call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE;
+       call->clip_validity = CLIP_VALIDITY_NOT_AVAILABLE;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644 (file)
index 0000000..eb006a7
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* 27.007 Section 7.3 <AcT> */
+enum access_technology {
+       ACCESS_TECHNOLOGY_GSM =                 0,
+       ACCESS_TECHNOLOGY_GSM_COMPACT =         1,
+       ACCESS_TECHNOLOGY_UTRAN =               2,
+       ACCESS_TECHNOLOGY_GSM_EGPRS =           3,
+       ACCESS_TECHNOLOGY_UTRAN_HSDPA =         4,
+       ACCESS_TECHNOLOGY_UTRAN_HSUPA =         5,
+       ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA =   6,
+       ACCESS_TECHNOLOGY_EUTRAN =              7,
+};
+
+/* 27.007 Section 7.2 <stat> */
+enum network_registration_status {
+       NETWORK_REGISTRATION_STATUS_NOT_REGISTERED =    0,
+       NETWORK_REGISTRATION_STATUS_REGISTERED =        1,
+       NETWORK_REGISTRATION_STATUS_SEARCHING =         2,
+       NETWORK_REGISTRATION_STATUS_DENIED =            3,
+       NETWORK_REGISTRATION_STATUS_UNKNOWN =           4,
+       NETWORK_REGISTRATION_STATUS_ROAMING =           5,
+};
+
+/* 27.007 Section 7.6 */
+enum clip_validity {
+       CLIP_VALIDITY_VALID =           0,
+       CLIP_VALIDITY_WITHHELD =        1,
+       CLIP_VALIDITY_NOT_AVAILABLE =   2,
+};
+
+/* 27.007 Section 7.30 */
+enum cnap_validity {
+       CNAP_VALIDITY_VALID =           0,
+       CNAP_VALIDITY_WITHHELD =        1,
+       CNAP_VALIDITY_NOT_AVAILABLE =   2,
+};
+
+/* 27.007 Section 7.18 */
+enum call_status {
+       CALL_STATUS_ACTIVE =            0,
+       CALL_STATUS_HELD =              1,
+       CALL_STATUS_DIALING =           2,
+       CALL_STATUS_ALERTING =          3,
+       CALL_STATUS_INCOMING =          4,
+       CALL_STATUS_WAITING =           5,
+       CALL_STATUS_DISCONNECTED
+};
+
+/* 27.007 Section 7.18 */
+enum call_direction {
+       CALL_DIRECTION_MOBILE_ORIGINATED =      0,
+       CALL_DIRECTION_MOBILE_TERMINATED =      1,
+};
+
+/* 27.007 Section 7.11 */
+enum bearer_class {
+       BEARER_CLASS_VOICE =            1,
+       BEARER_CLASS_DATA =             2,
+       BEARER_CLASS_FAX =              4,
+       BEARER_CLASS_DEFAULT =          7,
+       BEARER_CLASS_SMS =              8,
+       BEARER_CLASS_DATA_SYNC =        16,
+       BEARER_CLASS_DATA_ASYNC =       32,
+       /* According to 22.030, types 1-12 */
+       BEARER_CLASS_SS_DEFAULT =       61,
+       BEARER_CLASS_PACKET =           64,
+       BEARER_CLASS_PAD =              128,
+};
+
+/* 22.030 Section 6.5.2 */
+enum ss_control_type {
+       SS_CONTROL_TYPE_ACTIVATION,
+       SS_CONTROL_TYPE_DEACTIVATION,
+       SS_CONTROL_TYPE_QUERY,
+       SS_CONTROL_TYPE_REGISTRATION,
+       SS_CONTROL_TYPE_ERASURE,
+};
+
+/* TS 27.007 Supplementary service notifications +CSSN */
+enum ss_cssi {
+       SS_MO_UNCONDITIONAL_FORWARDING =        0,
+       SS_MO_CONDITIONAL_FORWARDING =          1,
+       SS_MO_CALL_FORWARDED =                  2,
+       SS_MO_CALL_WAITING =                    3,
+       SS_MO_CUG_CALL =                        4,
+       SS_MO_OUTGOING_BARRING =                5,
+       SS_MO_INCOMING_BARRING =                6,
+       SS_MO_CLIR_SUPPRESSION_REJECTED =       7,
+       SS_MO_CALL_DEFLECTED =                  8,
+};
+
+enum ss_cssu {
+       SS_MT_CALL_FORWARDED =                  0,
+       SS_MT_CUG_CALL =                        1,
+       SS_MT_VOICECALL_ON_HOLD =               2,
+       SS_MT_VOICECALL_RETRIEVED =             3,
+       SS_MT_MULTIPARTY_VOICECALL =            4,
+       SS_MT_VOICECALL_HOLD_RELEASED =         5,
+       SS_MT_FORWARD_CHECK_SS_MESSAGE =        6,
+       SS_MT_VOICECALL_IN_TRANSFER =           7,
+       SS_MT_VOICECALL_TRANSFERRED =           8,
+       SS_MT_CALL_DEFLECTED =                  9,
+};
+
+/* 27.007 Section 10.1.10 */
+enum context_status {
+       CONTEXT_STATUS_DEACTIVATED = 0,
+       CONTEXT_STATUS_ACTIVATED = 1,
+};
+
+const char *telephony_error_to_str(const struct ofono_error *error);
+
+gboolean valid_number_format(const char *number, int length);
+gboolean valid_phone_number_format(const char *number);
+gboolean valid_long_phone_number_format(const char *number);
+const char *phone_number_to_string(const struct ofono_phone_number *ph);
+void string_to_phone_number(const char *str, struct ofono_phone_number *ph);
+
+gboolean valid_cdma_phone_number_format(const char *number);
+const char *cdma_phone_number_to_string(
+                               const struct ofono_cdma_phone_number *ph);
+void string_to_cdma_phone_number(const char *str,
+                               struct ofono_cdma_phone_number *ph);
+
+int mmi_service_code_to_bearer_class(int code);
+
+gboolean valid_ussd_string(const char *str, gboolean call_in_progress);
+
+gboolean parse_ss_control_string(char *str, int *ss_type,
+                                       char **sc, char **sia,
+                                       char **sib, char **sic,
+                                       char **sid, char **dn);
+
+const char *ss_control_type_to_string(enum ss_control_type type);
+
+const char *bearer_class_to_string(enum bearer_class cls);
+
+const char *registration_status_to_string(int status);
+const char *registration_tech_to_string(int tech);
+const char *packet_bearer_to_string(int bearer);
+
+gboolean is_valid_apn(const char *apn);
diff --git a/src/ctm.c b/src/ctm.c
new file mode 100644 (file)
index 0000000..9cece8a
--- /dev/null
+++ b/src/ctm.c
@@ -0,0 +1,332 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+#include "common.h"
+
+#define CTM_FLAG_CACHED 0x1
+
+static GSList *g_drivers = NULL;
+
+struct ofono_ctm {
+       DBusMessage *pending;
+       int flags;
+       ofono_bool_t enabled;
+       const struct ofono_ctm_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static DBusMessage *ctm_get_properties_reply(DBusMessage *msg,
+                                               struct ofono_ctm *ctm)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t value;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       value = ctm->enabled;
+       ofono_dbus_dict_append(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &value);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void ctm_signal_enabled(struct ofono_ctm *ctm)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(ctm->atom);
+       ofono_bool_t value = ctm->enabled;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_TEXT_TELEPHONY_INTERFACE,
+                                               "Enabled",
+                                               DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void ctm_set_enabled_callback(const struct ofono_error *error,
+                                       void *data)
+{
+       struct ofono_ctm *ctm = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error setting ctm enabled property");
+
+               reply = __ofono_error_failed(ctm->pending);
+               __ofono_dbus_pending_reply(&ctm->pending, reply);
+
+               return;
+       }
+
+       ctm->enabled = !ctm->enabled;
+
+       reply = dbus_message_new_method_return(ctm->pending);
+       __ofono_dbus_pending_reply(&ctm->pending, reply);
+
+       ctm_signal_enabled(ctm);
+}
+
+static void ctm_query_enabled_callback(const struct ofono_error *error,
+                                               ofono_bool_t enable, void *data)
+{
+       struct ofono_ctm *ctm = data;
+       DBusMessage *reply;
+       ofono_bool_t enabled_old;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBusMessage *reply;
+
+               DBG("Error during ctm enabled query");
+
+               reply = __ofono_error_failed(ctm->pending);
+               __ofono_dbus_pending_reply(&ctm->pending, reply);
+
+               return;
+       }
+
+       ctm->flags |= CTM_FLAG_CACHED;
+
+       enabled_old = ctm->enabled;
+       ctm->enabled = enable;
+
+       reply = ctm_get_properties_reply(ctm->pending, ctm);
+       __ofono_dbus_pending_reply(&ctm->pending, reply);
+
+       if (ctm->enabled != enabled_old)
+               ctm_signal_enabled(ctm);
+}
+
+static DBusMessage *ctm_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_ctm *ctm = data;
+
+       if (ctm->flags & CTM_FLAG_CACHED)
+               return ctm_get_properties_reply(msg, ctm);
+
+       if (ctm->pending)
+               return __ofono_error_busy(msg);
+
+       ctm->pending = dbus_message_ref(msg);
+
+       ctm->driver->query_tty(ctm, ctm_query_enabled_callback, ctm);
+
+       return NULL;
+}
+
+static DBusMessage *ctm_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_ctm *ctm = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+
+       if (ctm->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (g_strcmp0(property, "Enabled") == 0) {
+               dbus_bool_t value;
+               int target;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+               target = value;
+
+               if (ctm->enabled == target)
+                       return dbus_message_new_method_return(msg);
+
+               ctm->pending = dbus_message_ref(msg);
+
+               ctm->driver->set_tty(ctm, target,
+                                       ctm_set_enabled_callback, ctm);
+               return NULL;
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable ctm_methods[] = {
+       { "GetProperties",  "",    "a{sv}",  ctm_get_properties,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SetProperty",    "sv",  "",       ctm_set_property,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable ctm_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+int ofono_ctm_driver_register(const struct ofono_ctm_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d == NULL || d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *)d);
+
+       return 0;
+}
+
+void ofono_ctm_driver_unregister(const struct ofono_ctm_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d == NULL)
+               return;
+
+       g_drivers = g_slist_remove(g_drivers, (void *)d);
+}
+
+static void text_telephony_unregister(struct ofono_atom *atom)
+{
+       struct ofono_ctm *ctm = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(ctm->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(ctm->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_TEXT_TELEPHONY_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_TEXT_TELEPHONY_INTERFACE);
+}
+
+static void text_telephony_remove(struct ofono_atom *atom)
+{
+       struct ofono_ctm *ctm = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (ctm == NULL)
+               return;
+
+       if (ctm->driver && ctm->driver->remove)
+               ctm->driver->remove(ctm);
+
+       g_free(ctm);
+}
+
+struct ofono_ctm *ofono_ctm_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data)
+{
+       struct ofono_ctm *ctm;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       ctm = g_try_new0(struct ofono_ctm, 1);
+       if (ctm == NULL)
+               return NULL;
+
+       ctm->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CTM,
+                                               text_telephony_remove, ctm);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_ctm_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver) != 0)
+                       continue;
+
+               if (drv->probe(ctm, vendor, data) < 0)
+                       continue;
+
+               ctm->driver = drv;
+               break;
+       }
+
+       return ctm;
+}
+
+void ofono_ctm_register(struct ofono_ctm *ctm)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(ctm->atom);
+       const char *path = __ofono_atom_get_path(ctm->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_TEXT_TELEPHONY_INTERFACE,
+                                       ctm_methods, ctm_signals,
+                                       NULL, ctm, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_TEXT_TELEPHONY_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_TEXT_TELEPHONY_INTERFACE);
+       __ofono_atom_register(ctm->atom, text_telephony_unregister);
+}
+
+void ofono_ctm_remove(struct ofono_ctm *ctm)
+{
+       __ofono_atom_free(ctm->atom);
+}
+
+void ofono_ctm_set_data(struct ofono_ctm *ctm, void *data)
+{
+       ctm->driver_data = data;
+}
+
+void *ofono_ctm_get_data(struct ofono_ctm *ctm)
+{
+       return ctm->driver_data;
+}
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100644 (file)
index 0000000..52e3a68
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#define OFONO_ERROR_INTERFACE "org.ofono.Error"
+
+static DBusConnection *g_connection;
+
+static void append_variant(DBusMessageIter *iter,
+                               int type, void *value)
+{
+       char sig[2];
+       DBusMessageIter valueiter;
+
+       sig[0] = type;
+       sig[1] = 0;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               sig, &valueiter);
+
+       dbus_message_iter_append_basic(&valueiter, type, value);
+
+       dbus_message_iter_close_container(iter, &valueiter);
+}
+
+void ofono_dbus_dict_append(DBusMessageIter *dict,
+                       const char *key, int type, void *value)
+{
+       DBusMessageIter keyiter;
+
+       if (type == DBUS_TYPE_STRING) {
+               const char *str = *((const char **) value);
+               if (str == NULL)
+                       return;
+       }
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &keyiter);
+
+       dbus_message_iter_append_basic(&keyiter, DBUS_TYPE_STRING, &key);
+
+       append_variant(&keyiter, type, value);
+
+       dbus_message_iter_close_container(dict, &keyiter);
+}
+
+static void append_array_variant(DBusMessageIter *iter, int type, void *val)
+{
+       DBusMessageIter variant, array;
+       char typesig[2];
+       char arraysig[3];
+       const char **str_array = *(const char ***) val;
+       int i;
+
+       arraysig[0] = DBUS_TYPE_ARRAY;
+       arraysig[1] = typesig[0] = type;
+       arraysig[2] = typesig[1] = '\0';
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               arraysig, &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                                               typesig, &array);
+
+       for (i = 0; str_array[i]; i++)
+               dbus_message_iter_append_basic(&array, type,
+                                               &(str_array[i]));
+
+       dbus_message_iter_close_container(&variant, &array);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+void ofono_dbus_dict_append_array(DBusMessageIter *dict, const char *key,
+                               int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       append_array_variant(&entry, type, val);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_dict_variant(DBusMessageIter *iter, int type, void *val)
+{
+       DBusMessageIter variant, array, entry;
+       char typesig[5];
+       char arraysig[6];
+       const void **val_array = *(const void ***) val;
+       int i;
+
+       arraysig[0] = DBUS_TYPE_ARRAY;
+       arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+       arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
+       arraysig[3] = typesig[2] = type;
+       arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
+       arraysig[5] = typesig[4] = '\0';
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               arraysig, &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                                               typesig, &array);
+
+       for (i = 0; val_array[i]; i += 2) {
+               dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+                                               &(val_array[i + 0]));
+
+               /*
+                * D-Bus expects a char** or uint8* depending on the type
+                * given. Since we are dealing with an array through a void**
+                * (and thus val_array[i] is a pointer) we need to
+                * differentiate DBUS_TYPE_STRING from the others. The other
+                * option would be the user to pass the exact type to this
+                * function, instead of a pointer to it. However in this case
+                * a cast from type to void* would be needed, which is not
+                * good.
+                */
+               if (type == DBUS_TYPE_STRING) {
+                       dbus_message_iter_append_basic(&entry, type,
+                                                       &(val_array[i + 1]));
+               } else {
+                       dbus_message_iter_append_basic(&entry, type,
+                                                       val_array[i + 1]);
+               }
+
+               dbus_message_iter_close_container(&array, &entry);
+       }
+
+       dbus_message_iter_close_container(&variant, &array);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+void ofono_dbus_dict_append_dict(DBusMessageIter *dict, const char *key,
+                               int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       append_dict_variant(&entry, type, val);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+int ofono_dbus_signal_property_changed(DBusConnection *conn,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *name,
+                                       int type, void *value)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL) {
+               ofono_error("Unable to allocate new %s.PropertyChanged signal",
+                               interface);
+               return -1;
+       }
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       append_variant(&iter, type, value);
+
+       return g_dbus_send_message(conn, signal);
+}
+
+int ofono_dbus_signal_array_property_changed(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name,
+                                               int type, void *value)
+
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL) {
+               ofono_error("Unable to allocate new %s.PropertyChanged signal",
+                               interface);
+               return -1;
+       }
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       append_array_variant(&iter, type, value);
+
+       return g_dbus_send_message(conn, signal);
+}
+
+int ofono_dbus_signal_dict_property_changed(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name,
+                                               int type, void *value)
+
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL) {
+               ofono_error("Unable to allocate new %s.PropertyChanged signal",
+                               interface);
+               return -1;
+       }
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       append_dict_variant(&iter, type, value);
+
+       return g_dbus_send_message(conn, signal);
+}
+
+DBusMessage *__ofono_error_invalid_args(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
+                                       ".InvalidArguments",
+                                       "Invalid arguments in method call");
+}
+
+DBusMessage *__ofono_error_invalid_format(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
+                                       ".InvalidFormat",
+                                       "Argument format is not recognized");
+}
+
+DBusMessage *__ofono_error_not_implemented(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
+                                       ".NotImplemented",
+                                       "Implementation not provided");
+}
+
+DBusMessage *__ofono_error_failed(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Failed",
+                                       "Operation failed");
+}
+
+DBusMessage *__ofono_error_busy(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InProgress",
+                                       "Operation already in progress");
+}
+
+DBusMessage *__ofono_error_not_found(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotFound",
+                       "Object is not found or not valid for this operation");
+}
+
+DBusMessage *__ofono_error_not_active(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotActive",
+                       "Operation is not active or in progress");
+}
+
+DBusMessage *__ofono_error_not_supported(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
+                                       ".NotSupported",
+                                       "Operation is not supported by the"
+                                       " network / modem");
+}
+
+DBusMessage *__ofono_error_not_available(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE
+                                       ".NotAvailable",
+                                       "Operation currently not available");
+}
+
+DBusMessage *__ofono_error_timed_out(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Timedout",
+                       "Operation failure due to timeout");
+}
+
+DBusMessage *__ofono_error_sim_not_ready(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".SimNotReady",
+                       "SIM is not ready or not inserted");
+}
+
+DBusMessage *__ofono_error_in_use(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InUse",
+                       "The resource is currently in use");
+}
+
+DBusMessage *__ofono_error_not_attached(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotAttached",
+                       "GPRS is not attached");
+}
+
+DBusMessage *__ofono_error_attach_in_progress(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg,
+                               OFONO_ERROR_INTERFACE ".AttachInProgress",
+                               "GPRS Attach is in progress");
+}
+
+DBusMessage *__ofono_error_not_registered(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg,
+                               OFONO_ERROR_INTERFACE ".NotRegistered",
+                               "Modem is not registered to the network");
+}
+
+DBusMessage *__ofono_error_canceled(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Canceled",
+                                       "Operation has been canceled");
+}
+
+DBusMessage *__ofono_error_access_denied(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".AccessDenied",
+                                       "Operation not permitted");
+}
+
+DBusMessage *__ofono_error_emergency_active(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg,
+                               OFONO_ERROR_INTERFACE ".EmergencyActive",
+                               "Emergency mode active");
+}
+
+void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       g_dbus_send_message(conn, reply);
+
+       dbus_message_unref(*msg);
+       *msg = NULL;
+}
+
+gboolean __ofono_dbus_valid_object_path(const char *path)
+{
+       unsigned int i;
+       char c = '\0';
+
+       if (path == NULL)
+               return FALSE;
+
+       if (path[0] == '\0')
+               return FALSE;
+
+       if (path[0] && !path[1] && path[0] == '/')
+               return TRUE;
+
+       if (path[0] != '/')
+               return FALSE;
+
+       for (i = 0; path[i]; i++) {
+               if (path[i] == '/' && c == '/')
+                       return FALSE;
+
+               c = path[i];
+
+               if (path[i] >= 'a' && path[i] <= 'z')
+                       continue;
+
+               if (path[i] >= 'A' && path[i] <= 'Z')
+                       continue;
+
+               if (path[i] >= '0' && path[i] <= '9')
+                       continue;
+
+               if (path[i] == '_' || path[i] == '/')
+                       continue;
+
+               return FALSE;
+       }
+
+       if (path[i-1] == '/')
+               return FALSE;
+
+       return TRUE;
+}
+
+DBusConnection *ofono_dbus_get_connection(void)
+{
+       return g_connection;
+}
+
+static void dbus_gsm_set_connection(DBusConnection *conn)
+{
+       if (conn && g_connection != NULL)
+               ofono_error("Setting a connection when it is not NULL");
+
+       g_connection = conn;
+}
+
+int __ofono_dbus_init(DBusConnection *conn)
+{
+       dbus_gsm_set_connection(conn);
+
+       return 0;
+}
+
+void __ofono_dbus_cleanup(void)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (conn == NULL || !dbus_connection_get_is_connected(conn))
+               return;
+
+       dbus_gsm_set_connection(NULL);
+}
diff --git a/src/emulator.c b/src/emulator.c
new file mode 100644 (file)
index 0000000..262e782
--- /dev/null
@@ -0,0 +1,1254 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "ofono.h"
+#include "common.h"
+#include "gatserver.h"
+#include "gatppp.h"
+
+#define RING_TIMEOUT 3
+
+struct ofono_emulator {
+       struct ofono_atom *atom;
+       enum ofono_emulator_type type;
+       GAtServer *server;
+       GAtPPP *ppp;
+       gboolean slc;
+       int l_features;
+       int r_features;
+       int events_mode;
+       gboolean events_ind;
+       unsigned char cmee_mode;
+       GSList *indicators;
+       guint callsetup_source;
+       gboolean clip;
+       gboolean ccwa;
+       int pns_id;
+};
+
+struct indicator {
+       char *name;
+       int value;
+       int min;
+       int max;
+       gboolean deferred;
+       gboolean active;
+       gboolean mandatory;
+};
+
+static void emulator_debug(const char *str, void *data)
+{
+       ofono_info("%s: %s\n", (char *)data, str);
+}
+
+static void emulator_disconnect(gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+
+       DBG("%p", em);
+
+       ofono_emulator_remove(em);
+}
+
+static void ppp_connect(const char *iface, const char *local,
+                       const char *remote,
+                       const char *dns1, const char *dns2,
+                       gpointer user_data)
+{
+       DBG("Network Device: %s\n", iface);
+       DBG("IP Address: %s\n", local);
+       DBG("Remote IP Address: %s\n", remote);
+       DBG("Primary DNS Server: %s\n", dns1);
+       DBG("Secondary DNS Server: %s\n", dns2);
+}
+
+static void cleanup_ppp(struct ofono_emulator *em)
+{
+       DBG("");
+
+       g_at_ppp_unref(em->ppp);
+       em->ppp = NULL;
+
+       __ofono_private_network_release(em->pns_id);
+       em->pns_id = 0;
+
+       if (em->server == NULL)
+               return;
+
+       g_at_server_resume(em->server);
+       g_at_server_send_final(em->server, G_AT_SERVER_RESULT_NO_CARRIER);
+}
+
+static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+
+       cleanup_ppp(em);
+}
+
+static void ppp_suspend(gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+
+       DBG("");
+
+       g_at_server_resume(em->server);
+}
+
+static void suspend_server(gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtIO *io = g_at_server_get_io(em->server);
+
+       g_at_server_suspend(em->server);
+
+       if (g_at_ppp_listen(em->ppp, io) == FALSE)
+               cleanup_ppp(em);
+}
+
+static void request_private_network_cb(
+                       const struct ofono_private_network_settings *pns,
+                       void *data)
+{
+       struct ofono_emulator *em = data;
+       GAtIO *io = g_at_server_get_io(em->server);
+
+       if (pns == NULL)
+               goto error;
+
+       em->ppp = g_at_ppp_server_new_full(pns->server_ip, pns->fd);
+       if (em->ppp == NULL) {
+               close(pns->fd);
+               goto badalloc;
+       }
+
+       g_at_ppp_set_server_info(em->ppp, pns->peer_ip,
+                                       pns->primary_dns, pns->secondary_dns);
+
+       g_at_ppp_set_acfc_enabled(em->ppp, TRUE);
+       g_at_ppp_set_pfc_enabled(em->ppp, TRUE);
+
+       g_at_ppp_set_credentials(em->ppp, "", "");
+       g_at_ppp_set_debug(em->ppp, emulator_debug, "PPP");
+
+       g_at_ppp_set_connect_function(em->ppp, ppp_connect, em);
+       g_at_ppp_set_disconnect_function(em->ppp, ppp_disconnect, em);
+       g_at_ppp_set_suspend_function(em->ppp, ppp_suspend, em);
+
+       g_at_server_send_intermediate(em->server, "CONNECT");
+       g_at_io_set_write_done(io, suspend_server, em);
+
+       return;
+
+badalloc:
+       __ofono_private_network_release(em->pns_id);
+
+error:
+       em->pns_id = 0;
+       g_at_server_send_final(em->server, G_AT_SERVER_RESULT_ERROR);
+}
+
+static gboolean dial_call(struct ofono_emulator *em, const char *dial_str)
+{
+       char c = *dial_str;
+
+       DBG("dial call %s", dial_str);
+
+       if (c == '*' || c == '#' || c == 'T' || c == 't') {
+               if (__ofono_private_network_request(request_private_network_cb,
+                                               &em->pns_id, em) == FALSE)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void dial_cb(GAtServer *server, GAtServerRequestType type,
+                               GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtResultIter iter;
+       const char *dial_str;
+
+       DBG("");
+
+       if (type != G_AT_SERVER_REQUEST_TYPE_SET)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, ""))
+               goto error;
+
+       dial_str = g_at_result_iter_raw_line(&iter);
+       if (!dial_str)
+               goto error;
+
+       if (em->ppp)
+               goto error;
+
+       if (!dial_call(em, dial_str))
+               goto error;
+
+       return;
+
+error:
+       g_at_server_send_final(em->server, G_AT_SERVER_RESULT_ERROR);
+}
+
+static void dun_ath_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtResultIter iter;
+       int val;
+
+       DBG("");
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &val) == FALSE)
+                       goto error;
+
+               if (val != 0)
+                       goto error;
+
+               /* Fall through */
+
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               if (em->ppp == NULL)
+                       goto error;
+
+               g_at_ppp_unref(em->ppp);
+               em->ppp = NULL;
+
+               __ofono_private_network_release(em->pns_id);
+               em->pns_id = 0;
+
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+error:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void resume_ppp(gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+
+       g_at_server_suspend(em->server);
+       g_at_ppp_resume(em->ppp);
+}
+
+static void dun_ato_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtIO *io = g_at_server_get_io(em->server);
+       GAtResultIter iter;
+       int val;
+
+       DBG("");
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &val) == FALSE)
+                       goto error;
+
+               if (val != 0)
+                       goto error;
+
+               /* Fall through */
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               if (em->ppp == NULL)
+                       goto error;
+
+               g_at_server_send_intermediate(em->server, "CONNECT");
+               g_at_io_set_write_done(io, resume_ppp, em);
+               break;
+
+       default:
+error:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static struct indicator *find_indicator(struct ofono_emulator *em,
+                                               const char *name, int *index)
+{
+       GSList *l;
+       int i;
+
+       for (i = 1, l = em->indicators; l; l = l->next, i++) {
+               struct indicator *ind = l->data;
+
+               if (g_str_equal(ind->name, name) == FALSE)
+                       continue;
+
+               if (index)
+                       *index = i;
+
+               return ind;
+       }
+
+       return NULL;
+}
+
+static struct ofono_call *find_call_with_status(struct ofono_emulator *em,
+                                                               int status)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(em->atom);
+       struct ofono_voicecall *vc;
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem);
+       if (vc == NULL)
+               return NULL;
+
+       return __ofono_voicecall_find_call_with_status(vc, status);
+}
+
+static void notify_deferred_indicators(GAtServer *server, void *user_data)
+{
+       struct ofono_emulator *em = user_data;
+       int i;
+       char buf[20];
+       GSList *l;
+       struct indicator *ind;
+
+       for (i = 1, l = em->indicators; l; l = l->next, i++) {
+               ind = l->data;
+
+               if (!ind->deferred)
+                       continue;
+
+               if (em->events_mode == 3 && em->events_ind && em->slc &&
+                               ind->active) {
+                       sprintf(buf, "+CIEV: %d,%d", i, ind->value);
+                       g_at_server_send_unsolicited(em->server, buf);
+               }
+
+               ind->deferred = FALSE;
+       }
+}
+
+static gboolean notify_ccwa(void *user_data)
+{
+       struct ofono_emulator *em = user_data;
+       struct ofono_call *c;
+       const char *phone;
+       /*
+        * '+CCWA: "+",' + phone number + phone type on 3 digits max
+        * + terminating null
+        */
+       char str[OFONO_MAX_PHONE_NUMBER_LENGTH + 14 + 1];
+
+       if ((em->type == OFONO_EMULATOR_TYPE_HFP && em->slc == FALSE) ||
+                       !em->ccwa)
+               goto end;
+
+       c = find_call_with_status(em, CALL_STATUS_WAITING);
+
+       if (c && c->clip_validity == CLIP_VALIDITY_VALID) {
+               phone = phone_number_to_string(&c->phone_number);
+               sprintf(str, "+CCWA: \"%s\",%d", phone, c->phone_number.type);
+
+               g_at_server_send_unsolicited(em->server, str);
+       } else
+               g_at_server_send_unsolicited(em->server, "+CCWA: \"\",128");
+
+end:
+       em->callsetup_source = 0;
+
+       return FALSE;
+}
+
+static gboolean notify_ring(void *user_data)
+{
+       struct ofono_emulator *em = user_data;
+       struct ofono_call *c;
+       const char *phone;
+       /*
+        * '+CLIP: "+",' + phone number + phone type on 3 digits max
+        * + terminating null
+        */
+       char str[OFONO_MAX_PHONE_NUMBER_LENGTH + 14 + 1];
+
+       if (em->type == OFONO_EMULATOR_TYPE_HFP && em->slc == FALSE)
+               return TRUE;
+
+       g_at_server_send_unsolicited(em->server, "RING");
+
+       if (!em->clip)
+               return TRUE;
+
+       c = find_call_with_status(em, CALL_STATUS_INCOMING);
+
+       /*
+        * In case of waiting call becoming an incoming call, call status
+        * change may not have been done yet, so try to find waiting call too
+        */
+       if (c == NULL)
+               c = find_call_with_status(em, CALL_STATUS_WAITING);
+
+       if (c == NULL)
+               return TRUE;
+
+       switch (c->clip_validity) {
+       case CLIP_VALIDITY_VALID:
+               phone = phone_number_to_string(&c->phone_number);
+               sprintf(str, "+CLIP: \"%s\",%d", phone, c->phone_number.type);
+               g_at_server_send_unsolicited(em->server, str);
+               break;
+
+       case CLIP_VALIDITY_WITHHELD:
+               g_at_server_send_unsolicited(em->server, "+CLIP: \"\",128");
+               break;
+       }
+
+       return TRUE;
+}
+
+static void brsf_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtResultIter iter;
+       int val;
+       char buf[16];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &val) == FALSE)
+                       goto fail;
+
+               if (val < 0 || val > 127)
+                       goto fail;
+
+               em->r_features = val;
+
+               sprintf(buf, "+BRSF: %d", em->l_features);
+               g_at_server_send_info(em->server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+fail:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cind_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GSList *l;
+       struct indicator *ind;
+       gsize size;
+       int len;
+       char *buf;
+       char *tmp;
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               /*
+                * "+CIND: " + terminating null + number of indicators *
+                * (max of 3 digits in the value + separator)
+                */
+               size = 7 + 1 + (g_slist_length(em->indicators) * 4);
+               buf = g_try_malloc0(size);
+               if (buf == NULL)
+                       goto fail;
+
+               len = sprintf(buf, "+CIND: ");
+               tmp = buf + len;
+
+               for (l = em->indicators; l; l = l->next) {
+                       ind = l->data;
+                       len = sprintf(tmp, "%s%d",
+                                       l == em->indicators ? "" : ",",
+                                       ind->value);
+                       tmp = tmp + len;
+               }
+
+               g_at_server_send_info(em->server, buf, TRUE);
+               g_free(buf);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               /*
+                * '+CIND: ' + terminating null + number of indicators *
+                * ( indicator name + '("",(000,000))' + separator)
+                */
+               size = 8;
+
+               for (l = em->indicators; l; l = l->next) {
+                       ind = l->data;
+                       size += strlen(ind->name) + 15;
+               }
+
+               buf = g_try_malloc0(size);
+               if (buf == NULL)
+                       goto fail;
+
+               len = sprintf(buf, "+CIND: ");
+               tmp = buf + len;
+
+               for (l = em->indicators; l; l = l->next) {
+                       ind = l->data;
+                       len = sprintf(tmp, "%s(\"%s\",(%d%c%d))",
+                                       l == em->indicators ? "" : ",",
+                                       ind->name, ind->min,
+                                       (ind->max - ind->min) == 1 ? ',' : '-',
+                                       ind->max);
+                       tmp = tmp + len;
+               }
+
+               g_at_server_send_info(server, buf, TRUE);
+               g_free(buf);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+fail:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void cmer_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       char buf[32];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               sprintf(buf, "+CMER: %d,0,0,%d,0", em->events_mode,
+                                               em->events_ind);
+               g_at_server_send_info(em->server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               sprintf(buf, "+CMER: (0,3),(0),(0),(0,1),(0)");
+               g_at_server_send_info(em->server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+       {
+               GAtResultIter iter;
+               int mode = em->events_mode;
+               int ind = em->events_ind;
+               int val;
+
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               /* mode */
+               if (!g_at_result_iter_next_number_default(&iter, mode, &mode))
+                       goto fail;
+
+               if (mode != 0 && mode != 3)
+                       goto fail;
+
+               /* keyp */
+               if (!g_at_result_iter_next_number_default(&iter, 0, &val)) {
+                       if (!g_at_result_iter_skip_next(&iter))
+                               goto done;
+                       goto fail;
+               }
+
+               if (val != 0)
+                       goto fail;
+
+               /* disp */
+               if (!g_at_result_iter_next_number_default(&iter, 0, &val)) {
+                       if (!g_at_result_iter_skip_next(&iter))
+                               goto done;
+                       goto fail;
+               }
+
+               if (val != 0)
+                       goto fail;
+
+               /* ind */
+               if (!g_at_result_iter_next_number_default(&iter, ind, &ind)) {
+                       if (!g_at_result_iter_skip_next(&iter))
+                               goto done;
+                       goto fail;
+               }
+
+               if (ind != 0 && ind != 1)
+                       goto fail;
+
+               /* bfr */
+               if (!g_at_result_iter_next_number_default(&iter, 0, &val)) {
+                       if (!g_at_result_iter_skip_next(&iter))
+                               goto done;
+                       goto fail;
+               }
+
+               if (val != 0)
+                       goto fail;
+
+               /* check that bfr is last parameter */
+               if (g_at_result_iter_skip_next(&iter))
+                       goto fail;
+
+done:
+               em->events_mode = mode;
+               em->events_ind = ind;
+
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+
+               em->slc = TRUE;
+               break;
+       }
+
+       default:
+fail:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void clip_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtResultIter iter;
+       int val;
+
+       if (em->slc == FALSE)
+               goto fail;
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               if (!g_at_result_iter_next_number(&iter, &val))
+                       goto fail;
+
+               if (val != 0 && val != 1)
+                       goto fail;
+
+               /* check this is last parameter */
+               if (g_at_result_iter_skip_next(&iter))
+                       goto fail;
+
+               em->clip = val;
+
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+fail:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+       };
+}
+
+static void ccwa_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtResultIter iter;
+       int val;
+       struct indicator *call_ind;
+       struct indicator *cs_ind;
+
+       if (em->slc == FALSE)
+               goto fail;
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               if (!g_at_result_iter_next_number(&iter, &val))
+                       goto fail;
+
+               if (val != 0 && val != 1)
+                       goto fail;
+
+               /* check this is last parameter */
+               if (g_at_result_iter_skip_next(&iter))
+                       goto fail;
+
+               call_ind = find_indicator(em, OFONO_EMULATOR_IND_CALL, NULL);
+               cs_ind = find_indicator(em, OFONO_EMULATOR_IND_CALLSETUP, NULL);
+
+               if (cs_ind->value == OFONO_EMULATOR_CALLSETUP_INCOMING &&
+                               call_ind->value == OFONO_EMULATOR_CALL_ACTIVE &&
+                               em->ccwa == FALSE && val == 1)
+                       em->callsetup_source = g_timeout_add_seconds(0,
+                                                       notify_ccwa, em);
+
+               em->ccwa = val;
+
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+fail:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+       };
+}
+
+static void cmee_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+       GAtResultIter iter;
+       int val;
+       char buf[16];
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               if (g_at_result_iter_next_number(&iter, &val) == FALSE)
+                       goto fail;
+
+               if (val != 0 && val != 1)
+                       goto fail;
+
+               em->cmee_mode = val;
+
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               sprintf(buf, "+CMEE: %d", em->cmee_mode);
+               g_at_server_send_info(em->server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               /* HFP only support 0 and 1 */
+               sprintf(buf, "+CMEE: (0,1)");
+               g_at_server_send_info(em->server, buf, TRUE);
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       default:
+fail:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void bia_cb(GAtServer *server, GAtServerRequestType type,
+                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_emulator *em = user_data;
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+       {
+               GAtResultIter iter;
+               GSList *l;
+               int val;
+
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               /* check validity of the request */
+               while (g_at_result_iter_next_number_default(&iter, 0, &val))
+                       if (val != 0 &&  val != 1)
+                               goto fail;
+
+               /* Check that we have no non-numbers in the stream */
+               if (g_at_result_iter_skip_next(&iter) == TRUE)
+                       goto fail;
+
+               /* request is valid, update the indicator activation status */
+               g_at_result_iter_init(&iter, result);
+               g_at_result_iter_next(&iter, "");
+
+               for (l = em->indicators; l; l = l->next) {
+                       struct indicator *ind = l->data;
+
+                       if (g_at_result_iter_next_number_default(&iter,
+                                               ind->active, &val) == FALSE)
+                               break;
+
+                       if (ind->mandatory == TRUE)
+                               continue;
+
+                       ind->active = val;
+               }
+
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+               break;
+       }
+
+       default:
+fail:
+               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       }
+}
+
+static void emulator_add_indicator(struct ofono_emulator *em, const char* name,
+                                       int min, int max, int dflt,
+                                       gboolean mandatory)
+{
+       struct indicator *ind;
+
+       ind = g_try_new0(struct indicator, 1);
+       if (ind == NULL) {
+               ofono_error("Unable to allocate indicator structure");
+               return;
+       }
+
+       ind->name = g_strdup(name);
+       ind->min = min;
+       ind->max = max;
+       ind->value = dflt;
+       ind->active = TRUE;
+       ind->mandatory = mandatory;
+
+       em->indicators = g_slist_append(em->indicators, ind);
+}
+
+static void emulator_unregister(struct ofono_atom *atom)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+       GSList *l;
+
+       DBG("%p", em);
+
+       if (em->callsetup_source) {
+               g_source_remove(em->callsetup_source);
+               em->callsetup_source = 0;
+       }
+
+       for (l = em->indicators; l; l = l->next) {
+               struct indicator *ind = l->data;
+
+               g_free(ind->name);
+               g_free(ind);
+       }
+
+       g_slist_free(em->indicators);
+       em->indicators = NULL;
+
+       g_at_ppp_unref(em->ppp);
+       em->ppp = NULL;
+
+       if (em->pns_id > 0) {
+               __ofono_private_network_release(em->pns_id);
+               em->pns_id = 0;
+       }
+
+       g_at_server_unref(em->server);
+       em->server = NULL;
+}
+
+void ofono_emulator_register(struct ofono_emulator *em, int fd)
+{
+       GIOChannel *io;
+
+       DBG("%p, %d", em, fd);
+
+       if (fd < 0)
+               return;
+
+       io = g_io_channel_unix_new(fd);
+
+       em->server = g_at_server_new(io);
+       if (em->server == NULL)
+               return;
+
+       g_io_channel_unref(io);
+
+       g_at_server_set_debug(em->server, emulator_debug, "Server");
+       g_at_server_set_disconnect_function(em->server,
+                                               emulator_disconnect, em);
+       g_at_server_set_finish_callback(em->server, notify_deferred_indicators,
+                                               em);
+
+       if (em->type == OFONO_EMULATOR_TYPE_HFP) {
+               emulator_add_indicator(em, OFONO_EMULATOR_IND_SERVICE, 0, 1, 0,
+                                                                       FALSE);
+               emulator_add_indicator(em, OFONO_EMULATOR_IND_CALL, 0, 1, 0,
+                                                                       TRUE);
+               emulator_add_indicator(em, OFONO_EMULATOR_IND_CALLSETUP, 0, 3,
+                                                               0, TRUE);
+               emulator_add_indicator(em, OFONO_EMULATOR_IND_CALLHELD, 0, 2,
+                                                               0, TRUE);
+               emulator_add_indicator(em, OFONO_EMULATOR_IND_SIGNAL, 0, 5, 0,
+                                                                       FALSE);
+               emulator_add_indicator(em, OFONO_EMULATOR_IND_ROAMING, 0, 1, 0,
+                                                                       FALSE);
+               emulator_add_indicator(em, OFONO_EMULATOR_IND_BATTERY, 0, 5, 5,
+                                                                       FALSE);
+
+               g_at_server_register(em->server, "+BRSF", brsf_cb, em, NULL);
+               g_at_server_register(em->server, "+CIND", cind_cb, em, NULL);
+               g_at_server_register(em->server, "+CMER", cmer_cb, em, NULL);
+               g_at_server_register(em->server, "+CLIP", clip_cb, em, NULL);
+               g_at_server_register(em->server, "+CCWA", ccwa_cb, em, NULL);
+               g_at_server_register(em->server, "+CMEE", cmee_cb, em, NULL);
+               g_at_server_register(em->server, "+BIA", bia_cb, em, NULL);
+       }
+
+       __ofono_atom_register(em->atom, emulator_unregister);
+
+       switch (em->type) {
+       case OFONO_EMULATOR_TYPE_DUN:
+               g_at_server_register(em->server, "D", dial_cb, em, NULL);
+               g_at_server_register(em->server, "H", dun_ath_cb, em, NULL);
+               g_at_server_register(em->server, "O", dun_ato_cb, em, NULL);
+               break;
+       case OFONO_EMULATOR_TYPE_HFP:
+               g_at_server_set_echo(em->server, FALSE);
+               break;
+       default:
+               break;
+       }
+}
+
+static void emulator_remove(struct ofono_atom *atom)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       g_free(em);
+}
+
+struct ofono_emulator *ofono_emulator_create(struct ofono_modem *modem,
+                                               enum ofono_emulator_type type)
+{
+       struct ofono_emulator *em;
+       enum ofono_atom_type atom_t;
+
+       DBG("modem: %p, type: %d", modem, type);
+
+       if (type == OFONO_EMULATOR_TYPE_DUN)
+               atom_t = OFONO_ATOM_TYPE_EMULATOR_DUN;
+       else if (type == OFONO_EMULATOR_TYPE_HFP)
+               atom_t = OFONO_ATOM_TYPE_EMULATOR_HFP;
+       else
+               return NULL;
+
+       em = g_try_new0(struct ofono_emulator, 1);
+
+       if (em == NULL)
+               return NULL;
+
+       em->type = type;
+       em->l_features |= HFP_AG_FEATURE_3WAY;
+       em->l_features |= HFP_AG_FEATURE_REJECT_CALL;
+       em->l_features |= HFP_AG_FEATURE_ENHANCED_CALL_STATUS;
+       em->l_features |= HFP_AG_FEATURE_ENHANCED_CALL_CONTROL;
+       em->l_features |= HFP_AG_FEATURE_EXTENDED_RES_CODE;
+       em->events_mode = 3;    /* default mode is forwarding events */
+       em->cmee_mode = 0;      /* CME ERROR disabled by default */
+
+       em->atom = __ofono_modem_add_atom_offline(modem, atom_t,
+                                                       emulator_remove, em);
+
+       return em;
+}
+
+void ofono_emulator_remove(struct ofono_emulator *em)
+{
+       __ofono_atom_free(em->atom);
+}
+
+void ofono_emulator_send_final(struct ofono_emulator *em,
+                               const struct ofono_error *final)
+{
+       char buf[256];
+
+       /*
+        * TODO: Handle various CMEE modes and report error strings from
+        * common.c
+        */
+       switch (final->type) {
+       case OFONO_ERROR_TYPE_CMS:
+               sprintf(buf, "+CMS ERROR: %d", final->error);
+               g_at_server_send_ext_final(em->server, buf);
+               break;
+
+       case OFONO_ERROR_TYPE_CME:
+               switch (em->cmee_mode) {
+               case 1:
+                       sprintf(buf, "+CME ERROR: %d", final->error);
+                       break;
+
+               case 2:
+                       sprintf(buf, "+CME ERROR: %s",
+                                               telephony_error_to_str(final));
+                       break;
+
+               default:
+                       goto failure;
+               }
+
+               g_at_server_send_ext_final(em->server, buf);
+               break;
+
+       case OFONO_ERROR_TYPE_NO_ERROR:
+               g_at_server_send_final(em->server, G_AT_SERVER_RESULT_OK);
+               break;
+
+       case OFONO_ERROR_TYPE_CEER:
+       case OFONO_ERROR_TYPE_SIM:
+       case OFONO_ERROR_TYPE_FAILURE:
+failure:
+               g_at_server_send_final(em->server, G_AT_SERVER_RESULT_ERROR);
+               break;
+       };
+}
+
+void ofono_emulator_send_unsolicited(struct ofono_emulator *em,
+                                       const char *result)
+{
+       g_at_server_send_unsolicited(em->server, result);
+}
+
+void ofono_emulator_send_intermediate(struct ofono_emulator *em,
+                                       const char *result)
+{
+       g_at_server_send_intermediate(em->server, result);
+}
+
+void ofono_emulator_send_info(struct ofono_emulator *em, const char *line,
+                               ofono_bool_t last)
+{
+       g_at_server_send_info(em->server, line, last);
+}
+
+struct handler {
+       ofono_emulator_request_cb_t cb;
+       void *data;
+       ofono_destroy_func destroy;
+       struct ofono_emulator *em;
+};
+
+struct ofono_emulator_request {
+       GAtResultIter iter;
+       enum ofono_emulator_request_type type;
+};
+
+static void handler_proxy(GAtServer *server, GAtServerRequestType type,
+                               GAtResult *result, gpointer userdata)
+{
+       struct handler *h = userdata;
+       struct ofono_emulator_request req;
+
+       if (h->em->type == OFONO_EMULATOR_TYPE_HFP && h->em->slc == FALSE) {
+               g_at_server_send_final(h->em->server, G_AT_SERVER_RESULT_ERROR);
+               return;
+       }
+
+       switch (type) {
+       case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
+               req.type = OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY;
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SET:
+               req.type = OFONO_EMULATOR_REQUEST_TYPE_SET;
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_QUERY:
+               req.type = OFONO_EMULATOR_REQUEST_TYPE_QUERY;
+               break;
+       case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
+               req.type = OFONO_EMULATOR_REQUEST_TYPE_SUPPORT;
+       }
+
+       g_at_result_iter_init(&req.iter, result);
+       g_at_result_iter_next(&req.iter, "");
+
+       h->cb(h->em, &req, h->data);
+}
+
+static void handler_destroy(gpointer userdata)
+{
+       struct handler *h = userdata;
+
+       if (h->destroy)
+               h->destroy(h->data);
+
+       g_free(h);
+}
+
+ofono_bool_t ofono_emulator_add_handler(struct ofono_emulator *em,
+                                       const char *prefix,
+                                       ofono_emulator_request_cb_t cb,
+                                       void *data, ofono_destroy_func destroy)
+{
+       struct handler *h;
+
+       h = g_new0(struct handler, 1);
+       h->cb = cb;
+       h->data = data;
+       h->destroy = destroy;
+       h->em = em;
+
+       if (g_at_server_register(em->server, prefix, handler_proxy, h,
+                                       handler_destroy) == TRUE)
+               return TRUE;
+
+       g_free(h);
+
+       return FALSE;
+}
+
+ofono_bool_t ofono_emulator_remove_handler(struct ofono_emulator *em,
+                                               const char *prefix)
+{
+       return g_at_server_unregister(em->server, prefix);
+}
+
+ofono_bool_t ofono_emulator_request_next_string(
+                                       struct ofono_emulator_request *req,
+                                       const char **str)
+{
+       return g_at_result_iter_next_string(&req->iter, str);
+}
+
+ofono_bool_t ofono_emulator_request_next_number(
+                                       struct ofono_emulator_request *req,
+                                       int *number)
+{
+       return g_at_result_iter_next_number(&req->iter, number);
+}
+
+const char *ofono_emulator_request_get_raw(struct ofono_emulator_request *req)
+{
+       return g_at_result_iter_raw_line(&req->iter);
+}
+
+enum ofono_emulator_request_type ofono_emulator_request_get_type(
+                                       struct ofono_emulator_request *req)
+{
+       return req->type;
+}
+
+void ofono_emulator_set_indicator(struct ofono_emulator *em,
+                                       const char *name, int value)
+{
+       int i;
+       char buf[20];
+       struct indicator *ind;
+       struct indicator *call_ind;
+       struct indicator *cs_ind;
+       gboolean call;
+       gboolean callsetup;
+       gboolean waiting;
+
+       ind = find_indicator(em, name, &i);
+
+       if (ind == NULL || ind->value == value || value < ind->min
+                       || value > ind->max)
+               return;
+
+       ind->value = value;
+
+       call_ind = find_indicator(em, OFONO_EMULATOR_IND_CALL, NULL);
+       cs_ind = find_indicator(em, OFONO_EMULATOR_IND_CALLSETUP, NULL);
+
+       call = ind == call_ind;
+       callsetup = ind == cs_ind;
+
+       /*
+        * When callsetup indicator goes to Incoming and there is an active
+        * call a +CCWA should be sent before +CIEV
+        */
+       waiting = (callsetup && value == OFONO_EMULATOR_CALLSETUP_INCOMING &&
+                       call_ind->value == OFONO_EMULATOR_CALL_ACTIVE);
+
+       if (waiting)
+               notify_ccwa(em);
+
+       if (em->events_mode == 3 && em->events_ind && em->slc && ind->active) {
+               if (!g_at_server_command_pending(em->server)) {
+                       sprintf(buf, "+CIEV: %d,%d", i, ind->value);
+                       g_at_server_send_unsolicited(em->server, buf);
+               } else
+                       ind->deferred = TRUE;
+       }
+
+       /*
+        * Ring timer should be started when:
+        * - callsetup indicator is set to Incoming and there is no active call
+        *   (not a waiting call)
+        * - or call indicator is set to inactive while callsetup is already
+        *   set to Incoming.
+        * In those cases, a first RING should be sent just after the +CIEV
+        * Ring timer should be stopped for all other values of callsetup
+        */
+       if (waiting)
+               return;
+
+       /* Call state went from active/held + waiting -> incoming */
+       if (call && value == OFONO_EMULATOR_CALL_INACTIVE &&
+                       cs_ind->value == OFONO_EMULATOR_CALLSETUP_INCOMING)
+               goto start_ring;
+
+       if (!callsetup)
+               return;
+
+       if (value != OFONO_EMULATOR_CALLSETUP_INCOMING) {
+               if (em->callsetup_source > 0) {
+                       g_source_remove(em->callsetup_source);
+                       em->callsetup_source = 0;
+               }
+
+               return;
+       }
+
+start_ring:
+       notify_ring(em);
+       em->callsetup_source = g_timeout_add_seconds(RING_TIMEOUT,
+                                                       notify_ring, em);
+}
diff --git a/src/genbuiltin b/src/genbuiltin
new file mode 100755 (executable)
index 0000000..d64bc72
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in $*
+do
+       echo "extern struct ofono_plugin_desc __ofono_builtin_$i;"
+done
+
+echo
+echo "static struct ofono_plugin_desc *__ofono_builtin[] = {"
+
+for i in $*
+do
+       echo "  &__ofono_builtin_$i,"
+done
+
+echo "  NULL"
+echo "};"
diff --git a/src/gnss.c b/src/gnss.c
new file mode 100644 (file)
index 0000000..f4c3311
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <gdbus.h>
+#include <errno.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "gnssagent.h"
+
+static GSList *g_drivers = NULL;
+
+struct ofono_gnss {
+       const struct ofono_gnss_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       DBusMessage *pending;
+       struct gnss_agent *posr_agent;
+       ofono_bool_t enabled;
+};
+
+static void gnss_unregister_agent_cb(const struct ofono_error *error,
+                                       void *data)
+{
+       DBusMessage *reply;
+       struct ofono_gnss *gnss = data;
+
+       DBG("");
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               ofono_error("Disabling Location Reporting Failed");
+
+       gnss->enabled = FALSE;
+
+       if (gnss->posr_agent)
+               gnss_agent_free(gnss->posr_agent);
+
+       reply = dbus_message_new_method_return(gnss->pending);
+       __ofono_dbus_pending_reply(&gnss->pending, reply);
+}
+
+static void gnss_disable_posr_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_gnss *gnss = data;
+
+       gnss->enabled = FALSE;
+}
+
+static void gnss_register_agent_cb(const struct ofono_error *error,
+                                       void *data)
+{
+       DBusMessage *reply;
+       struct ofono_gnss *gnss = data;
+
+       DBG("");
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Enabling Location Reporting Failed");
+               reply = __ofono_error_failed(gnss->pending);
+
+               if (gnss->posr_agent)
+                       gnss_agent_free(gnss->posr_agent);
+
+               __ofono_dbus_pending_reply(&gnss->pending, reply);
+               return;
+       }
+
+       reply = dbus_message_new_method_return(gnss->pending);
+       __ofono_dbus_pending_reply(&gnss->pending, reply);
+
+       gnss->enabled = TRUE;
+
+       if (gnss->posr_agent == NULL)
+               gnss->driver->set_position_reporting(gnss, FALSE,
+                                                       gnss_disable_posr_cb,
+                                                       gnss);
+}
+
+static void gnss_agent_notify(gpointer user_data)
+{
+       struct ofono_gnss *gnss = user_data;
+
+       gnss->posr_agent = NULL;
+
+       if (gnss->enabled == TRUE)
+               gnss->driver->set_position_reporting(gnss, FALSE,
+                                                       gnss_disable_posr_cb,
+                                                       gnss);
+}
+
+static DBusMessage *gnss_register_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_gnss *gnss = data;
+       const char *agent_path;
+
+       if (gnss->pending)
+               return __ofono_error_busy(msg);
+
+       if (gnss->posr_agent)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH,
+                               &agent_path, DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_dbus_valid_object_path(agent_path))
+               return __ofono_error_invalid_format(msg);
+
+       gnss->posr_agent = gnss_agent_new(agent_path,
+                                               dbus_message_get_sender(msg));
+
+       if (gnss->posr_agent == NULL)
+               return __ofono_error_failed(msg);
+
+       gnss_agent_set_removed_notify(gnss->posr_agent,
+                                       gnss_agent_notify, gnss);
+
+       gnss->driver->set_position_reporting(gnss, TRUE, gnss_register_agent_cb,
+                                               gnss);
+
+       gnss->pending = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *gnss_unregister_agent(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_gnss *gnss = data;
+       const char *agent_path;
+       const char *agent_bus = dbus_message_get_sender(msg);
+
+       if (gnss->pending)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (gnss->posr_agent == NULL)
+               return __ofono_error_failed(msg);
+
+       if (!gnss_agent_matches(gnss->posr_agent, agent_path, agent_bus))
+               return __ofono_error_access_denied(msg);
+
+       gnss->pending = dbus_message_ref(msg);
+
+       gnss->enabled = FALSE;
+       gnss->driver->set_position_reporting(gnss, FALSE,
+                                               gnss_unregister_agent_cb,
+                                               gnss);
+
+       return NULL;
+}
+
+static void gnss_send_element_cb(const struct ofono_error *error,
+                               void *data)
+{
+       DBusMessage *reply;
+       struct ofono_gnss *gnss = data;
+
+       DBG("");
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Sending Positioning Element failed");
+               reply = __ofono_error_failed(gnss->pending);
+       } else
+               reply = dbus_message_new_method_return(gnss->pending);
+
+       __ofono_dbus_pending_reply(&gnss->pending, reply);
+}
+
+static DBusMessage *gnss_send_element(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *caller = dbus_message_get_sender(msg);
+       struct ofono_gnss *gnss = data;
+       const char *xml;
+
+       DBG("");
+
+       if (gnss->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &xml,
+                                       DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       if (gnss->posr_agent == NULL)
+               return __ofono_error_not_available(msg);
+
+       if (!gnss_agent_sender_matches(gnss->posr_agent, caller))
+               return __ofono_error_access_denied(msg);
+
+       gnss->pending = dbus_message_ref(msg);
+
+       gnss->driver->send_element(gnss, xml, gnss_send_element_cb, gnss);
+
+       return NULL;
+}
+
+static GDBusMethodTable gnss_methods[] = {
+       { "SendPositioningElement",             "s",    "",
+                       gnss_send_element, G_DBUS_METHOD_FLAG_ASYNC },
+       { "RegisterPositioningRequestAgent",    "o",    "",
+                       gnss_register_agent, G_DBUS_METHOD_FLAG_ASYNC },
+       { "UnregisterPositioningRequestAgent",  "o",    "",
+                       gnss_unregister_agent, G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static void gnss_unregister(struct ofono_atom *atom)
+{
+       struct ofono_gnss *gnss = __ofono_atom_get_data(atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       if (gnss->posr_agent)
+               gnss_agent_free(gnss->posr_agent);
+
+       ofono_modem_remove_interface(modem, OFONO_GNSS_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_GNSS_INTERFACE);
+}
+
+static void gnss_remove(struct ofono_atom *atom)
+{
+       struct ofono_gnss *gnss = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (gnss == NULL)
+               return;
+
+       if (gnss->driver && gnss->driver->remove)
+               gnss->driver->remove(gnss);
+
+       g_free(gnss);
+}
+
+void ofono_gnss_register(struct ofono_gnss *gnss)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(gnss->atom);
+       const char *path = __ofono_atom_get_path(gnss->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_GNSS_INTERFACE,
+                                       gnss_methods, NULL, NULL,
+                                       gnss, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_GNSS_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_GNSS_INTERFACE);
+
+       __ofono_atom_register(gnss->atom, gnss_unregister);
+}
+
+int ofono_gnss_driver_register(const struct ofono_gnss_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_gnss_driver_unregister(const struct ofono_gnss_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+struct ofono_gnss *ofono_gnss_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_gnss *gnss;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       gnss = g_try_new0(struct ofono_gnss, 1);
+
+       if (gnss == NULL)
+               return NULL;
+
+       gnss->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_GNSS,
+                                               gnss_remove, gnss);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_gnss_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(gnss, vendor, data) < 0)
+                       continue;
+
+               gnss->driver = drv;
+               break;
+       }
+
+       return gnss;
+}
+
+void ofono_gnss_notify_posr_request(struct ofono_gnss *gnss, const char *xml)
+{
+       if (gnss->posr_agent)
+               gnss_agent_receive_request(gnss->posr_agent, xml);
+}
+
+void ofono_gnss_notify_posr_reset(struct ofono_gnss *gnss)
+{
+       if (gnss->posr_agent)
+               gnss_agent_receive_reset(gnss->posr_agent);
+}
+
+void ofono_gnss_remove(struct ofono_gnss *gnss)
+{
+       __ofono_atom_free(gnss->atom);
+}
+
+void ofono_gnss_set_data(struct ofono_gnss *gnss, void *data)
+{
+       gnss->driver_data = data;
+}
+
+void *ofono_gnss_get_data(struct ofono_gnss *gnss)
+{
+       return gnss->driver_data;
+}
diff --git a/src/gnssagent.c b/src/gnssagent.c
new file mode 100644 (file)
index 0000000..56d00f9
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+#include "gnssagent.h"
+
+struct gnss_agent {
+       char *path;
+       char *bus;
+       guint disconnect_watch;
+       ofono_destroy_func removed_cb;
+       void *removed_data;
+};
+
+static void gnss_agent_send_noreply(struct gnss_agent *agent,
+                                       const char *method, int type, ...)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *message;
+       va_list args;
+
+       message = dbus_message_new_method_call(agent->bus, agent->path,
+                                       OFONO_GNSS_POSR_AGENT_INTERFACE,
+                                       method);
+
+       va_start(args, type);
+       dbus_message_append_args_valist(message, type, args);
+       va_end(args);
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(conn, message);
+}
+
+static inline void gnss_agent_send_release(struct gnss_agent *agent)
+{
+       gnss_agent_send_noreply(agent, "Release", DBUS_TYPE_INVALID);
+}
+
+void gnss_agent_receive_request(struct gnss_agent *agent, const char *xml)
+{
+       gnss_agent_send_noreply(agent, "Request", DBUS_TYPE_STRING, &xml,
+                               DBUS_TYPE_INVALID);
+}
+
+void gnss_agent_receive_reset(struct gnss_agent *agent)
+{
+       gnss_agent_send_noreply(agent, "ResetAssistanceData",
+                               DBUS_TYPE_INVALID);
+}
+
+ofono_bool_t gnss_agent_matches(struct gnss_agent *agent,
+                               const char *path, const char *sender)
+{
+       return g_str_equal(agent->path, path) &&
+                       g_str_equal(agent->bus, sender);
+}
+
+ofono_bool_t gnss_agent_sender_matches(struct gnss_agent *agent,
+                                       const char *sender)
+{
+       return g_str_equal(agent->bus, sender);
+}
+
+void gnss_agent_set_removed_notify(struct gnss_agent *agent,
+                                       ofono_destroy_func destroy,
+                                       void *user_data)
+{
+       agent->removed_cb = destroy;
+       agent->removed_data = user_data;
+}
+
+void gnss_agent_free(struct gnss_agent *agent)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (agent->disconnect_watch) {
+               gnss_agent_send_release(agent);
+               g_dbus_remove_watch(conn, agent->disconnect_watch);
+               agent->disconnect_watch = 0;
+       }
+
+       if (agent->removed_cb)
+               agent->removed_cb(agent->removed_data);
+
+       g_free(agent->path);
+       g_free(agent->bus);
+       g_free(agent);
+}
+
+static void gnss_agent_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+       struct gnss_agent *agent = user_data;
+
+       agent->disconnect_watch = 0;
+
+       gnss_agent_free(agent);
+}
+
+struct gnss_agent *gnss_agent_new(const char *path, const char *sender)
+{
+       struct gnss_agent *agent = g_try_new0(struct gnss_agent, 1);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (agent == NULL)
+               return NULL;
+
+       agent->path = g_strdup(path);
+       agent->bus = g_strdup(sender);
+
+       agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, sender,
+                                               gnss_agent_disconnect_cb,
+                                               agent, NULL);
+
+       return agent;
+}
diff --git a/src/gnssagent.h b/src/gnssagent.h
new file mode 100644 (file)
index 0000000..ceb8106
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  ST-Ericsson AB.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct gnss_agent;
+
+struct gnss_agent *gnss_agent_new(const char *path, const char *sender);
+
+void gnss_agent_free(struct gnss_agent *agent);
+
+void gnss_agent_receive_request(struct gnss_agent *agent, const char *xml);
+
+void gnss_agent_receive_reset(struct gnss_agent *agent);
+
+void gnss_agent_set_removed_notify(struct gnss_agent *agent,
+                                       ofono_destroy_func removed_cb,
+                                       void *user_data);
+
+ofono_bool_t gnss_agent_matches(struct gnss_agent *agent,
+                               const char *path, const char *sender);
+
+ofono_bool_t gnss_agent_sender_matches(struct gnss_agent *agent,
+                                       const char *sender);
diff --git a/src/gprs-provision.c b/src/gprs-provision.c
new file mode 100644 (file)
index 0000000..011d5a8
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include "ofono.h"
+
+static GSList *g_drivers = NULL;
+
+void  __ofono_gprs_provision_free_settings(
+                               struct ofono_gprs_provision_data *settings,
+                               int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               g_free(settings[i].name);
+               g_free(settings[i].apn);
+               g_free(settings[i].username);
+               g_free(settings[i].password);
+               g_free(settings[i].message_proxy);
+               g_free(settings[i].message_center);
+       }
+
+       g_free(settings);
+}
+
+ofono_bool_t __ofono_gprs_provision_get_settings(const char *mcc,
+                               const char *mnc, const char *spn,
+                               struct ofono_gprs_provision_data **settings,
+                               int *count)
+{
+       GSList *d;
+
+       if (mcc == NULL || strlen(mcc) == 0 || mnc == NULL || strlen(mnc) == 0)
+               return FALSE;
+
+       for (d = g_drivers; d != NULL; d = d->next) {
+               const struct ofono_gprs_provision_driver *driver = d->data;
+
+               if (driver->get_settings == NULL)
+                       continue;
+
+               DBG("Calling provisioning plugin '%s'", driver->name);
+
+               if (driver->get_settings(mcc, mnc, spn, settings, count) < 0)
+                       continue;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_gprs_provision_driver *plugin1 = a;
+       const struct ofono_gprs_provision_driver *plugin2 = b;
+
+       return plugin2->priority - plugin1->priority;
+}
+
+int ofono_gprs_provision_driver_register(
+                       const struct ofono_gprs_provision_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       g_drivers = g_slist_insert_sorted(g_drivers, (void *) driver,
+                                               compare_priority);
+       return 0;
+}
+
+void ofono_gprs_provision_driver_unregister(
+                       const struct ofono_gprs_provision_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       g_drivers = g_slist_remove(g_drivers, driver);
+}
diff --git a/src/gprs.c b/src/gprs.c
new file mode 100644 (file)
index 0000000..0d25506
--- /dev/null
@@ -0,0 +1,3009 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "storage.h"
+#include "idmap.h"
+#include "simutil.h"
+#include "util.h"
+
+#define GPRS_FLAG_ATTACHING 0x1
+#define GPRS_FLAG_RECHECK 0x2
+
+#define SETTINGS_STORE "gprs"
+#define SETTINGS_GROUP "Settings"
+#define MAX_CONTEXT_NAME_LENGTH 127
+#define MAX_MESSAGE_PROXY_LENGTH 255
+#define MAX_MESSAGE_CENTER_LENGTH 255
+#define MAX_CONTEXTS 256
+#define SUSPEND_TIMEOUT 8
+
+/* 27.007 Section 7.29 */
+enum packet_bearer {
+       PACKET_BEARER_NONE =            0,
+       PACKET_BEARER_GPRS =            1,
+       PACKET_BEARER_EGPRS =           2,
+       PACKET_BEARER_UMTS =            3,
+       PACKET_BEARER_HSUPA =           4,
+       PACKET_BEARER_HSDPA =           5,
+       PACKET_BEARER_HSUPA_HSDPA =     6,
+       PACKET_BEARER_EPS =             7,
+};
+
+struct ofono_gprs {
+       GSList *contexts;
+       ofono_bool_t attached;
+       ofono_bool_t driver_attached;
+       ofono_bool_t roaming_allowed;
+       ofono_bool_t powered;
+       ofono_bool_t suspended;
+       int status;
+       int flags;
+       int bearer;
+       guint suspend_timeout;
+       struct idmap *pid_map;
+       unsigned int last_context_id;
+       struct idmap *cid_map;
+       int netreg_status;
+       struct ofono_netreg *netreg;
+       unsigned int netreg_watch;
+       unsigned int status_watch;
+       GKeyFile *settings;
+       char *imsi;
+       DBusMessage *pending;
+       GSList *context_drivers;
+       const struct ofono_gprs_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       unsigned int spn_watch;
+};
+
+struct ipv4_settings {
+       gboolean static_ip;
+       char *ip;
+       char *netmask;
+       char *gateway;
+       char **dns;
+       char *proxy;
+};
+
+struct ipv6_settings {
+       char *ip;
+       unsigned char prefix_len;
+       char *gateway;
+       char **dns;
+};
+
+struct context_settings {
+       char *interface;
+       struct ipv4_settings *ipv4;
+       struct ipv6_settings *ipv6;
+};
+
+struct ofono_gprs_context {
+       struct ofono_gprs *gprs;
+       enum ofono_gprs_context_type type;
+       ofono_bool_t inuse;
+       const struct ofono_gprs_context_driver *driver;
+       void *driver_data;
+       struct context_settings *settings;
+       struct ofono_atom *atom;
+};
+
+struct pri_context {
+       ofono_bool_t active;
+       enum ofono_gprs_context_type type;
+       char name[MAX_CONTEXT_NAME_LENGTH + 1];
+       char message_proxy[MAX_MESSAGE_PROXY_LENGTH + 1];
+       char message_center[MAX_MESSAGE_CENTER_LENGTH + 1];
+       unsigned int id;
+       char *path;
+       char *key;
+       char *proxy_host;
+       uint16_t proxy_port;
+       DBusMessage *pending;
+       struct ofono_gprs_primary_context context;
+       struct ofono_gprs_context *context_driver;
+       struct ofono_gprs *gprs;
+};
+
+static void gprs_netreg_update(struct ofono_gprs *gprs);
+static void gprs_deactivate_next(struct ofono_gprs *gprs);
+
+static GSList *g_drivers = NULL;
+static GSList *g_context_drivers = NULL;
+
+const char *packet_bearer_to_string(int bearer)
+{
+       switch (bearer) {
+       case PACKET_BEARER_NONE:
+               return "none";
+       case PACKET_BEARER_GPRS:
+               return "gprs";
+       case PACKET_BEARER_EGPRS:
+               return "edge";
+       case PACKET_BEARER_UMTS:
+               return "umts";
+       case PACKET_BEARER_HSUPA:
+               return "hsupa";
+       case PACKET_BEARER_HSDPA:
+               return "hsdpa";
+       case PACKET_BEARER_HSUPA_HSDPA:
+               return "hspa";
+       case PACKET_BEARER_EPS:
+               return "lte";
+       }
+       return "";
+}
+
+static const char *gprs_context_default_name(enum ofono_gprs_context_type type)
+{
+       switch (type) {
+       case OFONO_GPRS_CONTEXT_TYPE_ANY:
+               return NULL;
+       case OFONO_GPRS_CONTEXT_TYPE_INTERNET:
+               return "Internet";
+       case OFONO_GPRS_CONTEXT_TYPE_MMS:
+               return "MMS";
+       case OFONO_GPRS_CONTEXT_TYPE_WAP:
+               return "WAP";
+       case OFONO_GPRS_CONTEXT_TYPE_IMS:
+               return "IMS";
+       }
+
+       return NULL;
+}
+
+static const char *gprs_context_type_to_string(
+                                       enum ofono_gprs_context_type type)
+{
+       switch (type) {
+       case OFONO_GPRS_CONTEXT_TYPE_ANY:
+               return NULL;
+       case OFONO_GPRS_CONTEXT_TYPE_INTERNET:
+               return "internet";
+       case OFONO_GPRS_CONTEXT_TYPE_MMS:
+               return "mms";
+       case OFONO_GPRS_CONTEXT_TYPE_WAP:
+               return "wap";
+       case OFONO_GPRS_CONTEXT_TYPE_IMS:
+               return "ims";
+       }
+
+       return NULL;
+}
+
+static gboolean gprs_context_string_to_type(const char *str,
+                                       enum ofono_gprs_context_type *out)
+{
+       if (g_str_equal(str, "internet")) {
+               *out = OFONO_GPRS_CONTEXT_TYPE_INTERNET;
+               return TRUE;
+       } else if (g_str_equal(str, "wap")) {
+               *out = OFONO_GPRS_CONTEXT_TYPE_WAP;
+               return TRUE;
+       } else if (g_str_equal(str, "mms")) {
+               *out = OFONO_GPRS_CONTEXT_TYPE_MMS;
+               return TRUE;
+       } else if (g_str_equal(str, "ims")) {
+               *out = OFONO_GPRS_CONTEXT_TYPE_IMS;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static const char *gprs_proto_to_string(enum ofono_gprs_proto proto)
+{
+       switch (proto) {
+       case OFONO_GPRS_PROTO_IP:
+               return "ip";
+       case OFONO_GPRS_PROTO_IPV6:
+               return "ipv6";
+       case OFONO_GPRS_PROTO_IPV4V6:
+               return "dual";
+       };
+
+       return NULL;
+}
+
+static gboolean gprs_proto_from_string(const char *str,
+                                       enum ofono_gprs_proto *proto)
+{
+       if (g_str_equal(str, "ip")) {
+               *proto = OFONO_GPRS_PROTO_IP;
+               return TRUE;
+       } else if (g_str_equal(str, "ipv6")) {
+               *proto = OFONO_GPRS_PROTO_IPV6;
+               return TRUE;
+       } else if (g_str_equal(str, "dual")) {
+               *proto = OFONO_GPRS_PROTO_IPV4V6;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static unsigned int gprs_cid_alloc(struct ofono_gprs *gprs)
+{
+       return idmap_alloc(gprs->cid_map);
+}
+
+static void gprs_cid_release(struct ofono_gprs *gprs, unsigned int id)
+{
+       idmap_put(gprs->cid_map, id);
+}
+
+static gboolean assign_context(struct pri_context *ctx)
+{
+       struct idmap *cidmap = ctx->gprs->cid_map;
+       GSList *l;
+
+       if (cidmap == NULL)
+               return FALSE;
+
+       ctx->context.cid = gprs_cid_alloc(ctx->gprs);
+       if (ctx->context.cid == 0)
+               return FALSE;
+
+       for (l = ctx->gprs->context_drivers; l; l = l->next) {
+               struct ofono_gprs_context *gc = l->data;
+
+               if (gc->inuse == TRUE)
+                       continue;
+
+               if (gc->driver == NULL)
+                       continue;
+
+               if (gc->driver->activate_primary == NULL ||
+                               gc->driver->deactivate_primary == NULL)
+                       continue;
+
+               if (gc->type != OFONO_GPRS_CONTEXT_TYPE_ANY &&
+                               gc->type != ctx->type)
+                       continue;
+
+               ctx->context_driver = gc;
+               ctx->context_driver->inuse = TRUE;
+
+               if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 ||
+                               ctx->context.proto == OFONO_GPRS_PROTO_IP)
+                       gc->settings->ipv4 = g_new0(struct ipv4_settings, 1);
+
+               if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 ||
+                               ctx->context.proto == OFONO_GPRS_PROTO_IPV6)
+                       gc->settings->ipv6 = g_new0(struct ipv6_settings, 1);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void release_context(struct pri_context *ctx)
+{
+       if (ctx == NULL || ctx->gprs == NULL || ctx->context_driver == NULL)
+               return;
+
+       gprs_cid_release(ctx->gprs, ctx->context.cid);
+       ctx->context.cid = 0;
+       ctx->context_driver->inuse = FALSE;
+       ctx->context_driver = NULL;
+       ctx->active = FALSE;
+}
+
+static struct pri_context *gprs_context_by_path(struct ofono_gprs *gprs,
+                                               const char *ctx_path)
+{
+       GSList *l;
+
+       for (l = gprs->contexts; l; l = l->next) {
+               struct pri_context *ctx = l->data;
+
+               if (g_str_equal(ctx_path, ctx->path))
+                       return ctx;
+       }
+
+       return NULL;
+}
+
+static void context_settings_free(struct context_settings *settings)
+{
+       if (settings->ipv4) {
+               g_free(settings->ipv4->ip);
+               g_free(settings->ipv4->netmask);
+               g_free(settings->ipv4->gateway);
+               g_strfreev(settings->ipv4->dns);
+               g_free(settings->ipv4->proxy);
+
+               g_free(settings->ipv4);
+               settings->ipv4 = NULL;
+       }
+
+       if (settings->ipv6) {
+               g_free(settings->ipv6->ip);
+               g_free(settings->ipv6->gateway);
+               g_strfreev(settings->ipv6->dns);
+
+               g_free(settings->ipv6);
+               settings->ipv6 = NULL;
+       }
+
+       g_free(settings->interface);
+       settings->interface = NULL;
+}
+
+static void context_settings_append_ipv4(struct context_settings *settings,
+                                               DBusMessageIter *iter)
+{
+       DBusMessageIter variant;
+       DBusMessageIter array;
+       char typesig[5];
+       char arraysig[6];
+       const char *method;
+
+       arraysig[0] = DBUS_TYPE_ARRAY;
+       arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+       arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
+       arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT;
+       arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
+       arraysig[5] = typesig[4] = '\0';
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               arraysig, &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                                               typesig, &array);
+       if (settings == NULL || settings->ipv4 == NULL)
+               goto done;
+
+       ofono_dbus_dict_append(&array, "Interface",
+                               DBUS_TYPE_STRING, &settings->interface);
+
+       /* If we have a Proxy, no other settings are relevant */
+       if (settings->ipv4->proxy) {
+               ofono_dbus_dict_append(&array, "Proxy", DBUS_TYPE_STRING,
+                                       &settings->ipv4->proxy);
+               goto done;
+       }
+
+       if (settings->ipv4->static_ip == TRUE)
+               method = "static";
+       else
+               method = "dhcp";
+
+       ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method);
+
+       if (settings->ipv4->ip)
+               ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
+                                       &settings->ipv4->ip);
+
+       if (settings->ipv4->netmask)
+               ofono_dbus_dict_append(&array, "Netmask", DBUS_TYPE_STRING,
+                                       &settings->ipv4->netmask);
+
+       if (settings->ipv4->gateway)
+               ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING,
+                                       &settings->ipv4->gateway);
+
+       if (settings->ipv4->dns)
+               ofono_dbus_dict_append_array(&array, "DomainNameServers",
+                                               DBUS_TYPE_STRING,
+                                               &settings->ipv4->dns);
+
+done:
+       dbus_message_iter_close_container(&variant, &array);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+static void context_settings_append_ipv4_dict(struct context_settings *settings,
+                                               DBusMessageIter *dict)
+{
+       DBusMessageIter entry;
+       const char *key = "Settings";
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       context_settings_append_ipv4(settings, &entry);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void context_settings_append_ipv6(struct context_settings *settings,
+                                               DBusMessageIter *iter)
+{
+       DBusMessageIter variant;
+       DBusMessageIter array;
+       char typesig[5];
+       char arraysig[6];
+
+       arraysig[0] = DBUS_TYPE_ARRAY;
+       arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+       arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
+       arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT;
+       arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
+       arraysig[5] = typesig[4] = '\0';
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               arraysig, &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                                               typesig, &array);
+       if (settings == NULL || settings->ipv6 == NULL)
+               goto done;
+
+       ofono_dbus_dict_append(&array, "Interface",
+                               DBUS_TYPE_STRING, &settings->interface);
+
+       if (settings->ipv6->ip)
+               ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
+                                       &settings->ipv6->ip);
+
+       if (settings->ipv6->prefix_len)
+               ofono_dbus_dict_append(&array, "PrefixLength", DBUS_TYPE_BYTE,
+                                       &settings->ipv6->prefix_len);
+
+       if (settings->ipv6->gateway)
+               ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING,
+                                       &settings->ipv6->gateway);
+
+       if (settings->ipv6->dns)
+               ofono_dbus_dict_append_array(&array, "DomainNameServers",
+                                               DBUS_TYPE_STRING,
+                                               &settings->ipv6->dns);
+
+done:
+       dbus_message_iter_close_container(&variant, &array);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+static void context_settings_append_ipv6_dict(struct context_settings *settings,
+                                               DBusMessageIter *dict)
+{
+       DBusMessageIter entry;
+       const char *key = "IPv6.Settings";
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       context_settings_append_ipv6(settings, &entry);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void signal_settings(struct pri_context *ctx, const char *prop,
+               void (*append)(struct context_settings *, DBusMessageIter *))
+
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = ctx->path;
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       struct context_settings *settings;
+
+       signal = dbus_message_new_signal(path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "PropertyChanged");
+
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &prop);
+
+       if (ctx->context_driver)
+               settings = ctx->context_driver->settings;
+       else
+               settings = NULL;
+
+       append(settings, &iter);
+       g_dbus_send_message(conn, signal);
+}
+
+static void pri_context_signal_settings(struct pri_context *ctx,
+                                       gboolean ipv4, gboolean ipv6)
+{
+       if (ipv4)
+               signal_settings(ctx, "Settings",
+                               context_settings_append_ipv4);
+
+       if (ipv6)
+               signal_settings(ctx, "IPv6.Settings",
+                               context_settings_append_ipv6);
+}
+
+static void pri_parse_proxy(struct pri_context *ctx, const char *proxy)
+{
+       char *scheme, *host, *port, *path;
+
+       scheme = g_strdup(proxy);
+       if (scheme == NULL)
+               return;
+
+       host = strstr(scheme, "://");
+       if (host != NULL) {
+               *host = '\0';
+               host += 3;
+
+               if (strcasecmp(scheme, "https") == 0)
+                       ctx->proxy_port = 443;
+               else if (strcasecmp(scheme, "http") == 0)
+                       ctx->proxy_port = 80;
+               else {
+                       g_free(scheme);
+                       return;
+               }
+       } else {
+               host = scheme;
+               ctx->proxy_port = 80;
+       }
+
+       path = strchr(host, '/');
+       if (path != NULL)
+               *(path++) = '\0';
+
+       port = strrchr(host, ':');
+       if (port != NULL) {
+               char *end;
+               int tmp = strtol(port + 1, &end, 10);
+
+               if (*end == '\0') {
+                       *port = '\0';
+                       ctx->proxy_port = tmp;
+               }
+       }
+
+       g_free(ctx->proxy_host);
+       ctx->proxy_host = g_strdup(host);
+
+       g_free(scheme);
+}
+
+static void pri_ifupdown(const char *interface, ofono_bool_t active)
+{
+       struct ifreq ifr;
+       int sk;
+
+       if (interface == NULL)
+               return;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0)
+               goto done;
+
+       if (active == TRUE) {
+               if (ifr.ifr_flags & IFF_UP)
+                       goto done;
+               ifr.ifr_flags |= IFF_UP;
+       } else {
+               if (!(ifr.ifr_flags & IFF_UP))
+                       goto done;
+               ifr.ifr_flags &= ~IFF_UP;
+       }
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
+               ofono_error("Failed to change interface flags");
+
+done:
+       close(sk);
+}
+
+static void pri_set_ipv4_addr(const char *interface, const char *address)
+{
+       struct ifreq ifr;
+       struct sockaddr_in addr;
+       int sk;
+
+       if (interface == NULL)
+               return;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0)
+               goto done;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = address ? inet_addr(address) : INADDR_ANY;
+       memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr));
+
+       if (ioctl(sk, SIOCSIFADDR, &ifr) < 0) {
+               ofono_error("Failed to set interface address");
+               goto done;
+       }
+
+       if (address == NULL)
+               goto done;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr("255.255.255.255");
+       memcpy(&ifr.ifr_netmask, &addr, sizeof(ifr.ifr_netmask));
+
+       if (ioctl(sk, SIOCSIFNETMASK, &ifr) < 0)
+               ofono_error("Failed to set interface netmask");
+
+done:
+       close(sk);
+}
+
+static void pri_setproxy(const char *interface, const char *proxy)
+{
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk;
+
+       if (interface == NULL)
+               return;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return;
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP | RTF_HOST;
+       rt.rt_dev = (char *) interface;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(proxy);
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+
+       if (ioctl(sk, SIOCADDRT, &rt) < 0)
+               ofono_error("Failed to add proxy host route");
+
+       close(sk);
+}
+
+static void pri_reset_context_settings(struct pri_context *ctx)
+{
+       struct context_settings *settings;
+       char *interface;
+       gboolean signal_ipv4;
+       gboolean signal_ipv6;
+
+       if (ctx->context_driver == NULL)
+               return;
+
+       settings = ctx->context_driver->settings;
+
+       interface = settings->interface;
+       settings->interface = NULL;
+
+       signal_ipv4 = settings->ipv4 != NULL;
+       signal_ipv6 = settings->ipv6 != NULL;
+
+       context_settings_free(settings);
+
+       pri_context_signal_settings(ctx, signal_ipv4, signal_ipv6);
+
+       if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+               pri_set_ipv4_addr(interface, NULL);
+
+               g_free(ctx->proxy_host);
+               ctx->proxy_host = NULL;
+               ctx->proxy_port = 0;
+       }
+
+       pri_ifupdown(interface, FALSE);
+
+       g_free(interface);
+}
+
+static void pri_update_mms_context_settings(struct pri_context *ctx)
+{
+       struct ofono_gprs_context *gc = ctx->context_driver;
+       struct context_settings *settings = gc->settings;
+
+       if (ctx->message_proxy)
+               settings->ipv4->proxy = g_strdup(ctx->message_proxy);
+
+       pri_parse_proxy(ctx, ctx->message_proxy);
+
+       DBG("proxy %s port %u", ctx->proxy_host, ctx->proxy_port);
+
+       pri_set_ipv4_addr(settings->interface, settings->ipv4->ip);
+
+       if (ctx->proxy_host)
+               pri_setproxy(settings->interface, ctx->proxy_host);
+}
+
+static void append_context_properties(struct pri_context *ctx,
+                                       DBusMessageIter *dict)
+{
+       const char *type = gprs_context_type_to_string(ctx->type);
+       const char *proto = gprs_proto_to_string(ctx->context.proto);
+       const char *name = ctx->name;
+       dbus_bool_t value;
+       const char *strvalue;
+       struct context_settings *settings;
+
+       ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &name);
+
+       value = ctx->active;
+       ofono_dbus_dict_append(dict, "Active", DBUS_TYPE_BOOLEAN, &value);
+
+       ofono_dbus_dict_append(dict, "Type", DBUS_TYPE_STRING, &type);
+
+       ofono_dbus_dict_append(dict, "Protocol", DBUS_TYPE_STRING, &proto);
+
+       strvalue = ctx->context.apn;
+       ofono_dbus_dict_append(dict, "AccessPointName", DBUS_TYPE_STRING,
+                               &strvalue);
+
+       strvalue = ctx->context.username;
+       ofono_dbus_dict_append(dict, "Username", DBUS_TYPE_STRING,
+                               &strvalue);
+
+       strvalue = ctx->context.password;
+       ofono_dbus_dict_append(dict, "Password", DBUS_TYPE_STRING,
+                               &strvalue);
+
+       if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+               strvalue = ctx->message_proxy;
+               ofono_dbus_dict_append(dict, "MessageProxy",
+                                       DBUS_TYPE_STRING, &strvalue);
+
+               strvalue = ctx->message_center;
+               ofono_dbus_dict_append(dict, "MessageCenter",
+                                       DBUS_TYPE_STRING, &strvalue);
+       }
+
+       if (ctx->context_driver)
+               settings = ctx->context_driver->settings;
+       else
+               settings = NULL;
+
+       context_settings_append_ipv4_dict(settings, dict);
+       context_settings_append_ipv6_dict(settings, dict);
+}
+
+static DBusMessage *pri_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct pri_context *ctx = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       append_context_properties(ctx, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void pri_activate_callback(const struct ofono_error *error, void *data)
+{
+       struct pri_context *ctx = data;
+       struct ofono_gprs_context *gc = ctx->context_driver;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t value;
+
+       DBG("%p", ctx);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Activating context failed with error: %s",
+                               telephony_error_to_str(error));
+               __ofono_dbus_pending_reply(&ctx->pending,
+                                       __ofono_error_failed(ctx->pending));
+               context_settings_free(ctx->context_driver->settings);
+               release_context(ctx);
+               return;
+       }
+
+       ctx->active = TRUE;
+       __ofono_dbus_pending_reply(&ctx->pending,
+                               dbus_message_new_method_return(ctx->pending));
+
+       if (gc->settings->interface != NULL) {
+               pri_ifupdown(gc->settings->interface, TRUE);
+
+               if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS &&
+                               gc->settings->ipv4)
+                       pri_update_mms_context_settings(ctx);
+
+               pri_context_signal_settings(ctx, gc->settings->ipv4 != NULL,
+                                               gc->settings->ipv6 != NULL);
+       }
+
+       value = ctx->active;
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void pri_deactivate_callback(const struct ofono_error *error, void *data)
+{
+       struct pri_context *ctx = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t value;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Deactivating context failed with error: %s",
+                               telephony_error_to_str(error));
+               __ofono_dbus_pending_reply(&ctx->pending,
+                                       __ofono_error_failed(ctx->pending));
+               return;
+       }
+
+       __ofono_dbus_pending_reply(&ctx->pending,
+                               dbus_message_new_method_return(ctx->pending));
+
+       pri_reset_context_settings(ctx);
+       release_context(ctx);
+
+       value = ctx->active;
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static DBusMessage *pri_set_apn(struct pri_context *ctx, DBusConnection *conn,
+                               DBusMessage *msg, const char *apn)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+
+       if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (g_str_equal(apn, ctx->context.apn))
+               return dbus_message_new_method_return(msg);
+
+       if (is_valid_apn(apn) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       strcpy(ctx->context.apn, apn);
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key,
+                                       "AccessPointName", apn);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "AccessPointName",
+                                       DBUS_TYPE_STRING, &apn);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_username(struct pri_context *ctx,
+                                       DBusConnection *conn, DBusMessage *msg,
+                                       const char *username)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+
+       if (strlen(username) > OFONO_GPRS_MAX_USERNAME_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (g_str_equal(username, ctx->context.username))
+               return dbus_message_new_method_return(msg);
+
+       strcpy(ctx->context.username, username);
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key,
+                                       "Username", username);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Username",
+                                       DBUS_TYPE_STRING, &username);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_password(struct pri_context *ctx,
+                                       DBusConnection *conn, DBusMessage *msg,
+                                       const char *password)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+
+       if (strlen(password) > OFONO_GPRS_MAX_PASSWORD_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (g_str_equal(password, ctx->context.password))
+               return dbus_message_new_method_return(msg);
+
+       strcpy(ctx->context.password, password);
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key,
+                                       "Password", password);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Password",
+                                       DBUS_TYPE_STRING, &password);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_type(struct pri_context *ctx, DBusConnection *conn,
+                                       DBusMessage *msg, const char *type)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+       enum ofono_gprs_context_type context_type;
+
+       if (gprs_context_string_to_type(type, &context_type) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       if (ctx->type == context_type)
+               return dbus_message_new_method_return(msg);
+
+       ctx->type = context_type;
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key, "Type", type);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Type", DBUS_TYPE_STRING, &type);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_proto(struct pri_context *ctx,
+                                       DBusConnection *conn,
+                                       DBusMessage *msg, const char *str)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+       enum ofono_gprs_proto proto;
+
+       if (gprs_proto_from_string(str, &proto) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       if (ctx->context.proto == proto)
+               return dbus_message_new_method_return(msg);
+
+       ctx->context.proto = proto;
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key, "Protocol", str);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Protocol", DBUS_TYPE_STRING, &str);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_name(struct pri_context *ctx, DBusConnection *conn,
+                                       DBusMessage *msg, const char *name)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+
+       if (strlen(name) > MAX_CONTEXT_NAME_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (ctx->name && g_str_equal(ctx->name, name))
+               return dbus_message_new_method_return(msg);
+
+       strcpy(ctx->name, name);
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key, "Name", ctx->name);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Name", DBUS_TYPE_STRING, &name);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_message_proxy(struct pri_context *ctx,
+                                       DBusConnection *conn,
+                                       DBusMessage *msg, const char *proxy)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+
+       if (strlen(proxy) > MAX_MESSAGE_PROXY_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (ctx->message_proxy && g_str_equal(ctx->message_proxy, proxy))
+               return dbus_message_new_method_return(msg);
+
+       strcpy(ctx->message_proxy, proxy);
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key, "MessageProxy",
+                                                       ctx->message_proxy);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                               OFONO_CONNECTION_CONTEXT_INTERFACE,
+                               "MessageProxy", DBUS_TYPE_STRING, &proxy);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_message_center(struct pri_context *ctx,
+                                       DBusConnection *conn,
+                                       DBusMessage *msg, const char *center)
+{
+       GKeyFile *settings = ctx->gprs->settings;
+
+       if (strlen(center) > MAX_MESSAGE_CENTER_LENGTH)
+               return __ofono_error_invalid_format(msg);
+
+       if (ctx->message_center && g_str_equal(ctx->message_center, center))
+               return dbus_message_new_method_return(msg);
+
+       strcpy(ctx->message_center, center);
+
+       if (settings) {
+               g_key_file_set_string(settings, ctx->key, "MessageCenter",
+                                                       ctx->message_center);
+               storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings);
+       }
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                               OFONO_CONNECTION_CONTEXT_INTERFACE,
+                               "MessageCenter", DBUS_TYPE_STRING, &center);
+
+       return NULL;
+}
+
+static DBusMessage *pri_set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct pri_context *ctx = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+       dbus_bool_t value;
+       const char *str;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (g_str_equal(property, "Active")) {
+               struct ofono_gprs_context *gc;
+
+               if (ctx->gprs->pending)
+                       return __ofono_error_busy(msg);
+
+               if (ctx->pending)
+                       return __ofono_error_busy(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (ctx->active == (ofono_bool_t) value)
+                       return dbus_message_new_method_return(msg);
+
+               if (value && !ctx->gprs->attached)
+                       return __ofono_error_not_attached(msg);
+
+               if (ctx->gprs->flags & GPRS_FLAG_ATTACHING)
+                       return __ofono_error_attach_in_progress(msg);
+
+               if (value && assign_context(ctx) == FALSE)
+                       return __ofono_error_not_implemented(msg);
+
+               gc = ctx->context_driver;
+
+               ctx->pending = dbus_message_ref(msg);
+
+               if (value)
+                       gc->driver->activate_primary(gc, &ctx->context,
+                                               pri_activate_callback, ctx);
+               else
+                       gc->driver->deactivate_primary(gc, ctx->context.cid,
+                                               pri_deactivate_callback, ctx);
+
+               return NULL;
+       }
+
+       /* All other properties are read-only when context is active */
+       if (ctx->active == TRUE)
+               return __ofono_error_in_use(msg);
+
+       if (!strcmp(property, "AccessPointName")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_apn(ctx, conn, msg, str);
+       } else if (!strcmp(property, "Type")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_type(ctx, conn, msg, str);
+       } else if (!strcmp(property, "Protocol")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_proto(ctx, conn, msg, str);
+       } else if (!strcmp(property, "Username")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_username(ctx, conn, msg, str);
+       } else if (!strcmp(property, "Password")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_password(ctx, conn, msg, str);
+       } else if (!strcmp(property, "Name")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_name(ctx, conn, msg, str);
+       }
+
+       if (ctx->type != OFONO_GPRS_CONTEXT_TYPE_MMS)
+               return __ofono_error_invalid_args(msg);
+
+       if (!strcmp(property, "MessageProxy")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_message_proxy(ctx, conn, msg, str);
+       } else if (!strcmp(property, "MessageCenter")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &str);
+
+               return pri_set_message_center(ctx, conn, msg, str);
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable context_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        pri_get_properties },
+       { "SetProperty",        "sv",   "",             pri_set_property,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable context_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static struct pri_context *pri_context_create(struct ofono_gprs *gprs,
+                                       const char *name,
+                                       enum ofono_gprs_context_type type)
+{
+       struct pri_context *context = g_try_new0(struct pri_context, 1);
+
+       if (context == NULL)
+               return NULL;
+
+       if (name == NULL) {
+               name = gprs_context_default_name(type);
+               if (name == NULL) {
+                       g_free(context);
+                       return NULL;
+               }
+       }
+
+       context->gprs = gprs;
+       strcpy(context->name, name);
+       context->type = type;
+
+       return context;
+}
+
+static void pri_context_destroy(gpointer userdata)
+{
+       struct pri_context *ctx = userdata;
+
+       g_free(ctx->proxy_host);
+       g_free(ctx->path);
+       g_free(ctx);
+}
+
+static gboolean context_dbus_register(struct pri_context *ctx)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       char path[256];
+       const char *basepath;
+
+       basepath = __ofono_atom_get_path(ctx->gprs->atom);
+
+       snprintf(path, sizeof(path), "%s/context%u", basepath, ctx->id);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       context_methods, context_signals,
+                                       NULL, ctx, pri_context_destroy)) {
+               ofono_error("Could not register PrimaryContext %s", path);
+               idmap_put(ctx->gprs->pid_map, ctx->id);
+               pri_context_destroy(ctx);
+
+               return FALSE;
+       }
+
+       ctx->path = g_strdup(path);
+       ctx->key = ctx->path + strlen(basepath) + 1;
+
+       return TRUE;
+}
+
+static gboolean context_dbus_unregister(struct pri_context *ctx)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       char path[256];
+
+       if (ctx->active == TRUE) {
+               const char *interface =
+                       ctx->context_driver->settings->interface;
+
+               if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS)
+                       pri_set_ipv4_addr(interface, NULL);
+
+               pri_ifupdown(interface, FALSE);
+       }
+
+       strcpy(path, ctx->path);
+       idmap_put(ctx->gprs->pid_map, ctx->id);
+
+       return g_dbus_unregister_interface(conn, path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE);
+}
+
+static void update_suspended_property(struct ofono_gprs *gprs,
+                               ofono_bool_t suspended)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(gprs->atom);
+       dbus_bool_t value = suspended;
+
+       if (gprs->suspend_timeout) {
+               g_source_remove(gprs->suspend_timeout);
+               gprs->suspend_timeout = 0;
+       }
+
+       if (gprs->suspended == suspended)
+               return;
+
+       DBG("%s GPRS service %s", __ofono_atom_get_path(gprs->atom),
+               suspended ? "suspended" : "resumed");
+
+       gprs->suspended = suspended;
+
+       if (gprs->attached)
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CONNECTION_MANAGER_INTERFACE,
+                                       "Suspended", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static gboolean suspend_timeout(gpointer data)
+{
+       struct ofono_gprs *gprs = data;
+
+       gprs->suspend_timeout = 0;
+       update_suspended_property(gprs, TRUE);
+       return FALSE;
+}
+
+void ofono_gprs_suspend_notify(struct ofono_gprs *gprs, int cause)
+{
+       switch (cause) {
+       case GPRS_SUSPENDED_DETACHED:
+       case GPRS_SUSPENDED_CALL:
+       case GPRS_SUSPENDED_NO_COVERAGE:
+               update_suspended_property(gprs, TRUE);
+               break;
+
+       case GPRS_SUSPENDED_SIGNALLING:
+       case GPRS_SUSPENDED_UNKNOWN_CAUSE:
+               if (gprs->suspend_timeout)
+                       g_source_remove(gprs->suspend_timeout);
+               gprs->suspend_timeout = g_timeout_add_seconds(SUSPEND_TIMEOUT,
+                                                       suspend_timeout,
+                                                       gprs);
+               break;
+       }
+}
+
+void ofono_gprs_resume_notify(struct ofono_gprs *gprs)
+{
+       update_suspended_property(gprs, FALSE);
+}
+
+static void gprs_attached_update(struct ofono_gprs *gprs)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       ofono_bool_t attached;
+       dbus_bool_t value;
+
+       attached = gprs->driver_attached &&
+               (gprs->status == NETWORK_REGISTRATION_STATUS_REGISTERED ||
+                       gprs->status == NETWORK_REGISTRATION_STATUS_ROAMING);
+
+       if (attached == gprs->attached)
+               return;
+
+       gprs->attached = attached;
+
+       if (gprs->attached == FALSE) {
+               GSList *l;
+               struct pri_context *ctx;
+
+               for (l = gprs->contexts; l; l = l->next) {
+                       ctx = l->data;
+
+                       if (ctx->active == FALSE)
+                               continue;
+
+                       pri_reset_context_settings(ctx);
+                       release_context(ctx);
+
+                       value = FALSE;
+                       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+               }
+
+               gprs->bearer = -1;
+       }
+
+       path = __ofono_atom_get_path(gprs->atom);
+       value = attached;
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_CONNECTION_MANAGER_INTERFACE,
+                               "Attached", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void registration_status_cb(const struct ofono_error *error,
+                                       int status, void *data)
+{
+       struct ofono_gprs *gprs = data;
+
+       DBG("%s error %d status %d", __ofono_atom_get_path(gprs->atom),
+               error->type, status);
+
+       gprs->flags &= ~GPRS_FLAG_ATTACHING;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               ofono_gprs_status_notify(gprs, status);
+       else
+               gprs_attached_update(gprs);
+
+       if (gprs->flags & GPRS_FLAG_RECHECK) {
+               gprs->flags &= ~GPRS_FLAG_RECHECK;
+               gprs_netreg_update(gprs);
+       }
+}
+
+static void gprs_attach_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_gprs *gprs = data;
+
+       DBG("%s error = %d", __ofono_atom_get_path(gprs->atom), error->type);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               gprs->driver_attached = !gprs->driver_attached;
+
+       if (gprs->driver->attached_status == NULL) {
+               struct ofono_error error;
+
+               error.type = OFONO_ERROR_TYPE_FAILURE;
+               error.error = 0;
+
+               registration_status_cb(&error, -1, gprs);
+               return;
+       }
+
+       gprs->driver->attached_status(gprs, registration_status_cb, gprs);
+}
+
+static void gprs_netreg_removed(struct ofono_gprs *gprs)
+{
+       gprs->netreg = NULL;
+
+       gprs->flags &= ~(GPRS_FLAG_RECHECK | GPRS_FLAG_ATTACHING);
+       gprs->status_watch = 0;
+       gprs->netreg_status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+       gprs->driver_attached = FALSE;
+
+       gprs_attached_update(gprs);
+}
+
+static void gprs_netreg_update(struct ofono_gprs *gprs)
+{
+       ofono_bool_t attach;
+
+       attach = gprs->netreg_status == NETWORK_REGISTRATION_STATUS_REGISTERED;
+
+       attach = attach || (gprs->roaming_allowed &&
+               gprs->netreg_status == NETWORK_REGISTRATION_STATUS_ROAMING);
+
+       attach = attach && gprs->powered;
+
+       if (gprs->driver_attached == attach)
+               return;
+
+       if (gprs->flags & GPRS_FLAG_ATTACHING) {
+               gprs->flags |= GPRS_FLAG_RECHECK;
+               return;
+       }
+
+       gprs->flags |= GPRS_FLAG_ATTACHING;
+
+       gprs->driver->set_attached(gprs, attach, gprs_attach_callback, gprs);
+       gprs->driver_attached = attach;
+}
+
+static void netreg_status_changed(int status, int lac, int ci, int tech,
+                                       const char *mcc, const char *mnc,
+                                       void *data)
+{
+       struct ofono_gprs *gprs = data;
+
+       DBG("%d", status);
+
+       if (gprs->netreg_status == status)
+               return;
+
+       gprs->netreg_status = status;
+
+       gprs_netreg_update(gprs);
+}
+
+static DBusMessage *gprs_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_gprs *gprs = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t value;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       value = gprs->attached;
+       ofono_dbus_dict_append(&dict, "Attached", DBUS_TYPE_BOOLEAN, &value);
+
+       if (gprs->bearer != -1) {
+               const char *bearer = packet_bearer_to_string(gprs->bearer);
+
+               ofono_dbus_dict_append(&dict, "Bearer",
+                                       DBUS_TYPE_STRING, &bearer);
+       }
+
+       value = gprs->roaming_allowed;
+       ofono_dbus_dict_append(&dict, "RoamingAllowed",
+                               DBUS_TYPE_BOOLEAN, &value);
+
+       value = gprs->powered;
+       ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);
+
+       if (gprs->attached) {
+               value = gprs->suspended;
+               ofono_dbus_dict_append(&dict, "Suspended",
+                               DBUS_TYPE_BOOLEAN, &value);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *gprs_set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_gprs *gprs = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+       dbus_bool_t value;
+       const char *path;
+
+       if (gprs->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (!strcmp(property, "RoamingAllowed")) {
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (gprs->roaming_allowed == (ofono_bool_t) value)
+                       return dbus_message_new_method_return(msg);
+
+               gprs->roaming_allowed = value;
+
+               if (gprs->settings) {
+                       g_key_file_set_integer(gprs->settings, SETTINGS_GROUP,
+                                               "RoamingAllowed",
+                                               gprs->roaming_allowed);
+                       storage_sync(gprs->imsi, SETTINGS_STORE,
+                                       gprs->settings);
+               }
+
+               gprs_netreg_update(gprs);
+       } else if (!strcmp(property, "Powered")) {
+               if (gprs->driver->set_attached == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (gprs->powered == (ofono_bool_t) value)
+                       return dbus_message_new_method_return(msg);
+
+               gprs->powered = value;
+
+               if (gprs->settings) {
+                       g_key_file_set_integer(gprs->settings, SETTINGS_GROUP,
+                                               "Powered", gprs->powered);
+                       storage_sync(gprs->imsi, SETTINGS_STORE,
+                                       gprs->settings);
+               }
+
+               gprs_netreg_update(gprs);
+       } else {
+               return __ofono_error_invalid_args(msg);
+       }
+
+       path = __ofono_atom_get_path(gprs->atom);
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CONNECTION_MANAGER_INTERFACE,
+                                       property, DBUS_TYPE_BOOLEAN, &value);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void write_context_settings(struct ofono_gprs *gprs,
+                                       struct pri_context *context)
+{
+       g_key_file_set_string(gprs->settings, context->key,
+                               "Name", context->name);
+       g_key_file_set_string(gprs->settings, context->key,
+                               "AccessPointName", context->context.apn);
+       g_key_file_set_string(gprs->settings, context->key,
+                               "Username", context->context.username);
+       g_key_file_set_string(gprs->settings, context->key,
+                               "Password", context->context.password);
+       g_key_file_set_string(gprs->settings, context->key, "Type",
+                               gprs_context_type_to_string(context->type));
+       g_key_file_set_string(gprs->settings, context->key, "Protocol",
+                               gprs_proto_to_string(context->context.proto));
+
+       if (context->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+               g_key_file_set_string(gprs->settings, context->key,
+                                       "MessageProxy",
+                                       context->message_proxy);
+               g_key_file_set_string(gprs->settings, context->key,
+                                       "MessageCenter",
+                                       context->message_center);
+       }
+}
+
+static struct pri_context *add_context(struct ofono_gprs *gprs,
+                                       const char *name,
+                                       enum ofono_gprs_context_type type)
+{
+       unsigned int id;
+       struct pri_context *context;
+
+       if (gprs->last_context_id)
+               id = idmap_alloc_next(gprs->pid_map, gprs->last_context_id);
+       else
+               id = idmap_alloc(gprs->pid_map);
+
+       if (id > idmap_get_max(gprs->pid_map))
+               return NULL;
+
+       context = pri_context_create(gprs, name, type);
+       if (context == NULL) {
+               idmap_put(gprs->pid_map, id);
+               ofono_error("Unable to allocate context struct");
+               return NULL;
+       }
+
+       context->id = id;
+
+       DBG("Registering new context");
+
+       if (!context_dbus_register(context)) {
+               ofono_error("Unable to register primary context");
+               return NULL;
+       }
+
+       gprs->last_context_id = id;
+
+       if (gprs->settings) {
+               write_context_settings(gprs, context);
+               storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings);
+       }
+
+       gprs->contexts = g_slist_append(gprs->contexts, context);
+
+       return context;
+}
+
+static DBusMessage *gprs_add_context(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_gprs *gprs = data;
+       struct pri_context *context;
+       const char *typestr;
+       const char *name;
+       const char *path;
+       enum ofono_gprs_context_type type;
+       DBusMessage *signal;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
+                                       DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       if (gprs_context_string_to_type(typestr, &type) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       name = gprs_context_default_name(type);
+       if (name == NULL)
+               name = typestr;
+
+       context = add_context(gprs, name, type);
+       if (context == NULL)
+               return __ofono_error_failed(msg);
+
+       path = context->path;
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID);
+
+       path = __ofono_atom_get_path(gprs->atom);
+       signal = dbus_message_new_signal(path,
+                                       OFONO_CONNECTION_MANAGER_INTERFACE,
+                                       "ContextAdded");
+
+       if (signal) {
+               DBusMessageIter iter;
+               DBusMessageIter dict;
+
+               dbus_message_iter_init_append(signal, &iter);
+
+               path = context->path;
+               dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                               &path);
+
+               dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+               append_context_properties(context, &dict);
+               dbus_message_iter_close_container(&iter, &dict);
+
+               g_dbus_send_message(conn, signal);
+       }
+
+       return NULL;
+}
+
+static void gprs_deactivate_for_remove(const struct ofono_error *error,
+                                               void *data)
+{
+       struct pri_context *ctx = data;
+       struct ofono_gprs *gprs = ctx->gprs;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       char *path;
+       const char *atompath;
+       dbus_bool_t value;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Removing context failed with error: %s",
+                               telephony_error_to_str(error));
+
+               __ofono_dbus_pending_reply(&gprs->pending,
+                                       __ofono_error_failed(gprs->pending));
+               return;
+       }
+
+       pri_reset_context_settings(ctx);
+       release_context(ctx);
+
+       value = FALSE;
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+
+       if (gprs->settings) {
+               g_key_file_remove_group(gprs->settings, ctx->key, NULL);
+               storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings);
+       }
+
+       /* Make a backup copy of path for signal emission below */
+       path = g_strdup(ctx->path);
+
+       context_dbus_unregister(ctx);
+       gprs->contexts = g_slist_remove(gprs->contexts, ctx);
+
+       __ofono_dbus_pending_reply(&gprs->pending,
+                               dbus_message_new_method_return(gprs->pending));
+
+       atompath = __ofono_atom_get_path(gprs->atom);
+       g_dbus_emit_signal(conn, atompath, OFONO_CONNECTION_MANAGER_INTERFACE,
+                               "ContextRemoved", DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       g_free(path);
+}
+
+static DBusMessage *gprs_remove_context(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_gprs *gprs = data;
+       struct pri_context *ctx;
+       const char *path;
+       const char *atompath;
+
+       if (gprs->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       if (path[0] == '\0')
+               return __ofono_error_invalid_format(msg);
+
+       ctx = gprs_context_by_path(gprs, path);
+       if (ctx == NULL)
+               return __ofono_error_not_found(msg);
+
+       if (ctx->active) {
+               struct ofono_gprs_context *gc = ctx->context_driver;
+
+               /* This context is already being messed with */
+               if (ctx->pending)
+                       return __ofono_error_busy(msg);
+
+               gprs->pending = dbus_message_ref(msg);
+               gc->driver->deactivate_primary(gc, ctx->context.cid,
+                                       gprs_deactivate_for_remove, ctx);
+               return NULL;
+       }
+
+       if (gprs->settings) {
+               g_key_file_remove_group(gprs->settings, ctx->key, NULL);
+               storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings);
+       }
+
+       DBG("Unregistering context: %s", ctx->path);
+       context_dbus_unregister(ctx);
+       gprs->contexts = g_slist_remove(gprs->contexts, ctx);
+
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       atompath = __ofono_atom_get_path(gprs->atom);
+       g_dbus_emit_signal(conn, atompath, OFONO_CONNECTION_MANAGER_INTERFACE,
+                               "ContextRemoved", DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+
+       return NULL;
+}
+
+static void gprs_deactivate_for_all(const struct ofono_error *error,
+                                       void *data)
+{
+       struct pri_context *ctx = data;
+       struct ofono_gprs *gprs = ctx->gprs;
+       DBusConnection *conn;
+       dbus_bool_t value;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               __ofono_dbus_pending_reply(&gprs->pending,
+                                       __ofono_error_failed(gprs->pending));
+               return;
+       }
+
+       pri_reset_context_settings(ctx);
+       release_context(ctx);
+
+       value = ctx->active;
+       conn = ofono_dbus_get_connection();
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+
+       gprs_deactivate_next(gprs);
+}
+
+static void gprs_deactivate_next(struct ofono_gprs *gprs)
+{
+       GSList *l;
+       struct pri_context *ctx;
+       struct ofono_gprs_context *gc;
+
+       for (l = gprs->contexts; l; l = l->next) {
+               ctx = l->data;
+
+               if (ctx->active == FALSE)
+                       continue;
+
+               gc = ctx->context_driver;
+               gc->driver->deactivate_primary(gc, ctx->context.cid,
+                                       gprs_deactivate_for_all, ctx);
+
+               return;
+       }
+
+       __ofono_dbus_pending_reply(&gprs->pending,
+                               dbus_message_new_method_return(gprs->pending));
+}
+
+static DBusMessage *gprs_deactivate_all(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_gprs *gprs = data;
+       GSList *l;
+       struct pri_context *ctx;
+
+       if (gprs->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       for (l = gprs->contexts; l; l = l->next) {
+               ctx = l->data;
+
+               if (ctx->pending)
+                       return __ofono_error_busy(msg);
+       }
+
+       gprs->pending = dbus_message_ref(msg);
+
+       gprs_deactivate_next(gprs);
+
+       return NULL;
+}
+
+static DBusMessage *gprs_get_contexts(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_gprs *gprs = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter array;
+       DBusMessageIter entry, dict;
+       const char *path;
+       GSList *l;
+       struct pri_context *ctx;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+
+       for (l = gprs->contexts; l; l = l->next) {
+               ctx = l->data;
+
+               path = ctx->path;
+
+               dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT,
+                                                       NULL, &entry);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                                               &path);
+               dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+               append_context_properties(ctx, &dict);
+               dbus_message_iter_close_container(&entry, &dict);
+               dbus_message_iter_close_container(&array, &entry);
+       }
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static GDBusMethodTable manager_methods[] = {
+       { "GetProperties",     "",     "a{sv}",     gprs_get_properties },
+       { "SetProperty",       "sv",   "",          gprs_set_property },
+       { "AddContext",        "s",    "o",         gprs_add_context,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "RemoveContext",     "o",    "",          gprs_remove_context,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "DeactivateAll",     "",     "",          gprs_deactivate_all,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "GetContexts",       "",     "a(oa{sv})", gprs_get_contexts },
+       { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { "ContextAdded",       "oa{sv}" },
+       { "ContextRemoved",     "o" },
+       { }
+};
+
+void ofono_gprs_detached_notify(struct ofono_gprs *gprs)
+{
+       DBG("%s", __ofono_atom_get_path(gprs->atom));
+
+       gprs->driver_attached = FALSE;
+       gprs_attached_update(gprs);
+
+       /*
+        * TODO: The network forced a detach, we should wait for some time
+        * and try to re-attach.  This might also be related to a suspend
+        * event while voicecall is active.
+        */
+}
+
+void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status)
+{
+       DBG("%s status %d", __ofono_atom_get_path(gprs->atom), status);
+
+       gprs->status = status;
+
+       if (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
+                       status != NETWORK_REGISTRATION_STATUS_ROAMING) {
+               gprs_attached_update(gprs);
+               return;
+       }
+
+       /*
+        * If we're already taking action, e.g. attaching or detaching, then
+        * ignore this notification for now, we will take appropriate action
+        * after the set_attach operation has completed
+        */
+       if (gprs->flags & GPRS_FLAG_ATTACHING)
+               return;
+
+       /* We registered without being powered */
+       if (gprs->powered == FALSE)
+               goto detach;
+
+       if (gprs->roaming_allowed == FALSE &&
+                       status == NETWORK_REGISTRATION_STATUS_ROAMING)
+               goto detach;
+
+       gprs->driver_attached = TRUE;
+       gprs_attached_update(gprs);
+
+       return;
+
+detach:
+       gprs->flags |= GPRS_FLAG_ATTACHING;
+       gprs->driver->set_attached(gprs, FALSE, gprs_attach_callback, gprs);
+}
+
+void ofono_gprs_set_cid_range(struct ofono_gprs *gprs,
+                               unsigned int min, unsigned int max)
+{
+       if (gprs == NULL)
+               return;
+
+       if (gprs->cid_map)
+               idmap_free(gprs->cid_map);
+
+       gprs->cid_map = idmap_new_from_range(min, max);
+}
+
+static void gprs_context_unregister(struct ofono_atom *atom)
+{
+       struct ofono_gprs_context *gc = __ofono_atom_get_data(atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       GSList *l;
+       struct pri_context *ctx;
+       dbus_bool_t value;
+
+       DBG("%p, %p", gc, gc->gprs);
+
+       if (gc->gprs == NULL)
+               goto done;
+
+       for (l = gc->gprs->contexts; l; l = l->next) {
+               ctx = l->data;
+
+               if (ctx->context_driver != gc)
+                       continue;
+
+               if (ctx->pending != NULL)
+                       __ofono_dbus_pending_reply(&ctx->pending,
+                                       __ofono_error_failed(ctx->pending));
+
+               if (ctx->active == FALSE)
+                       break;
+
+               pri_reset_context_settings(ctx);
+               release_context(ctx);
+
+               value = FALSE;
+               ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+       }
+
+       gc->gprs->context_drivers = g_slist_remove(gc->gprs->context_drivers,
+                                                       gc);
+       gc->gprs = NULL;
+
+done:
+       if (gc->settings) {
+               context_settings_free(gc->settings);
+               g_free(gc->settings);
+               gc->settings = NULL;
+       }
+}
+
+void ofono_gprs_add_context(struct ofono_gprs *gprs,
+                               struct ofono_gprs_context *gc)
+{
+       if (gc->driver == NULL)
+               return;
+
+       gc->gprs = gprs;
+       gc->settings = g_new0(struct context_settings, 1);
+
+       gprs->context_drivers = g_slist_append(gprs->context_drivers, gc);
+       __ofono_atom_register(gc->atom, gprs_context_unregister);
+}
+
+void ofono_gprs_bearer_notify(struct ofono_gprs *gprs, int bearer)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *value;
+
+       if (gprs->bearer == bearer)
+               return;
+
+       gprs->bearer = bearer;
+       path = __ofono_atom_get_path(gprs->atom);
+       value = packet_bearer_to_string(bearer);
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Bearer", DBUS_TYPE_STRING, &value);
+}
+
+void ofono_gprs_context_deactivated(struct ofono_gprs_context *gc,
+                                       unsigned int cid)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       GSList *l;
+       struct pri_context *ctx;
+       dbus_bool_t value;
+
+       if (gc->gprs == NULL)
+               return;
+
+       for (l = gc->gprs->contexts; l; l = l->next) {
+               ctx = l->data;
+
+               if (ctx->context.cid != cid)
+                       continue;
+
+               if (ctx->active == FALSE)
+                       break;
+
+               pri_reset_context_settings(ctx);
+               release_context(ctx);
+
+               value = FALSE;
+               ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+       }
+}
+
+int ofono_gprs_context_driver_register(
+                               const struct ofono_gprs_context_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_context_drivers = g_slist_prepend(g_context_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_gprs_context_driver_unregister(
+                               const struct ofono_gprs_context_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_context_drivers = g_slist_remove(g_context_drivers, (void *) d);
+}
+
+static void gprs_context_remove(struct ofono_atom *atom)
+{
+       struct ofono_gprs_context *gc = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (gc == NULL)
+               return;
+
+       if (gc->driver && gc->driver->remove)
+               gc->driver->remove(gc);
+
+       g_free(gc);
+}
+
+struct ofono_gprs_context *ofono_gprs_context_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver, void *data)
+{
+       struct ofono_gprs_context *gc;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       gc = g_try_new0(struct ofono_gprs_context, 1);
+       if (gc == NULL)
+               return NULL;
+
+       gc->type = OFONO_GPRS_CONTEXT_TYPE_ANY;
+
+       gc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_GPRS_CONTEXT,
+                                               gprs_context_remove, gc);
+
+       for (l = g_context_drivers; l; l = l->next) {
+               const struct ofono_gprs_context_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(gc, vendor, data) < 0)
+                       continue;
+
+               gc->driver = drv;
+               break;
+       }
+
+       return gc;
+}
+
+void ofono_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       if (gc == NULL)
+               return;
+
+       __ofono_atom_free(gc->atom);
+}
+
+void ofono_gprs_context_set_data(struct ofono_gprs_context *gc, void *data)
+{
+       gc->driver_data = data;
+}
+
+void *ofono_gprs_context_get_data(struct ofono_gprs_context *gc)
+{
+       return gc->driver_data;
+}
+
+struct ofono_modem *ofono_gprs_context_get_modem(struct ofono_gprs_context *gc)
+{
+       return __ofono_atom_get_modem(gc->atom);
+}
+
+void ofono_gprs_context_set_type(struct ofono_gprs_context *gc,
+                                       enum ofono_gprs_context_type type)
+{
+       DBG("type %d", type);
+
+       gc->type = type;
+}
+
+void ofono_gprs_context_set_interface(struct ofono_gprs_context *gc,
+                                       const char *interface)
+{
+       struct context_settings *settings = gc->settings;
+
+       g_free(settings->interface);
+       settings->interface = g_strdup(interface);
+}
+
+void ofono_gprs_context_set_ipv4_address(struct ofono_gprs_context *gc,
+                                               const char *address,
+                                               gboolean static_ip)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv4 == NULL)
+               return;
+
+       g_free(settings->ipv4->ip);
+       settings->ipv4->ip = g_strdup(address);
+       settings->ipv4->static_ip = static_ip;
+}
+
+void ofono_gprs_context_set_ipv4_netmask(struct ofono_gprs_context *gc,
+                                               const char *netmask)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv4 == NULL)
+               return;
+
+       g_free(settings->ipv4->netmask);
+       settings->ipv4->netmask = g_strdup(netmask);
+}
+
+void ofono_gprs_context_set_ipv4_gateway(struct ofono_gprs_context *gc,
+                                               const char *gateway)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv4 == NULL)
+               return;
+
+       g_free(settings->ipv4->gateway);
+       settings->ipv4->gateway = g_strdup(gateway);
+}
+
+void ofono_gprs_context_set_ipv4_dns_servers(struct ofono_gprs_context *gc,
+                                               const char **dns)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv4 == NULL)
+               return;
+
+       g_strfreev(settings->ipv4->dns);
+       settings->ipv4->dns = g_strdupv((char **) dns);
+}
+
+void ofono_gprs_context_set_ipv6_address(struct ofono_gprs_context *gc,
+                                               const char *address)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv6 == NULL)
+               return;
+
+       g_free(settings->ipv6->ip);
+       settings->ipv6->ip = g_strdup(address);
+}
+
+void ofono_gprs_context_set_ipv6_prefix_length(struct ofono_gprs_context *gc,
+                                               unsigned char length)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv6 == NULL)
+               return;
+
+       settings->ipv6->prefix_len = length;
+}
+
+void ofono_gprs_context_set_ipv6_gateway(struct ofono_gprs_context *gc,
+                                               const char *gateway)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv6 == NULL)
+               return;
+
+       g_free(settings->ipv6->gateway);
+       settings->ipv6->gateway = g_strdup(gateway);
+}
+
+void ofono_gprs_context_set_ipv6_dns_servers(struct ofono_gprs_context *gc,
+                                               const char **dns)
+{
+       struct context_settings *settings = gc->settings;
+
+       if (settings->ipv6 == NULL)
+               return;
+
+       g_strfreev(settings->ipv6->dns);
+       settings->ipv6->dns = g_strdupv((char **) dns);
+}
+
+int ofono_gprs_driver_register(const struct ofono_gprs_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *)d);
+
+       return 0;
+}
+
+void ofono_gprs_driver_unregister(const struct ofono_gprs_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *)d);
+}
+
+static void free_contexts(struct ofono_gprs *gprs)
+{
+       GSList *l;
+
+       if (gprs->settings) {
+               storage_close(gprs->imsi, SETTINGS_STORE,
+                               gprs->settings, TRUE);
+
+               g_free(gprs->imsi);
+               gprs->imsi = NULL;
+               gprs->settings = NULL;
+       }
+
+       for (l = gprs->contexts; l; l = l->next) {
+               struct pri_context *context = l->data;
+
+               context_dbus_unregister(context);
+       }
+
+       g_slist_free(gprs->contexts);
+}
+
+static void gprs_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_gprs *gprs = __ofono_atom_get_data(atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       DBG("%p", gprs);
+
+       free_contexts(gprs);
+
+       if (gprs->cid_map) {
+               idmap_free(gprs->cid_map);
+               gprs->cid_map = NULL;
+       }
+
+       if (gprs->netreg_watch) {
+               if (gprs->status_watch) {
+                       __ofono_netreg_remove_status_watch(gprs->netreg,
+                                                       gprs->status_watch);
+                       gprs->status_watch = 0;
+               }
+
+               __ofono_modem_remove_atom_watch(modem, gprs->netreg_watch);
+               gprs->netreg_watch = 0;
+               gprs->netreg = NULL;
+       }
+
+       if (gprs->spn_watch) {
+               struct ofono_sim *sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM,
+                                                               modem);
+
+               ofono_sim_remove_spn_watch(sim, &gprs->spn_watch);
+       }
+
+       ofono_modem_remove_interface(modem,
+                                       OFONO_CONNECTION_MANAGER_INTERFACE);
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_CONNECTION_MANAGER_INTERFACE);
+}
+
+static void gprs_remove(struct ofono_atom *atom)
+{
+       struct ofono_gprs *gprs = __ofono_atom_get_data(atom);
+       GSList *l;
+
+       DBG("atom: %p", atom);
+
+       if (gprs == NULL)
+               return;
+
+       if (gprs->suspend_timeout)
+               g_source_remove(gprs->suspend_timeout);
+
+       if (gprs->pid_map) {
+               idmap_free(gprs->pid_map);
+               gprs->pid_map = NULL;
+       }
+
+       for (l = gprs->context_drivers; l; l = l->next) {
+               struct ofono_gprs_context *gc = l->data;
+
+               gc->gprs = NULL;
+       }
+
+       g_slist_free(gprs->context_drivers);
+
+       if (gprs->driver && gprs->driver->remove)
+               gprs->driver->remove(gprs);
+
+       g_free(gprs);
+}
+
+struct ofono_gprs *ofono_gprs_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver, void *data)
+{
+       struct ofono_gprs *gprs;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       gprs = g_try_new0(struct ofono_gprs, 1);
+       if (gprs == NULL)
+               return NULL;
+
+       gprs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_GPRS,
+                                               gprs_remove, gprs);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_gprs_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(gprs, vendor, data) < 0)
+                       continue;
+
+               gprs->driver = drv;
+               break;
+       }
+
+       gprs->status = NETWORK_REGISTRATION_STATUS_UNKNOWN;
+       gprs->netreg_status = NETWORK_REGISTRATION_STATUS_UNKNOWN;
+       gprs->pid_map = idmap_new(MAX_CONTEXTS);
+
+       return gprs;
+}
+
+static void netreg_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct ofono_gprs *gprs = data;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               gprs_netreg_removed(gprs);
+               return;
+       }
+
+       gprs->netreg = __ofono_atom_get_data(atom);
+       gprs->netreg_status = ofono_netreg_get_status(gprs->netreg);
+       gprs->status_watch = __ofono_netreg_add_status_watch(gprs->netreg,
+                                       netreg_status_changed, gprs, NULL);
+
+       gprs_netreg_update(gprs);
+}
+
+static gboolean load_context(struct ofono_gprs *gprs, const char *group)
+{
+       char *name = NULL;
+       char *typestr = NULL;
+       char *protostr = NULL;
+       char *username = NULL;
+       char *password = NULL;
+       char *apn = NULL;
+       char *msgproxy = NULL;
+       char *msgcenter = NULL;
+       gboolean ret = FALSE;
+       gboolean legacy = FALSE;
+       struct pri_context *context;
+       enum ofono_gprs_context_type type;
+       enum ofono_gprs_proto proto;
+       unsigned int id;
+
+       if (sscanf(group, "context%d", &id) != 1) {
+               if (sscanf(group, "primarycontext%d", &id) != 1)
+                       goto error;
+
+               legacy = TRUE;
+       }
+
+       if (id < 1 || id > MAX_CONTEXTS)
+               goto error;
+
+       name = g_key_file_get_string(gprs->settings, group, "Name", NULL);
+       if (name == NULL)
+               goto error;
+
+       typestr = g_key_file_get_string(gprs->settings, group, "Type", NULL);
+       if (typestr == NULL)
+               goto error;
+
+       if (gprs_context_string_to_type(typestr, &type) == FALSE)
+               goto error;
+
+       protostr = g_key_file_get_string(gprs->settings, group,
+                                                       "Protocol", NULL);
+       if (protostr == NULL)
+               protostr = g_strdup("ip");
+
+       if (gprs_proto_from_string(protostr, &proto) == FALSE)
+               goto error;
+
+       username = g_key_file_get_string(gprs->settings, group,
+                                               "Username", NULL);
+       if (username == NULL)
+               goto error;
+
+       if (strlen(username) > OFONO_GPRS_MAX_USERNAME_LENGTH)
+               goto error;
+
+       password = g_key_file_get_string(gprs->settings, group,
+                                               "Password", NULL);
+       if (password == NULL)
+               goto error;
+
+       if (strlen(password) > OFONO_GPRS_MAX_PASSWORD_LENGTH)
+               goto error;
+
+       apn = g_key_file_get_string(gprs->settings, group,
+                                       "AccessPointName", NULL);
+       if (apn == NULL)
+               goto error;
+
+       if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH)
+               goto error;
+
+       if (type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+               msgproxy = g_key_file_get_string(gprs->settings, group,
+                                               "MessageProxy", NULL);
+
+               msgcenter = g_key_file_get_string(gprs->settings, group,
+                                               "MessageCenter", NULL);
+       }
+
+       /*
+        * Accept empty (just created) APNs, but don't allow other
+        * invalid ones
+        */
+       if (apn[0] != '\0' && is_valid_apn(apn) == FALSE)
+               goto error;
+
+       context = pri_context_create(gprs, name, type);
+       if (context == NULL)
+               goto error;
+
+       idmap_take(gprs->pid_map, id);
+       context->id = id;
+       strcpy(context->context.username, username);
+       strcpy(context->context.password, password);
+       strcpy(context->context.apn, apn);
+       context->context.proto = proto;
+
+       if (msgproxy != NULL)
+               strcpy(context->message_proxy, msgproxy);
+
+       if (msgcenter != NULL)
+               strcpy(context->message_center, msgcenter);
+
+       if (context_dbus_register(context) == FALSE)
+               goto error;
+
+       gprs->last_context_id = id;
+
+       gprs->contexts = g_slist_append(gprs->contexts, context);
+       ret = TRUE;
+
+       if (legacy) {
+               write_context_settings(gprs, context);
+               g_key_file_remove_group(gprs->settings, group, NULL);
+       }
+
+error:
+       g_free(name);
+       g_free(typestr);
+       g_free(protostr);
+       g_free(username);
+       g_free(password);
+       g_free(apn);
+       g_free(msgproxy);
+       g_free(msgcenter);
+
+       return ret;
+}
+
+static void gprs_load_settings(struct ofono_gprs *gprs, const char *imsi)
+{
+       GError *error;
+       gboolean legacy = FALSE;
+       char **groups;
+       int i;
+
+       gprs->settings = storage_open(imsi, SETTINGS_STORE);
+
+       if (gprs->settings == NULL)
+               return;
+
+       gprs->imsi = g_strdup(imsi);
+
+       error = NULL;
+       gprs->powered = g_key_file_get_boolean(gprs->settings, SETTINGS_GROUP,
+                                               "Powered", &error);
+
+       /*
+        * If any error occurs, simply switch to defaults.
+        * Default to Powered = True
+        * and RoamingAllowed = False
+        */
+       if (error) {
+               g_error_free(error);
+               gprs->powered = TRUE;
+               g_key_file_set_boolean(gprs->settings, SETTINGS_GROUP,
+                                       "Powered", gprs->powered);
+       }
+
+       error = NULL;
+       gprs->roaming_allowed = g_key_file_get_boolean(gprs->settings,
+                                                       SETTINGS_GROUP,
+                                                       "RoamingAllowed",
+                                                       &error);
+
+       if (error) {
+               g_error_free(error);
+               gprs->roaming_allowed = FALSE;
+               g_key_file_set_boolean(gprs->settings, SETTINGS_GROUP,
+                                       "RoamingAllowed",
+                                       gprs->roaming_allowed);
+       }
+
+       groups = g_key_file_get_groups(gprs->settings, NULL);
+
+       for (i = 0; groups[i]; i++) {
+               if (g_str_equal(groups[i], SETTINGS_GROUP))
+                       continue;
+
+               if (!g_str_has_prefix(groups[i], "context")) {
+                       if (!g_str_has_prefix(groups[i], "primarycontext"))
+                               goto remove;
+
+                       legacy = TRUE;
+               }
+
+               if (load_context(gprs, groups[i]) == TRUE)
+                       continue;
+
+remove:
+               g_key_file_remove_group(gprs->settings, groups[i], NULL);
+       }
+
+       g_strfreev(groups);
+
+       if (legacy)
+               storage_sync(imsi, SETTINGS_STORE, gprs->settings);
+}
+
+static void provision_context(const struct ofono_gprs_provision_data *ap,
+                               struct ofono_gprs *gprs)
+{
+       unsigned int id;
+       struct pri_context *context = NULL;
+
+       /* Sanity check */
+       if (ap == NULL)
+               return;
+
+       if (ap->name && strlen(ap->name) > MAX_CONTEXT_NAME_LENGTH)
+               return;
+
+       if (ap->apn == NULL || strlen(ap->apn) > OFONO_GPRS_MAX_APN_LENGTH)
+               return;
+
+       if (is_valid_apn(ap->apn) == FALSE)
+               return;
+
+       if (ap->username &&
+                       strlen(ap->username) > OFONO_GPRS_MAX_USERNAME_LENGTH)
+               return;
+
+       if (ap->password &&
+                       strlen(ap->password) > OFONO_GPRS_MAX_PASSWORD_LENGTH)
+               return;
+
+       if (ap->message_proxy &&
+                       strlen(ap->message_proxy) > MAX_MESSAGE_PROXY_LENGTH)
+               return;
+
+       if (ap->message_center &&
+                       strlen(ap->message_center) > MAX_MESSAGE_CENTER_LENGTH)
+               return;
+
+       if (gprs->last_context_id)
+               id = idmap_alloc_next(gprs->pid_map, gprs->last_context_id);
+       else
+               id = idmap_alloc(gprs->pid_map);
+
+       if (id > idmap_get_max(gprs->pid_map))
+               return;
+
+       context = pri_context_create(gprs, ap->name, ap->type);
+       if (context == NULL) {
+               idmap_put(gprs->pid_map, id);
+               return;
+       }
+
+       context->id = id;
+
+       if (ap->username != NULL)
+               strcpy(context->context.username, ap->username);
+
+       if (ap->password != NULL)
+               strcpy(context->context.password, ap->password);
+
+       strcpy(context->context.apn, ap->apn);
+       context->context.proto = ap->proto;
+
+       if (ap->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+               if (ap->message_proxy != NULL)
+                       strcpy(context->message_proxy, ap->message_proxy);
+
+               if (ap->message_center != NULL)
+                       strcpy(context->message_center, ap->message_center);
+       }
+
+       if (context_dbus_register(context) == FALSE)
+               return;
+
+       gprs->last_context_id = id;
+
+       if (gprs->settings) {
+               write_context_settings(gprs, context);
+               storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings);
+       }
+
+       gprs->contexts = g_slist_append(gprs->contexts, context);
+}
+
+static void provision_contexts(struct ofono_gprs *gprs, const char *mcc,
+                               const char *mnc, const char *spn)
+{
+       struct ofono_gprs_provision_data *settings;
+       int count;
+       int i;
+
+       if (__ofono_gprs_provision_get_settings(mcc, mnc, spn,
+                                               &settings, &count) == FALSE) {
+               ofono_warn("Provisioning failed");
+               return;
+       }
+
+       for (i = 0; i < count; i++)
+               provision_context(&settings[i], gprs);
+
+       __ofono_gprs_provision_free_settings(settings, count);
+}
+
+static void ofono_gprs_finish_register(struct ofono_gprs *gprs)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(gprs->atom);
+       const char *path = __ofono_atom_get_path(gprs->atom);
+
+       if (gprs->contexts == NULL) /* Automatic provisioning failed */
+               add_context(gprs, NULL, OFONO_GPRS_CONTEXT_TYPE_INTERNET);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_CONNECTION_MANAGER_INTERFACE,
+                                       manager_methods, manager_signals, NULL,
+                                       gprs, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_CONNECTION_MANAGER_INTERFACE);
+
+               free_contexts(gprs);
+               return;
+       }
+
+       ofono_modem_add_interface(modem,
+                               OFONO_CONNECTION_MANAGER_INTERFACE);
+
+       gprs->netreg_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_NETREG,
+                                       netreg_watch, gprs, NULL);
+
+       __ofono_atom_register(gprs->atom, gprs_unregister);
+}
+
+static void spn_read_cb(const char *spn, const char *dc, void *data)
+{
+       struct ofono_gprs *gprs = data;
+       struct ofono_modem *modem = __ofono_atom_get_modem(gprs->atom);
+       struct ofono_sim *sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+
+       provision_contexts(gprs, ofono_sim_get_mcc(sim),
+                                       ofono_sim_get_mnc(sim), spn);
+
+       ofono_sim_remove_spn_watch(sim, &gprs->spn_watch);
+
+       ofono_gprs_finish_register(gprs);
+}
+
+void ofono_gprs_register(struct ofono_gprs *gprs)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(gprs->atom);
+       struct ofono_sim *sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+
+       if (sim == NULL)
+               goto finish;
+
+       gprs_load_settings(gprs, ofono_sim_get_imsi(sim));
+
+       if (gprs->contexts)
+               goto finish;
+
+       ofono_sim_add_spn_watch(sim, &gprs->spn_watch, spn_read_cb, gprs, NULL);
+       return;
+
+finish:
+       ofono_gprs_finish_register(gprs);
+}
+
+void ofono_gprs_remove(struct ofono_gprs *gprs)
+{
+       __ofono_atom_free(gprs->atom);
+}
+
+void ofono_gprs_set_data(struct ofono_gprs *gprs, void *data)
+{
+       gprs->driver_data = data;
+}
+
+void *ofono_gprs_get_data(struct ofono_gprs *gprs)
+{
+       return gprs->driver_data;
+}
diff --git a/src/handsfree.c b/src/handsfree.c
new file mode 100644 (file)
index 0000000..66daaf1
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/handsfree.h>
+
+#include <gdbus.h>
+#include "ofono.h"
+#include "common.h"
+
+static GSList *g_drivers = NULL;
+
+struct ofono_handsfree {
+       ofono_bool_t inband_ringing;
+       ofono_bool_t voice_recognition;
+       ofono_bool_t voice_recognition_pending;
+       unsigned int ag_features;
+
+       const struct ofono_handsfree_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       DBusMessage *pending;
+};
+
+static const char **ag_features_list(unsigned int features)
+{
+       static const char *list[33];
+       unsigned int i = 0;
+
+       if (features & HFP_AG_FEATURE_VOICE_RECOG)
+               list[i++] = "voice-recognition";
+
+       if (features & HFP_AG_FEATURE_ATTACH_VOICE_TAG)
+               list[i++] = "attach-voice-tag";
+
+       list[i] = NULL;
+
+       return list;
+}
+
+void ofono_handsfree_set_inband_ringing(struct ofono_handsfree *hf,
+                                               ofono_bool_t enabled)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(hf->atom);
+       dbus_bool_t dbus_enabled = enabled;
+
+       if (hf->inband_ringing == enabled)
+               return;
+
+       hf->inband_ringing = enabled;
+
+       if (__ofono_atom_get_registered(hf->atom) == FALSE)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_HANDSFREE_INTERFACE,
+                                       "InbandRinging", DBUS_TYPE_BOOLEAN,
+                                       &dbus_enabled);
+}
+
+void ofono_handsfree_voice_recognition_notify(struct ofono_handsfree *hf,
+                                               ofono_bool_t enabled)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(hf->atom);
+       dbus_bool_t dbus_enabled = enabled;
+
+       if (hf->voice_recognition == enabled)
+               return;
+
+       hf->voice_recognition = enabled;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_HANDSFREE_INTERFACE,
+                                       "VoiceRecognition", DBUS_TYPE_BOOLEAN,
+                                       &dbus_enabled);
+}
+
+void ofono_handsfree_set_ag_features(struct ofono_handsfree *hf,
+                                       unsigned int ag_features)
+{
+       if (hf == NULL)
+               return;
+
+       hf->ag_features = ag_features;
+}
+
+static DBusMessage *handsfree_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_handsfree *hf = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t inband_ringing;
+       dbus_bool_t voice_recognition;
+       const char **features;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       inband_ringing = hf->inband_ringing;
+       ofono_dbus_dict_append(&dict, "InbandRinging", DBUS_TYPE_BOOLEAN,
+                               &inband_ringing);
+
+       voice_recognition = hf->voice_recognition;
+       ofono_dbus_dict_append(&dict, "VoiceRecognition", DBUS_TYPE_BOOLEAN,
+                               &voice_recognition);
+
+       features = ag_features_list(hf->ag_features);
+       ofono_dbus_dict_append_array(&dict, "Features", DBUS_TYPE_STRING,
+                                       &features);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void voicerec_set_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_handsfree *hf = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(hf->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               __ofono_dbus_pending_reply(&hf->pending,
+                                       __ofono_error_failed(hf->pending));
+               return;
+       }
+
+       hf->voice_recognition = hf->voice_recognition_pending;
+
+       __ofono_dbus_pending_reply(&hf->pending,
+                               dbus_message_new_method_return(hf->pending));
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_HANDSFREE_INTERFACE,
+                                       "VoiceRecognition",
+                                       DBUS_TYPE_BOOLEAN,
+                                       &hf->voice_recognition);
+}
+
+static DBusMessage *handsfree_set_property(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_handsfree *hf = data;
+       DBusMessageIter iter, var;
+       const char *name;
+
+       if (hf->pending)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (g_str_equal(name, "VoiceRecognition") == TRUE) {
+               ofono_bool_t enabled;
+
+               if (!hf->driver->voice_recognition)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &enabled);
+
+               if (hf->voice_recognition == enabled)
+                       return dbus_message_new_method_return(msg);
+
+               hf->voice_recognition_pending = enabled;
+               hf->pending = dbus_message_ref(msg);
+               hf->driver->voice_recognition(hf, enabled, voicerec_set_cb, hf);
+
+               return NULL;
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static void request_phone_number_cb(const struct ofono_error *error,
+                                       const struct ofono_phone_number *number,
+                                       void *data)
+{
+       struct ofono_handsfree *hf = data;
+       DBusMessage *reply;
+       const char *phone_number;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Phone number request callback returned error: %s",
+                       telephony_error_to_str(error));
+
+               reply = __ofono_error_failed(hf->pending);
+               __ofono_dbus_pending_reply(&hf->pending, reply);
+               return;
+       }
+
+       phone_number = phone_number_to_string(number);
+       reply = dbus_message_new_method_return(hf->pending);
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &phone_number,
+                                       DBUS_TYPE_INVALID);
+       __ofono_dbus_pending_reply(&hf->pending, reply);
+}
+
+static DBusMessage *handsfree_request_phone_number(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_handsfree *hf = data;
+
+       if (hf->pending)
+               return __ofono_error_busy(msg);
+
+       if (!hf->driver->request_phone_number)
+               return __ofono_error_not_supported(msg);
+
+       hf->pending = dbus_message_ref(msg);
+       hf->driver->request_phone_number(hf, request_phone_number_cb, hf);
+
+       return NULL;
+}
+
+static GDBusMethodTable handsfree_methods[] = {
+       { "GetProperties",    "",    "a{sv}", handsfree_get_properties },
+       { "SetProperty",      "sv",  "", handsfree_set_property,
+               G_DBUS_METHOD_FLAG_ASYNC },
+       { "RequestPhoneNumber", "", "s", handsfree_request_phone_number,
+               G_DBUS_METHOD_FLAG_ASYNC },
+       { NULL, NULL, NULL, NULL }
+};
+
+static GDBusSignalTable handsfree_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static void handsfree_remove(struct ofono_atom *atom)
+{
+       struct ofono_handsfree *hf = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (hf == NULL)
+               return;
+
+       if (hf->driver != NULL && hf->driver->remove != NULL)
+               hf->driver->remove(hf);
+
+       g_free(hf);
+}
+
+struct ofono_handsfree *ofono_handsfree_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_handsfree *hf;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       hf = g_try_new0(struct ofono_handsfree, 1);
+       if (hf == NULL)
+               return NULL;
+
+       hf->atom = __ofono_modem_add_atom(modem,
+                                       OFONO_ATOM_TYPE_HANDSFREE,
+                                       handsfree_remove, hf);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_handsfree_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(hf, vendor, data) < 0)
+                       continue;
+
+               hf->driver = drv;
+               break;
+       }
+
+       return hf;
+}
+
+static void handsfree_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+       struct ofono_handsfree *hf = __ofono_atom_get_data(atom);
+
+       if (hf->pending) {
+               DBusMessage *reply = __ofono_error_failed(hf->pending);
+               __ofono_dbus_pending_reply(&hf->pending, reply);
+       }
+
+       ofono_modem_remove_interface(modem, OFONO_HANDSFREE_INTERFACE);
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_HANDSFREE_INTERFACE);
+}
+
+void ofono_handsfree_register(struct ofono_handsfree *hf)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(hf->atom);
+       const char *path = __ofono_atom_get_path(hf->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_HANDSFREE_INTERFACE,
+                                       handsfree_methods, handsfree_signals,
+                                       NULL, hf, NULL)) {
+               ofono_error("Could not create %s interface",
+                                       OFONO_HANDSFREE_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_HANDSFREE_INTERFACE);
+
+       __ofono_atom_register(hf->atom, handsfree_unregister);
+}
+
+int ofono_handsfree_driver_register(const struct ofono_handsfree_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_handsfree_driver_unregister(
+                               const struct ofono_handsfree_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+void ofono_handsfree_remove(struct ofono_handsfree *hf)
+{
+       __ofono_atom_free(hf->atom);
+}
+
+void ofono_handsfree_set_data(struct ofono_handsfree *hf, void *data)
+{
+       hf->driver_data = data;
+}
+
+void *ofono_handsfree_get_data(struct ofono_handsfree *hf)
+{
+       return hf->driver_data;
+}
diff --git a/src/history.c b/src/history.c
new file mode 100644 (file)
index 0000000..ec11748
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "ofono.h"
+
+static GSList *history_drivers = NULL;
+
+struct history_call_foreach_data {
+       const struct ofono_call *call;
+       union {
+               struct {
+                       time_t start;
+                       time_t end;
+               };
+
+               time_t when;
+       };
+};
+
+struct history_sms_foreach_data {
+       const struct ofono_uuid *uuid;
+       const char *address;
+       const char *text;
+       union {
+               struct {
+                       const struct tm *remote;
+                       const struct tm *local;
+               };
+               struct {
+                       time_t when;
+                       enum ofono_history_sms_status status;
+               };
+       };
+};
+
+static struct ofono_history_context *history_context_create(
+                                       struct ofono_modem *modem,
+                                       struct ofono_history_driver *driver)
+{
+       struct ofono_history_context *context;
+
+       if (driver->probe == NULL)
+               return NULL;
+
+       context = g_try_new0(struct ofono_history_context, 1);
+
+       if (context == NULL)
+               return NULL;
+
+       context->driver = driver;
+       context->modem = modem;
+
+       if (driver->probe(context) < 0) {
+               g_free(context);
+               return NULL;
+       }
+
+       return context;
+}
+
+static void context_remove(struct ofono_atom *atom)
+{
+       struct ofono_history_context *context = __ofono_atom_get_data(atom);
+
+       if (context->driver->remove)
+               context->driver->remove(context);
+
+       g_free(context);
+}
+
+void __ofono_history_probe_drivers(struct ofono_modem *modem)
+{
+       struct ofono_history_driver *driver;
+       struct ofono_history_context *context;
+       GSList *l;
+
+       for (l = history_drivers; l; l = l->next) {
+               driver = l->data;
+
+               context = history_context_create(modem, driver);
+               if (context == NULL)
+                       continue;
+
+               __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_HISTORY,
+                                               context_remove, context);
+       }
+}
+
+static void history_call_ended(struct ofono_atom *atom, void *data)
+{
+       struct ofono_history_context *context = __ofono_atom_get_data(atom);
+       struct history_call_foreach_data *hfd = data;
+
+       if (context->driver->call_ended == NULL)
+               return;
+
+       context->driver->call_ended(context, hfd->call, hfd->start, hfd->end);
+}
+
+void __ofono_history_call_ended(struct ofono_modem *modem,
+                               const struct ofono_call *call,
+                               time_t start, time_t end)
+{
+       struct history_call_foreach_data hfd;
+
+       hfd.call = call;
+       hfd.start = start;
+       hfd.end = end;
+
+       __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY,
+                                       history_call_ended, &hfd);
+}
+
+static void history_call_missed(struct ofono_atom *atom, void *data)
+{
+       struct ofono_history_context *context = __ofono_atom_get_data(atom);
+       struct history_call_foreach_data *hfd = data;
+
+       if (context->driver->call_missed == NULL)
+               return;
+
+       context->driver->call_missed(context, hfd->call, hfd->when);
+}
+
+void __ofono_history_call_missed(struct ofono_modem *modem,
+                               const struct ofono_call *call, time_t when)
+{
+       struct history_call_foreach_data hfd;
+
+       hfd.call = call;
+       hfd.when = when;
+
+       __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY,
+                                       history_call_missed, &hfd);
+}
+
+static void history_sms_received(struct ofono_atom *atom, void *data)
+{
+       struct ofono_history_context *context = __ofono_atom_get_data(atom);
+       struct history_sms_foreach_data *hfd = data;
+
+       if (context->driver->sms_received == NULL)
+               return;
+
+       context->driver->sms_received(context, hfd->uuid, hfd->address,
+                                       hfd->remote, hfd->local, hfd->text);
+}
+
+void __ofono_history_sms_received(struct ofono_modem *modem,
+                                       const struct ofono_uuid *uuid,
+                                       const char *from,
+                                       const struct tm *remote,
+                                       const struct tm *local,
+                                       const char *text)
+{
+       struct history_sms_foreach_data hfd;
+
+       hfd.uuid = uuid;
+       hfd.address = from;
+       hfd.remote = remote;
+       hfd.local = local;
+       hfd.text = text;
+
+       __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY,
+                                       history_sms_received, &hfd);
+}
+
+static void history_sms_send_pending(struct ofono_atom *atom, void *data)
+{
+       struct ofono_history_context *context = __ofono_atom_get_data(atom);
+       struct history_sms_foreach_data *hfd = data;
+
+       if (context->driver->sms_send_pending == NULL)
+               return;
+
+       context->driver->sms_send_pending(context, hfd->uuid, hfd->address,
+                                               hfd->when, hfd->text);
+}
+
+void __ofono_history_sms_send_pending(struct ofono_modem *modem,
+                                       const struct ofono_uuid *uuid,
+                                       const char *to,
+                                       time_t when, const char *text)
+{
+       struct history_sms_foreach_data hfd;
+
+       hfd.uuid = uuid;
+       hfd.address = to;
+       hfd.text = text;
+       hfd.when = when;
+       hfd.status = OFONO_HISTORY_SMS_STATUS_PENDING;
+
+       __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY,
+                                       history_sms_send_pending, &hfd);
+}
+
+static void history_sms_send_status(struct ofono_atom *atom, void *data)
+{
+       struct ofono_history_context *context = __ofono_atom_get_data(atom);
+       struct history_sms_foreach_data *hfd = data;
+
+       if (context->driver->sms_send_status == NULL)
+               return;
+
+       context->driver->sms_send_status(context, hfd->uuid,
+                                               hfd->when, hfd->status);
+}
+
+void __ofono_history_sms_send_status(struct ofono_modem *modem,
+                                       const struct ofono_uuid *uuid,
+                                       time_t when,
+                                       enum ofono_history_sms_status status)
+{
+       struct history_sms_foreach_data hfd;
+
+       hfd.uuid = uuid;
+       hfd.address = NULL;
+       hfd.text = NULL;
+       hfd.when = when;
+       hfd.status = status;
+
+       __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY,
+                                       history_sms_send_status, &hfd);
+}
+
+int ofono_history_driver_register(const struct ofono_history_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       history_drivers = g_slist_prepend(history_drivers, (void *) driver);
+
+       return 0;
+}
+
+void ofono_history_driver_unregister(const struct ofono_history_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       history_drivers = g_slist_remove(history_drivers, driver);
+}
diff --git a/src/idmap.c b/src/idmap.c
new file mode 100644 (file)
index 0000000..63f5c7c
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2004  Red Hat, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <glib.h>
+
+#include "idmap.h"
+
+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
+
+struct idmap {
+       unsigned long *bits;
+       unsigned int size;
+       unsigned int min;
+       unsigned int max;
+};
+
+static inline int ffz(unsigned long word)
+{
+       return __builtin_ctzl(~word);
+}
+
+/*
+ * Stolen from linux kernel lib/find_next_bit.c
+ */
+static unsigned int find_next_zero_bit(const unsigned long *addr,
+                                       unsigned int size,
+                                       unsigned int offset)
+{
+       const unsigned long *p = addr + offset / BITS_PER_LONG;
+       unsigned int result = offset & ~(BITS_PER_LONG-1);
+       unsigned long tmp;
+
+       if (offset >= size)
+               return size;
+
+       size -= result;
+       offset %= BITS_PER_LONG;
+
+       if (offset) {
+               tmp = *(p++);
+               tmp |= ~0UL >> (BITS_PER_LONG - offset);
+
+               if (size < BITS_PER_LONG)
+                       goto found_first;
+
+               if (~tmp)
+                       goto found_middle;
+
+               size -= BITS_PER_LONG;
+               result += BITS_PER_LONG;
+       }
+
+       while (size & ~(BITS_PER_LONG-1)) {
+               if (~(tmp = *(p++)))
+                       goto found_middle;
+
+               size -= BITS_PER_LONG;
+               result += BITS_PER_LONG;
+       }
+
+       if (!size)
+               return result;
+
+       tmp = *p;
+
+found_first:
+       tmp |= ~0UL << size;
+
+       if (tmp == ~0UL)        /* Are any bits zero? */
+               return result + size;   /* Nope. */
+
+found_middle:
+       return result + ffz(tmp);
+}
+
+struct idmap *idmap_new_from_range(unsigned int min, unsigned int max)
+{
+       struct idmap *ret = g_new0(struct idmap, 1);
+       unsigned int size = max - min + 1;
+
+       ret->bits = g_new0(unsigned long,
+                               (size + BITS_PER_LONG - 1) / BITS_PER_LONG);
+       ret->size = size;
+       ret->min = min;
+       ret->max = max;
+
+       return ret;
+}
+
+struct idmap *idmap_new(unsigned int size)
+{
+       return idmap_new_from_range(1, size);
+}
+
+void idmap_free(struct idmap *idmap)
+{
+       g_free(idmap->bits);
+       g_free(idmap);
+}
+
+void idmap_put(struct idmap *idmap, unsigned int id)
+{
+       unsigned int offset = (id - idmap->min) / BITS_PER_LONG;
+
+       id -= idmap->min;
+
+       if (id > idmap->size)
+               return;
+
+       id %= BITS_PER_LONG;
+
+       idmap->bits[offset] &= ~(1 << id);
+}
+
+unsigned int idmap_alloc(struct idmap *idmap)
+{
+       unsigned int bit;
+       unsigned int offset;
+
+       bit = find_next_zero_bit(idmap->bits, idmap->size, 0);
+
+       if (bit >= idmap->size)
+               return idmap->max + 1;
+
+       offset = bit / BITS_PER_LONG;
+       idmap->bits[offset] |= 1 << (bit % BITS_PER_LONG);
+
+       return bit + idmap->min;
+}
+
+void idmap_take(struct idmap *idmap, unsigned int id)
+{
+       unsigned int bit = id - idmap->min;
+       unsigned int offset;
+
+       if (bit >= idmap->size)
+               return;
+
+       offset = bit / BITS_PER_LONG;
+       idmap->bits[offset] |= 1 << (bit % BITS_PER_LONG);
+}
+
+/*
+ * Allocate the next bit skipping the ids up to and including last.  If there
+ * is no free ids until the max id is encountered, the counter is wrapped back
+ * to min and the search starts again.
+ */
+unsigned int idmap_alloc_next(struct idmap *idmap, unsigned int last)
+{
+       unsigned int bit;
+       unsigned int offset;
+
+       if (last < idmap->min || last > idmap->max)
+               return idmap->max + 1;
+
+       bit = find_next_zero_bit(idmap->bits, idmap->size,
+                                       last - idmap->min + 1);
+
+       if (bit >= idmap->size)
+               return idmap_alloc(idmap);
+
+       offset = bit / BITS_PER_LONG;
+       idmap->bits[offset] |= 1 << (bit % BITS_PER_LONG);
+
+       return bit + idmap->min;
+}
+
+unsigned int idmap_get_min(struct idmap *idmap)
+{
+       return idmap->min;
+}
+
+unsigned int idmap_get_max(struct idmap *idmap)
+{
+       return idmap->max;
+}
diff --git a/src/idmap.h b/src/idmap.h
new file mode 100644 (file)
index 0000000..ebda177
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct idmap;
+
+struct idmap *idmap_new(unsigned int size);
+void idmap_free(struct idmap *idmap);
+void idmap_put(struct idmap *idmap, unsigned int id);
+void idmap_take(struct idmap *idmap, unsigned int id);
+unsigned int idmap_alloc(struct idmap *idmap);
+unsigned int idmap_alloc_next(struct idmap *idmap, unsigned int last);
+struct idmap *idmap_new_from_range(unsigned int min, unsigned int max);
+unsigned int idmap_get_min(struct idmap *idmap);
+unsigned int idmap_get_max(struct idmap *idmap);
diff --git a/src/location-reporting.c b/src/location-reporting.c
new file mode 100644 (file)
index 0000000..2fa5b02
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  ProFUSION embedded systems.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+#include "common.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+static GSList *g_drivers = NULL;
+
+struct ofono_location_reporting {
+       DBusMessage *pending;
+       const struct ofono_location_reporting_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       ofono_bool_t enabled;
+       char *client_owner;
+       guint disconnect_watch;
+};
+
+static const char *location_reporting_type_to_string(
+                                       enum ofono_location_reporting_type type)
+{
+       switch (type) {
+       case OFONO_LOCATION_REPORTING_TYPE_NMEA:
+               return "nmea";
+       };
+
+       return NULL;
+}
+
+static DBusMessage *location_reporting_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+
+{
+       struct ofono_location_reporting *lr = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *type;
+       int value;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       value = lr->enabled;
+       ofono_dbus_dict_append(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &value);
+
+       type = location_reporting_type_to_string(lr->driver->type);
+       ofono_dbus_dict_append(&dict, "Type", DBUS_TYPE_STRING, &type);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void client_remove(struct ofono_location_reporting *lr)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (lr->disconnect_watch) {
+               g_dbus_remove_watch(conn, lr->disconnect_watch);
+               lr->disconnect_watch = 0;
+       }
+
+       g_free(lr->client_owner);
+}
+
+static void signal_enabled(const struct ofono_location_reporting *lr)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(lr->atom);
+       int value = lr->enabled;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_LOCATION_REPORTING_INTERFACE,
+                                       "Enabled", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void client_exited_disable_cb(const struct ofono_error *error,
+                                                               void *data)
+{
+       struct ofono_location_reporting *lr = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Disabling location-reporting failed");
+
+               return;
+       }
+
+       client_remove(lr);
+       lr->enabled = FALSE;
+
+       signal_enabled(lr);
+}
+
+static void client_exited(DBusConnection *conn, void *data)
+{
+       struct ofono_location_reporting *lr = data;
+
+       lr->disconnect_watch = 0;
+
+       lr->driver->disable(lr, client_exited_disable_cb , lr);
+}
+
+static void location_reporting_disable_cb(const struct ofono_error *error,
+                                                               void *data)
+{
+       struct ofono_location_reporting *lr = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Disabling location-reporting failed");
+
+               reply = __ofono_error_failed(lr->pending);
+               __ofono_dbus_pending_reply(&lr->pending, reply);
+
+               return;
+       }
+
+       client_remove(lr);
+       lr->enabled = FALSE;
+
+       reply = dbus_message_new_method_return(lr->pending);
+       __ofono_dbus_pending_reply(&lr->pending, reply);
+
+       signal_enabled(lr);
+}
+
+static void location_reporting_enable_cb(const struct ofono_error *error,
+                                                       int fd, void *data)
+{
+       struct ofono_location_reporting *lr = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Enabling location-reporting failed");
+
+               reply = __ofono_error_failed(lr->pending);
+               __ofono_dbus_pending_reply(&lr->pending, reply);
+
+               return;
+       }
+
+       lr->enabled = TRUE;
+       lr->client_owner = g_strdup(dbus_message_get_sender(lr->pending));
+       lr->disconnect_watch = g_dbus_add_disconnect_watch(conn,
+                               lr->client_owner, client_exited, lr, NULL);
+
+       reply = dbus_message_new_method_return(lr->pending);
+       dbus_message_append_args(reply, DBUS_TYPE_UNIX_FD, &fd,
+                                                       DBUS_TYPE_INVALID);
+
+       __ofono_dbus_pending_reply(&lr->pending, reply);
+
+       signal_enabled(lr);
+}
+
+static DBusMessage *location_reporting_request(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_location_reporting *lr = data;
+
+       if (lr->pending != NULL)
+               return __ofono_error_busy(msg);
+
+       if (lr->enabled)
+               return __ofono_error_in_use(msg);
+
+       lr->pending = dbus_message_ref(msg);
+
+       lr->driver->enable(lr, location_reporting_enable_cb, lr);
+
+       return NULL;
+}
+
+static DBusMessage *location_reporting_release(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_location_reporting *lr = data;
+       const char *caller = dbus_message_get_sender(msg);
+
+
+       /*
+        * Avoid a race by not trying to release the device if there is a
+        * pending message or client already signaled it's exiting. In the
+        * later case, the device will eventually be released in
+        * client_exited_disable_cb().
+        */
+       if (lr->pending != NULL || (lr->enabled && !lr->disconnect_watch))
+               return __ofono_error_busy(msg);
+
+       if (lr->enabled == FALSE)
+               return __ofono_error_not_available(msg);
+
+       if (g_strcmp0(caller, lr->client_owner))
+               return __ofono_error_access_denied(msg);
+
+       lr->pending = dbus_message_ref(msg);
+
+       lr->driver->disable(lr, location_reporting_disable_cb, lr);
+
+       return NULL;
+}
+
+static GDBusMethodTable location_reporting_methods[] = {
+       { "GetProperties",  "",    "a{sv}", location_reporting_get_properties },
+       { "Request",        "",    "h",     location_reporting_request,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Release",        "",    "",      location_reporting_release,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable location_reporting_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+int ofono_location_reporting_driver_register(
+                               const struct ofono_location_reporting_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d == NULL || d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_location_reporting_driver_unregister(
+                               const struct ofono_location_reporting_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d == NULL)
+               return;
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+struct ofono_modem *ofono_location_reporting_get_modem(
+                                       struct ofono_location_reporting *lr)
+{
+       return __ofono_atom_get_modem(lr->atom);
+}
+
+static void location_reporting_unregister(struct ofono_atom *atom)
+{
+       struct ofono_location_reporting *lr = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(lr->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(lr->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_LOCATION_REPORTING_INTERFACE);
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_LOCATION_REPORTING_INTERFACE);
+}
+
+static void location_reporting_remove(struct ofono_atom *atom)
+{
+       struct ofono_location_reporting *lr = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (lr == NULL)
+               return;
+
+       if (lr->driver && lr->driver->remove)
+               lr->driver->remove(lr);
+
+       g_free(lr);
+}
+
+struct ofono_location_reporting *ofono_location_reporting_create(
+                                               struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver, void *data)
+{
+       struct ofono_location_reporting *lr;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       /* Only D-Bus >= 1.3 supports fd-passing */
+       if (DBUS_TYPE_UNIX_FD == -1)
+               return NULL;
+
+       lr = g_try_new0(struct ofono_location_reporting, 1);
+       if (lr == NULL)
+               return NULL;
+
+       lr->atom = __ofono_modem_add_atom(modem,
+                                       OFONO_ATOM_TYPE_LOCATION_REPORTING,
+                                       location_reporting_remove, lr);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_location_reporting_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver) != 0)
+                       continue;
+
+               if (drv->probe(lr, vendor, data) < 0)
+                       continue;
+
+               lr->driver = drv;
+               break;
+       }
+
+       return lr;
+}
+
+void ofono_location_reporting_register(struct ofono_location_reporting *lr)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(lr->atom);
+       const char *path = __ofono_atom_get_path(lr->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_LOCATION_REPORTING_INTERFACE,
+                                       location_reporting_methods,
+                                       location_reporting_signals,
+                                       NULL, lr, NULL)) {
+               ofono_error("Could not create %s interface",
+                                       OFONO_LOCATION_REPORTING_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_LOCATION_REPORTING_INTERFACE);
+       __ofono_atom_register(lr->atom, location_reporting_unregister);
+}
+
+void ofono_location_reporting_remove(struct ofono_location_reporting *lr)
+{
+       __ofono_atom_free(lr->atom);
+}
+
+void ofono_location_reporting_set_data(struct ofono_location_reporting *lr,
+                                                               void *data)
+{
+       lr->driver_data = data;
+}
+
+void *ofono_location_reporting_get_data(struct ofono_location_reporting *lr)
+{
+       return lr->driver_data;
+}
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..febc874
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,326 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <execinfo.h>
+#include <dlfcn.h>
+
+#include "ofono.h"
+
+static const char *program_exec;
+static const char *program_path;
+
+/**
+ * ofono_info:
+ * @format: format string
+ * @Varargs: list of arguments
+ *
+ * Output general information
+ */
+void ofono_info(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_INFO, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * ofono_warn:
+ * @format: format string
+ * @Varargs: list of arguments
+ *
+ * Output warning messages
+ */
+void ofono_warn(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_WARNING, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * ofono_error:
+ * @format: format string
+ * @varargs: list of arguments
+ *
+ * Output error messages
+ */
+void ofono_error(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_ERR, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * ofono_debug:
+ * @format: format string
+ * @varargs: list of arguments
+ *
+ * Output debug message
+ *
+ * The actual output of the debug message is controlled via a command line
+ * switch. If not enabled, these messages will be ignored.
+ */
+void ofono_debug(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_DEBUG, format, ap);
+
+       va_end(ap);
+}
+
+static void print_backtrace(unsigned int offset)
+{
+       void *frames[99];
+       size_t n_ptrs;
+       unsigned int i;
+       int outfd[2], infd[2];
+       int pathlen;
+       pid_t pid;
+
+       if (program_exec == NULL)
+               return;
+
+       pathlen = strlen(program_path);
+
+       n_ptrs = backtrace(frames, G_N_ELEMENTS(frames));
+       if (n_ptrs < offset)
+               return;
+
+       if (pipe(outfd) < 0)
+               return;
+
+       if (pipe(infd) < 0) {
+               close(outfd[0]);
+               close(outfd[1]);
+               return;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               close(outfd[0]);
+               close(outfd[1]);
+               close(infd[0]);
+               close(infd[1]);
+               return;
+       }
+
+       if (pid == 0) {
+               close(outfd[1]);
+               close(infd[0]);
+
+               dup2(outfd[0], STDIN_FILENO);
+               dup2(infd[1], STDOUT_FILENO);
+
+               execlp("addr2line", "-C", "-f", "-e", program_exec, NULL);
+
+               exit(EXIT_FAILURE);
+       }
+
+       close(outfd[0]);
+       close(infd[1]);
+
+       ofono_error("++++++++ backtrace ++++++++");
+
+       for (i = offset; i < n_ptrs - 1; i++) {
+               Dl_info info;
+               char addr[20], buf[PATH_MAX * 2];
+               int len, written;
+               char *ptr, *pos;
+
+               dladdr(frames[i], &info);
+
+               len = snprintf(addr, sizeof(addr), "%p\n", frames[i]);
+               if (len < 0)
+                       break;
+
+               written = write(outfd[1], addr, len);
+               if (written < 0)
+                       break;
+
+               len = read(infd[0], buf, sizeof(buf));
+               if (len < 0)
+                       break;
+
+               buf[len] = '\0';
+
+               pos = strchr(buf, '\n');
+               *pos++ = '\0';
+
+               if (strcmp(buf, "??") == 0) {
+                       ofono_error("#%-2u %p in %s", i - offset,
+                                               frames[i], info.dli_fname);
+                       continue;
+               }
+
+               ptr = strchr(pos, '\n');
+               *ptr++ = '\0';
+
+               if (strncmp(pos, program_path, pathlen) == 0)
+                       pos += pathlen + 1;
+
+               ofono_error("#%-2u %p in %s() at %s", i - offset,
+                                               frames[i], buf, pos);
+       }
+
+       ofono_error("+++++++++++++++++++++++++++");
+
+       kill(pid, SIGTERM);
+
+       close(outfd[1]);
+       close(infd[0]);
+}
+
+static void signal_handler(int signo)
+{
+       ofono_error("Aborting (signal %d) [%s]", signo, program_exec);
+
+       print_backtrace(2);
+
+       exit(EXIT_FAILURE);
+}
+
+static void signal_setup(sighandler_t handler)
+{
+       struct sigaction sa;
+       sigset_t mask;
+
+       sigemptyset(&mask);
+       sa.sa_handler = handler;
+       sa.sa_mask = mask;
+       sa.sa_flags = 0;
+       sigaction(SIGBUS, &sa, NULL);
+       sigaction(SIGILL, &sa, NULL);
+       sigaction(SIGFPE, &sa, NULL);
+       sigaction(SIGSEGV, &sa, NULL);
+       sigaction(SIGABRT, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+}
+
+extern struct ofono_debug_desc __start___debug[];
+extern struct ofono_debug_desc __stop___debug[];
+
+static gchar **enabled = NULL;
+
+static ofono_bool_t is_enabled(struct ofono_debug_desc *desc)
+{
+       int i;
+
+       if (enabled == NULL)
+               return FALSE;
+
+       for (i = 0; enabled[i] != NULL; i++) {
+               if (desc->name != NULL && g_pattern_match_simple(enabled[i],
+                                                       desc->name) == TRUE)
+                       return TRUE;
+               if (desc->file != NULL && g_pattern_match_simple(enabled[i],
+                                                       desc->file) == TRUE)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+void __ofono_log_enable(struct ofono_debug_desc *start,
+                                       struct ofono_debug_desc *stop)
+{
+       struct ofono_debug_desc *desc;
+       const char *name = NULL, *file = NULL;
+
+       if (start == NULL || stop == NULL)
+               return;
+
+       for (desc = start; desc < stop; desc++) {
+               if (file != NULL || name != NULL) {
+                       if (g_strcmp0(desc->file, file) == 0) {
+                               if (desc->name == NULL)
+                                       desc->name = name;
+                       } else
+                               file = NULL;
+               }
+
+               if (is_enabled(desc) == TRUE)
+                       desc->flags |= OFONO_DEBUG_FLAG_PRINT;
+       }
+}
+
+int __ofono_log_init(const char *program, const char *debug,
+                                               ofono_bool_t detach)
+{
+       static char path[PATH_MAX];
+       int option = LOG_NDELAY | LOG_PID;
+
+       program_exec = program;
+       program_path = getcwd(path, sizeof(path));
+
+       if (debug != NULL)
+               enabled = g_strsplit_set(debug, ":, ", 0);
+
+       __ofono_log_enable(__start___debug, __stop___debug);
+
+       if (detach == FALSE)
+               option |= LOG_PERROR;
+
+       signal_setup(signal_handler);
+
+       openlog(basename(program), option, LOG_DAEMON);
+
+       syslog(LOG_INFO, "oFono version %s", VERSION);
+
+       return 0;
+}
+
+void __ofono_log_cleanup(void)
+{
+       syslog(LOG_INFO, "Exit");
+
+       closelog();
+
+       signal_setup(SIG_DFL);
+
+       g_strfreev(enabled);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..42c452f
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+
+#include <gdbus.h>
+
+#ifdef HAVE_CAPNG
+#include <cap-ng.h>
+#endif
+
+#include "ofono.h"
+
+#define SHUTDOWN_GRACE_SECONDS 10
+
+static GMainLoop *event_loop;
+
+void __ofono_exit(void)
+{
+       g_main_loop_quit(event_loop);
+}
+
+static gboolean quit_eventloop(gpointer user_data)
+{
+       __ofono_exit();
+       return FALSE;
+}
+
+static unsigned int __terminated = 0;
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct signalfd_siginfo si;
+       ssize_t result;
+       int fd;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+               return FALSE;
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       result = read(fd, &si, sizeof(si));
+       if (result != sizeof(si))
+               return FALSE;
+
+       switch (si.ssi_signo) {
+       case SIGINT:
+       case SIGTERM:
+               if (__terminated == 0) {
+                       ofono_info("Terminating");
+                       g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
+                                               quit_eventloop, NULL);
+                       __ofono_modem_shutdown();
+               }
+
+               __terminated = 1;
+               break;
+       }
+
+       return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+       GIOChannel *channel;
+       guint source;
+       sigset_t mask;
+       int fd;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               perror("Failed to set signal mask");
+               return 0;
+       }
+
+       fd = signalfd(-1, &mask, 0);
+       if (fd < 0) {
+               perror("Failed to create signal descriptor");
+               return 0;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       source = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               signal_handler, NULL);
+
+       g_io_channel_unref(channel);
+
+       return source;
+}
+
+static void system_bus_disconnected(DBusConnection *conn, void *user_data)
+{
+       ofono_error("System bus has disconnected!");
+
+       g_main_loop_quit(event_loop);
+}
+
+static gchar *option_debug = NULL;
+static gchar *option_plugin = NULL;
+static gchar *option_noplugin = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_version = FALSE;
+
+static gboolean parse_debug(const char *key, const char *value,
+                                       gpointer user_data, GError **error)
+{
+       if (value)
+               option_debug = g_strdup(value);
+       else
+               option_debug = g_strdup("*");
+
+       return TRUE;
+}
+
+static GOptionEntry options[] = {
+       { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+                               G_OPTION_ARG_CALLBACK, parse_debug,
+                               "Specify debug options to enable", "DEBUG" },
+       { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+                               "Specify plugins to load", "NAME,..," },
+       { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+                               "Specify plugins not to load", "NAME,..." },
+       { "nodetach", 'n', G_OPTION_FLAG_REVERSE,
+                               G_OPTION_ARG_NONE, &option_detach,
+                               "Don't run as daemon in background" },
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { NULL },
+};
+
+int main(int argc, char **argv)
+{
+       GOptionContext *context;
+       GError *err = NULL;
+       DBusConnection *conn;
+       DBusError error;
+       guint signal;
+
+#ifdef HAVE_CAPNG
+       /* Drop capabilities */
+       capng_clear(CAPNG_SELECT_BOTH);
+       capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+                               CAP_NET_BIND_SERVICE, CAP_NET_ADMIN,
+                               CAP_NET_RAW, CAP_SYS_ADMIN, -1);
+       capng_apply(CAPNG_SELECT_BOTH);
+#endif
+
+#ifdef NEED_THREADS
+       if (g_thread_supported() == FALSE)
+               g_thread_init(NULL);
+#endif
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+               if (err != NULL) {
+                       g_printerr("%s\n", err->message);
+                       g_error_free(err);
+                       return 1;
+               }
+
+               g_printerr("An unknown error occurred\n");
+               return 1;
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               printf("%s\n", VERSION);
+               exit(0);
+       }
+
+       if (option_detach == TRUE) {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       return 1;
+               }
+       }
+
+       event_loop = g_main_loop_new(NULL, FALSE);
+
+#ifdef NEED_THREADS
+       if (dbus_threads_init_default() == FALSE) {
+               fprintf(stderr, "Can't init usage of threads\n");
+               exit(1);
+       }
+#endif
+
+       signal = setup_signalfd();
+
+       __ofono_log_init(argv[0], option_debug, option_detach);
+
+       dbus_error_init(&error);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, OFONO_SERVICE, &error);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       ofono_error("Unable to hop onto D-Bus: %s",
+                                       error.message);
+                       dbus_error_free(&error);
+               } else {
+                       ofono_error("Unable to hop onto D-Bus");
+               }
+
+               goto cleanup;
+       }
+
+       g_dbus_set_disconnect_function(conn, system_bus_disconnected,
+                                       NULL, NULL);
+
+       __ofono_dbus_init(conn);
+
+       __ofono_modemwatch_init();
+
+       __ofono_manager_init();
+
+       __ofono_plugin_init(option_plugin, option_noplugin);
+
+       g_free(option_plugin);
+       g_free(option_noplugin);
+
+       g_main_loop_run(event_loop);
+
+       __ofono_plugin_cleanup();
+
+       __ofono_manager_cleanup();
+
+       __ofono_modemwatch_cleanup();
+
+       __ofono_dbus_cleanup();
+       dbus_connection_unref(conn);
+
+cleanup:
+       g_source_remove(signal);
+
+       g_main_loop_unref(event_loop);
+
+       __ofono_log_cleanup();
+
+       return 0;
+}
diff --git a/src/manager.c b/src/manager.c
new file mode 100644 (file)
index 0000000..9614527
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+static void append_modem(struct ofono_modem *modem, void *userdata)
+{
+       DBusMessageIter *array = userdata;
+       const char *path = ofono_modem_get_path(modem);
+       DBusMessageIter entry, dict;
+
+       if (ofono_modem_is_registered(modem) == FALSE)
+               return;
+
+       dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+                                               NULL, &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                                       &path);
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+                               OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                               &dict);
+
+       __ofono_modem_append_properties(modem, &dict);
+       dbus_message_iter_close_container(&entry, &dict);
+       dbus_message_iter_close_container(array, &entry);
+}
+
+static DBusMessage *manager_get_modems(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter array;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+       __ofono_modem_foreach(append_modem, &array);
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static GDBusMethodTable manager_methods[] = {
+       { "GetModems",          "",    "a(oa{sv})",  manager_get_modems },
+       { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+       { "ModemAdded",        "oa{sv}" },
+       { "ModemRemoved",      "o" },
+       { }
+};
+
+int __ofono_manager_init(void)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       gboolean ret;
+
+       ret = g_dbus_register_interface(conn, OFONO_MANAGER_PATH,
+                                       OFONO_MANAGER_INTERFACE,
+                                       manager_methods, manager_signals,
+                                       NULL, NULL, NULL);
+
+       if (ret == FALSE)
+               return -1;
+
+       return 0;
+}
+
+void __ofono_manager_cleanup(void)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       g_dbus_unregister_interface(conn, OFONO_MANAGER_PATH,
+                                       OFONO_MANAGER_INTERFACE);
+}
diff --git a/src/message-waiting.c b/src/message-waiting.c
new file mode 100644 (file)
index 0000000..328e193
--- /dev/null
@@ -0,0 +1,1111 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "util.h"
+#include "simutil.h"
+#include "smsutil.h"
+
+struct mailbox_state {
+       gboolean indication;
+       unsigned char message_count;
+};
+
+struct ofono_message_waiting {
+       struct mailbox_state messages[5];
+       unsigned char efmwis_length;
+       unsigned char efmbdn_length;
+       unsigned char efmbdn_record_id[5];
+       unsigned int efmbdn_watch;
+       unsigned char ef_cphs_mwis_length;
+       unsigned char ef_cphs_mbdn_length;
+       unsigned int ef_cphs_mbdn_watch;
+       gboolean mbdn_not_provided;
+       gboolean cphs_mbdn_not_provided;
+       struct ofono_phone_number mailbox_number[5];
+       struct ofono_sim *sim;
+       struct ofono_sim_context *sim_context;
+       struct ofono_atom *atom;
+};
+
+struct mbdn_set_request {
+       struct ofono_message_waiting *mw;
+       int mailbox;
+       struct ofono_phone_number number;
+       DBusMessage *msg;
+       gboolean cphs;
+};
+
+static const char *mw_message_waiting_property_name[5] = {
+       "VoicemailWaiting",
+#if 0
+       "FaxWaiting",
+       "EmailWaiting",
+       "OtherWaiting",
+       "VideomailWaiting",
+#endif
+};
+
+static const char *mw_message_count_property_name[5] = {
+       "VoicemailMessageCount",
+#if 0
+       "FaxMessageCount",
+       "EmailMessageCount",
+       "OtherMessageCount",
+       "VideomailMessageCount",
+#endif
+};
+
+static const char *mw_mailbox_property_name[5] = {
+       "VoicemailMailboxNumber",
+#if 0
+       "FaxMailboxNumber",
+       "EmailMailboxNumber",
+       "OtherMailboxNumber",
+       "VideomailMailboxNumber",
+#endif
+};
+
+static const int mw_mailbox_to_cphs_record[5] = {
+       1, /* Line 1 mailbox */
+       4, /* Fax mailbox */
+       0,
+       3, /* Data mailbox */
+       0,
+};
+
+static void mbdn_set_cb(int ok, void *data);
+
+static DBusMessage *mw_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_message_waiting *mw = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       int i;
+       dbus_bool_t indication;
+       unsigned char count;
+       const char *number;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+       for (i = 0; i < 5; i++) {
+               if (mw_message_waiting_property_name[i]) {
+                       indication = mw->messages[i].indication;
+
+                       ofono_dbus_dict_append(&dict,
+                                       mw_message_waiting_property_name[i],
+                                       DBUS_TYPE_BOOLEAN, &indication);
+               }
+
+               if (mw_message_count_property_name[i]) {
+                       count = mw->messages[i].message_count;
+
+                       ofono_dbus_dict_append(&dict,
+                                       mw_message_count_property_name[i],
+                                       DBUS_TYPE_BYTE, &count);
+               }
+
+               if (mw_mailbox_property_name[i]) {
+                       number = phone_number_to_string(&mw->mailbox_number[i]);
+
+                       ofono_dbus_dict_append(&dict,
+                                       mw_mailbox_property_name[i],
+                                       DBUS_TYPE_STRING, &number);
+               }
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void cphs_mbdn_sync_cb(int ok, void *data)
+{
+       struct mbdn_set_request *req = data;
+
+       if (!ok)
+               ofono_info("Failed to synchronize CPHS MBDN record");
+
+       g_free(req);
+}
+
+static DBusMessage *set_cphs_mbdn(struct ofono_message_waiting *mw,
+                                       gboolean sync,
+                                       int mailbox,
+                                       const char *number,
+                                       DBusMessage *msg)
+{
+       struct mbdn_set_request *req;
+       unsigned char efmbdn[255];
+
+       if ((mw->ef_cphs_mbdn_length && !mw_mailbox_to_cphs_record[mailbox]) ||
+                       mw->cphs_mbdn_not_provided == TRUE) {
+               if (msg)
+                       return __ofono_error_not_supported(msg);
+
+               return NULL;
+       }
+
+       if (mw->ef_cphs_mbdn_length == 0) {
+               if (msg)
+                       return __ofono_error_sim_not_ready(msg);
+
+               return NULL;
+       }
+
+       req = g_new0(struct mbdn_set_request, 1);
+
+       req->mw = mw;
+       req->mailbox = mailbox;
+       string_to_phone_number(number, &req->number);
+       req->cphs = TRUE;
+
+       sim_adn_build(efmbdn, req->mw->ef_cphs_mbdn_length,
+                       &req->number, NULL);
+
+       if (ofono_sim_write(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID,
+                       sync ? cphs_mbdn_sync_cb : mbdn_set_cb,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                       mw_mailbox_to_cphs_record[mailbox],
+                       efmbdn, mw->ef_cphs_mbdn_length, req) == -1) {
+               g_free(req);
+
+               if (msg)
+                       return __ofono_error_failed(msg);
+       } else
+               req->msg = msg ? dbus_message_ref(msg) : NULL;
+
+       return NULL;
+}
+
+static void mbdn_set_cb(int ok, void *data)
+{
+       struct mbdn_set_request *req = data;
+       struct ofono_phone_number *old = &req->mw->mailbox_number[req->mailbox];
+       const char *property;
+       DBusMessage *reply = NULL;
+
+       if (!ok) {
+               if (req->msg)
+                       reply = __ofono_error_failed(req->msg);
+
+               goto out;
+       }
+
+       if (req->msg)
+               reply = dbus_message_new_method_return(req->msg);
+
+       if (g_str_equal(req->number.number, old->number) &&
+                       req->number.type == old->type)
+               goto out;
+
+       memcpy(old, &req->number, sizeof(struct ofono_phone_number));
+
+       property = mw_mailbox_property_name[req->mailbox];
+
+       if (property) {
+               DBusConnection *conn = ofono_dbus_get_connection();
+               const char *path = __ofono_atom_get_path(req->mw->atom);
+               const char *number;
+
+               number = phone_number_to_string(old);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MESSAGE_WAITING_INTERFACE,
+                                               property, DBUS_TYPE_STRING,
+                                               &number);
+       }
+
+       /*
+        * Make a single attempt at keeping the CPHS version of the file
+        * in sync.
+        */
+       if (req->cphs == FALSE)
+               set_cphs_mbdn(req->mw, TRUE, req->mailbox,
+                               phone_number_to_string(&req->number), NULL);
+
+out:
+       if (req->msg && reply)
+               __ofono_dbus_pending_reply(&req->msg, reply);
+
+       g_free(req);
+}
+
+static DBusMessage *set_mbdn(struct ofono_message_waiting *mw, int mailbox,
+                       const char *number, DBusMessage *msg)
+{
+       struct mbdn_set_request *req;
+       unsigned char efmbdn[255];
+
+       /*
+        * If we have no 3GPP EFmbdn on the card, maybe the
+        * CPHS version is available
+        */
+       if ((mw->efmbdn_length > 0 && mw->efmbdn_record_id[mailbox] == 0) ||
+                       mw->mbdn_not_provided == TRUE)
+               return set_cphs_mbdn(mw, FALSE, mailbox, number, msg);
+
+       if (mw->efmbdn_length == 0) {
+               if (msg)
+                       return __ofono_error_sim_not_ready(msg);
+
+               return NULL;
+       }
+
+       req = g_new0(struct mbdn_set_request, 1);
+
+       req->mw = mw;
+       req->mailbox = mailbox;
+       string_to_phone_number(number, &req->number);
+       req->cphs = FALSE;
+
+       sim_adn_build(efmbdn, req->mw->efmbdn_length, &req->number, NULL);
+
+       if (ofono_sim_write(req->mw->sim_context, SIM_EFMBDN_FILEID,
+                               mbdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               req->mw->efmbdn_record_id[mailbox],
+                               efmbdn, req->mw->efmbdn_length, req) == -1) {
+               g_free(req);
+
+               if (msg)
+                       return __ofono_error_failed(msg);
+       } else
+               req->msg = msg ? dbus_message_ref(msg) : NULL;
+
+       return NULL;
+}
+
+static DBusMessage *mw_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_message_waiting *mw = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *name, *value;
+       int i;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+
+       for (i = 0; i < 5; i++)
+               if (mw_mailbox_property_name[i] &&
+                               !strcmp(name, mw_mailbox_property_name[i]))
+                       break;
+
+       if (i < 5) {
+               const char *cur_number;
+
+               dbus_message_iter_next(&iter);
+
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_recurse(&iter, &var);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (!valid_phone_number_format(value))
+                       return __ofono_error_invalid_format(msg);
+
+               cur_number = phone_number_to_string(&mw->mailbox_number[i]);
+
+               if (g_str_equal(cur_number, value))
+                       return dbus_message_new_method_return(msg);
+
+               return set_mbdn(mw, i, value, msg);
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable message_waiting_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        mw_get_properties       },
+       { "SetProperty",        "sv",   "",             mw_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable message_waiting_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static void update_indicator_and_emit(struct ofono_message_waiting *mw,
+                                       int mailbox,
+                                       struct mailbox_state *info)
+{
+       dbus_bool_t indication;
+       unsigned char count;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(mw->atom);
+
+       if (mw->messages[mailbox].message_count == info->message_count &&
+                       mw->messages[mailbox].indication == info->indication)
+               return;
+
+       memcpy(&mw->messages[mailbox], info, sizeof(struct mailbox_state));
+
+       indication = info->indication;
+       count = info->message_count;
+
+       if (mw_message_waiting_property_name[mailbox] == NULL)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_MESSAGE_WAITING_INTERFACE,
+                               mw_message_waiting_property_name[mailbox],
+                               DBUS_TYPE_BOOLEAN, &indication);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_MESSAGE_WAITING_INTERFACE,
+                               mw_message_count_property_name[mailbox],
+                               DBUS_TYPE_BYTE, &count);
+}
+
+static void mw_cphs_mwis_read_cb(int ok, int total_length, int record,
+                                       const unsigned char *data,
+                                       int record_length, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+       struct mailbox_state info;
+       unsigned char indication;
+
+       if (!ok || total_length < 1) {
+               DBG("No CPHS MWIS on SIM");
+               mw->ef_cphs_mwis_length = 0;
+               return;
+       }
+
+       mw->ef_cphs_mwis_length = total_length;
+
+       if (mw->efmwis_length != 0)
+               return;
+
+       /* Read Line 1 indication */
+       indication = data[0] & 0xf;
+       info.indication = (indication == 0xa);
+       info.message_count = 0;
+       update_indicator_and_emit(mw, 0, &info);
+
+       if (total_length == 1)
+               return;
+
+       /* Read Fax indication */
+       indication = data[1] & 0xf;
+       info.indication = (indication == 0xa);
+       info.message_count = 0;
+       update_indicator_and_emit(mw, 1, &info);
+
+       /* Read Data indication, map to 'Other' */
+       indication = (data[1] >> 4) & 0xf;
+       info.indication = (indication == 0xa);
+       info.message_count = 0;
+       update_indicator_and_emit(mw, 3, &info);
+}
+
+static void mw_mwis_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+       int i, status;
+       struct mailbox_state info;
+
+       if (!ok || record_length < 5) {
+               ofono_error("Unable to read waiting messages numbers "
+                               "from SIM");
+
+               mw->efmwis_length = 0;
+
+               return;
+       }
+
+       /* Handle only current identity (TODO: currently assumes first) */
+       if (record != 1)
+               return;
+
+       status = data[0];
+       data++;
+
+       for (i = 0; i < 5 && i < record_length - 1; i++) {
+               info.indication = (status >> i) & 1;
+               info.message_count = info.indication ? data[0] : 0;
+
+               update_indicator_and_emit(mw, i, &info);
+       }
+
+       mw->efmwis_length = record_length;
+}
+
+static void mw_cphs_mbdn_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+       int i;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *value;
+
+       if (!ok || record_length < 14 || total_length < record_length) {
+               ofono_error("Unable to read CPHS mailbox dialling numbers "
+                               "from SIM");
+
+               mw->ef_cphs_mbdn_length = 0;
+               mw->cphs_mbdn_not_provided = TRUE;
+               return;
+       }
+
+       for (i = 0; i < 5; i++)
+               if (record == mw_mailbox_to_cphs_record[i])
+                       break;
+
+       if (i == 5)
+               return;
+
+       mw->ef_cphs_mbdn_length = record_length;
+
+       if (mw->mbdn_not_provided != TRUE)
+               return;
+
+       ofono_info("3GPP MBDN not provided, parsing CPHS..");
+
+       if (sim_adn_parse(data, record_length, &mw->mailbox_number[i], NULL) ==
+                       FALSE)
+               mw->mailbox_number[i].number[0] = '\0';
+
+       if (mw_mailbox_property_name[i]) {
+               const char *path = __ofono_atom_get_path(mw->atom);
+
+               value = phone_number_to_string(&mw->mailbox_number[i]);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_MESSAGE_WAITING_INTERFACE,
+                               mw_mailbox_property_name[i],
+                               DBUS_TYPE_STRING, &value);
+       }
+}
+
+static void mw_mbdn_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+       int i;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *value;
+
+       if (!ok || record_length < 14 || total_length < record_length) {
+               ofono_error("Unable to read mailbox dialling numbers "
+                               "from SIM");
+
+               mw->efmbdn_length = 0;
+               mw->mbdn_not_provided = TRUE;
+               return;
+       }
+
+       for (i = 0; i < 5; i++)
+               if (record == mw->efmbdn_record_id[i])
+                       break;
+
+       if (i == 5)
+               return;
+
+       if (sim_adn_parse(data, record_length, &mw->mailbox_number[i], NULL) ==
+                       FALSE)
+               mw->mailbox_number[i].number[0] = '\0';
+
+       if (mw_mailbox_property_name[i]) {
+               const char *path = __ofono_atom_get_path(mw->atom);
+
+               value = phone_number_to_string(&mw->mailbox_number[i]);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                               OFONO_MESSAGE_WAITING_INTERFACE,
+                               mw_mailbox_property_name[i],
+                               DBUS_TYPE_STRING, &value);
+       }
+
+       mw->efmbdn_length = record_length;
+}
+
+static void mw_mbdn_changed(int id, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+       int err;
+
+       mw->efmbdn_length = 0;
+       mw->mbdn_not_provided = FALSE;
+
+       err = ofono_sim_read(mw->sim_context, SIM_EFMBDN_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               mw_mbdn_read_cb, mw);
+       if (err != 0)
+               ofono_error("Unable to read EF-MBDN from SIM");
+}
+
+static void mw_cphs_mbdn_changed(int id, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+
+       mw->ef_cphs_mbdn_length = 0;
+       mw->cphs_mbdn_not_provided = FALSE;
+
+       ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                       mw_cphs_mbdn_read_cb, mw);
+}
+
+const struct ofono_phone_number *__ofono_message_waiting_get_mbdn(
+                                       struct ofono_message_waiting *mw,
+                                       unsigned int index)
+{
+       return &mw->mailbox_number[index];
+}
+
+static void mw_mbi_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+       int i, err;
+
+       if (!ok || record_length < 4) {
+               ofono_error("Unable to read mailbox identifies "
+                               "from SIM");
+
+               mw->efmbdn_length = 0;
+               mw->mbdn_not_provided = TRUE;
+
+               goto out;
+       }
+
+       /* Handle only current identity (TODO: currently assumes first) */
+       if (record != 1)
+               return;
+
+       for (i = 0; i < 5 && i < record_length; i++)
+               mw->efmbdn_record_id[i] = data[i];
+
+       err = ofono_sim_read(mw->sim_context, SIM_EFMBDN_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               mw_mbdn_read_cb, mw);
+       mw->efmbdn_watch = ofono_sim_add_file_watch(mw->sim_context,
+                                               SIM_EFMBDN_FILEID,
+                                               mw_mbdn_changed, mw, NULL);
+
+       if (err != 0)
+               ofono_error("Unable to read EF-MBDN from SIM");
+
+out:
+       /*
+        * Mailbox numbers located in Byte 1, bits 6 & 5,
+        * Check for Activated & Allocated
+        */
+       if (__ofono_sim_cphs_service_available(mw->sim,
+                                       SIM_CPHS_SERVICE_MAILBOX_NUMBERS)) {
+               ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               mw_cphs_mbdn_read_cb, mw);
+               mw->ef_cphs_mbdn_watch = ofono_sim_add_file_watch(
+                                               mw->sim_context,
+                                               SIM_EF_CPHS_MBDN_FILEID,
+                                               mw_cphs_mbdn_changed, mw, NULL);
+       }
+}
+
+static void mw_mwis_write_cb(int ok, void *userdata)
+{
+       if (!ok)
+               ofono_error("Writing new EF-MWIS failed");
+}
+
+static void mw_set_indicator(struct ofono_message_waiting *mw, int profile,
+                               enum sms_mwi_type type,
+                               gboolean present, unsigned char messages)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       unsigned char efmwis[255];  /* Max record size */
+       int i;
+
+       if (mw == NULL)
+               return;
+
+       /* Handle only current identity (TODO: currently assumes first) */
+       if (profile != 1)
+               return;
+
+       if (mw->messages[type].indication == present &&
+                       mw->messages[type].message_count == messages)
+               return;
+
+       if (mw->messages[type].indication != present) {
+               dbus_bool_t indication;
+               const char *path = __ofono_atom_get_path(mw->atom);
+
+               indication = present;
+               mw->messages[type].indication = present;
+
+               if (mw_message_waiting_property_name[type])
+                       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_MESSAGE_WAITING_INTERFACE,
+                                       mw_message_waiting_property_name[type],
+                                       DBUS_TYPE_BOOLEAN, &indication);
+       }
+
+       if (mw->messages[type].message_count != messages) {
+               const char *path = __ofono_atom_get_path(mw->atom);
+
+               mw->messages[type].message_count = messages;
+
+               if (mw_message_waiting_property_name[type])
+                       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_MESSAGE_WAITING_INTERFACE,
+                                       mw_message_count_property_name[type],
+                                       DBUS_TYPE_BYTE, &messages);
+       }
+
+       /* Writes MWI states and/or MBDN back to SIM */
+       if (mw->efmwis_length < 5) {
+               if (mw->ef_cphs_mwis_length >= 1)
+                       goto try_cphs;
+
+               ofono_error("Unable to update MWIS indicator");
+               return;
+       }
+
+       /* Fill in numbers of messages in bytes 1 to X of EF-MWIS */
+       for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++)
+               efmwis[i + 1] = mw->messages[i].message_count;
+
+       /* Fill in indicator state bits in byte 0 */
+       for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++)
+               if (mw->messages[i].indication)
+                       efmwis[0] |= 1 << i;
+
+       if (ofono_sim_write(mw->sim_context, SIM_EFMWIS_FILEID,
+                               mw_mwis_write_cb,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED, 1,
+                               efmwis, mw->efmwis_length, mw) != 0) {
+               ofono_error("Queuing a EF-MWI write to SIM failed");
+       }
+
+       if (mw->ef_cphs_mwis_length == 0)
+               return;
+
+try_cphs:
+       memset(efmwis, 0x55, 255);
+
+       efmwis[0] = mw->messages[0].indication ? 0xa : 0x5;
+
+       if (mw->ef_cphs_mwis_length > 1)
+               efmwis[1] = mw->messages[1].indication ? 0xa : 0x5 |
+                       mw->messages[3].indication ? 0xa0 : 0x50;
+
+       if (ofono_sim_write(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID,
+                               mw_mwis_write_cb,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, 0,
+                               efmwis, mw->ef_cphs_mwis_length, mw) != 0)
+               ofono_error("Queuing a EF-MWIS write to SIM failed (CPHS)");
+}
+
+static void handle_special_sms_iei(struct ofono_message_waiting *mw,
+                                       const guint8 *iei, gboolean *discard)
+{
+       enum sms_mwi_type type;
+       int profile;
+       gboolean set;
+
+       /* Parse type & storage byte */
+       if (discard)
+               *discard = (iei[0] & (1 << 7)) ? FALSE : TRUE;
+
+       type = iei[0] & 0x1f;
+       if (type > SMS_MWI_TYPE_OTHER) {
+               if (type == (SMS_MWI_TYPE_OTHER | 4))
+                       type = SMS_MWI_TYPE_VIDEO;
+               else
+                       /*
+                        * 23.040 9.2.3.24.2: "Terminals should be capable of
+                        * receiving any values in octet 1, even including
+                        * those marked as Reserved."  Treat Reserved as
+                        * "Other".
+                        */
+                       type = SMS_MWI_TYPE_OTHER;
+       }
+
+       set = iei[1] > 0 ? TRUE : FALSE;
+       profile = ((iei[0] >> 5) & 3) + 1;
+
+       mw_set_indicator(mw, profile, type, set, iei[1]);
+}
+
+static void handle_enhanced_voicemail_iei(struct ofono_message_waiting *mw,
+                                               const guint8 *iei,
+                                               gboolean *discard, int length)
+{
+       int profile, n;
+       gboolean set;
+       struct sms_address mailbox_address;
+
+       if (length < 3)
+               return;
+
+       /* ENHANCED_VOICE_MAIL_PDU_TYPE */
+       if (!(iei[0] & 1)) {
+               /* 9.2.3.24.13.1 Enhanced Voice Mail Notification */
+
+               /* MULTIPLE_SUBSCRIBER_PROFILE */
+               profile = ((iei[0] >> 2) & 3) + 1;
+
+               /* SM_STORAGE */
+               if (discard)
+                       *discard = (iei[0] & (1 << 4)) ? FALSE : TRUE;
+
+               /* VM_MAILBOX_ACCESS_ADDRESS */
+               n = 0;
+               if (!sms_decode_address_field(iei + 1, length - 1, &n,
+                                       FALSE, &mailbox_address))
+                       return;
+
+               /* TODO: VM_MESSAGE_PRIORITY_INDICATION */
+
+               /* Other parameters currently not supported */
+
+               if (length < n + 3)
+                       return;
+
+               set = iei[n + 1] > 0 ? TRUE : FALSE;
+               mw_set_indicator(mw, profile, SMS_MWI_TYPE_VOICE,
+                                       set, iei[n + 1]);
+       } else {
+               /* 9.2.3.24.13.2 Enhanced Voice Delete Confirmation */
+
+               /* MULTIPLE_SUBSCRIBER_PROFILE */
+               profile = ((iei[0] >> 2) & 3) + 1;
+
+               /* SM_STORAGE */
+               if (discard)
+                       *discard = (iei[0] & (1 << 4)) ? FALSE : TRUE;
+
+               /* VM_MAILBOX_ACCESS_ADDRESS */
+               n = 0;
+               if (!sms_decode_address_field(iei + 1, length - 1, &n,
+                                       FALSE, &mailbox_address))
+                       return;
+
+               /* Other parameters currently not supported */
+
+               if (length < n + 3)
+                       return;
+
+               set = iei[n + 1] > 0 ? TRUE : FALSE;
+               mw_set_indicator(mw, profile, SMS_MWI_TYPE_VOICE,
+                                       set, iei[n + 1]);
+       }
+
+       if (mailbox_address.address[0] != '\0')
+               set_mbdn(mw, SMS_MWI_TYPE_VOICE,
+                               sms_address_to_string(&mailbox_address), NULL);
+}
+
+void __ofono_message_waiting_mwi(struct ofono_message_waiting *mw,
+                                       struct sms *sms, gboolean *out_discard)
+{
+       gboolean active, discard;
+       enum sms_mwi_type type;
+       int profile = 1, iei_found = 0;
+
+       if (out_discard)
+               *out_discard = FALSE;
+
+       /*
+        * Check MWI types in the order from highest priority to lowest
+        * because they must override one another.
+        */
+
+       if (sms->deliver.udhi) {
+               guint8 evm_iei[140];
+               struct sms_udh_iter iter;
+               enum sms_iei iei;
+
+               if (!sms_udh_iter_init(sms, &iter))
+                       return;
+
+               while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
+                               SMS_IEI_INVALID) {
+                       switch (iei) {
+                       case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION:
+                               sms_udh_iter_get_ie_data(&iter, evm_iei);
+
+                               handle_enhanced_voicemail_iei(mw, evm_iei,
+                                               out_discard,
+                                               sms_udh_iter_get_ie_length(
+                                                       &iter));
+                               return;
+                       default:
+                               break;
+                       }
+
+                       sms_udh_iter_next(&iter);
+               }
+       }
+
+       if (sms->deliver.udhi) {
+               guint8 special_iei[4];
+               struct sms_udh_iter iter;
+               enum sms_iei iei;
+
+               if (!sms_udh_iter_init(sms, &iter))
+                       return;
+
+               while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
+                               SMS_IEI_INVALID) {
+                       switch (iei) {
+                       case SMS_IEI_SPECIAL_MESSAGE_INDICATION:
+                               if (sms_udh_iter_get_ie_length(&iter) != 2)
+                                       break;
+                               sms_udh_iter_get_ie_data(&iter, special_iei);
+
+                               handle_special_sms_iei(mw, special_iei,
+                                               &discard);
+                               if (out_discard)
+                                       *out_discard = *out_discard || discard;
+                               iei_found = 1;
+                               break;
+                       default:
+                               break;
+                       }
+
+                       sms_udh_iter_next(&iter);
+               }
+
+               if (iei_found) {
+                       /*
+                        * 23.040 9.2.3.24.2 says "In the event of a
+                        * conflict between this setting and the setting
+                        * of the Data Coding Scheme (see 3GPP TS 23.038 [9])
+                        * then the message shall be stored if either the DCS
+                        * indicates this, or Octet 1 above indicates this."
+                        */
+                       if (sms_mwi_dcs_decode(sms->deliver.dcs, NULL,
+                                               NULL, NULL, &discard)) {
+                               if (out_discard)
+                                       *out_discard = *out_discard || discard;
+                       }
+
+                       return;
+               }
+       }
+
+       if (sms_mwi_dcs_decode(sms->deliver.dcs, &type,
+                               NULL, &active, out_discard)) {
+               mw_set_indicator(mw, profile, type, active, 0);
+
+               return;
+       }
+
+       if (sms->deliver.pid == SMS_PID_TYPE_RETURN_CALL)
+               return;
+}
+
+static void message_waiting_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+       struct ofono_message_waiting *mw = __ofono_atom_get_data(atom);
+
+       if (mw->sim_context) {
+               ofono_sim_context_free(mw->sim_context);
+               mw->sim_context = NULL;
+       }
+
+       mw->sim = NULL;
+
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_MESSAGE_WAITING_INTERFACE);
+       ofono_modem_remove_interface(modem, OFONO_MESSAGE_WAITING_INTERFACE);
+}
+
+static void mw_mwis_changed(int id, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+
+       mw->efmwis_length = 0;
+
+       ofono_sim_read(mw->sim_context, SIM_EFMWIS_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                       mw_mwis_read_cb, mw);
+}
+
+static void mw_cphs_mwis_changed(int id, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+
+       mw->ef_cphs_mwis_length = 0;
+
+       ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       mw_cphs_mwis_read_cb, mw);
+}
+
+static void mw_mbi_changed(int id, void *userdata)
+{
+       struct ofono_message_waiting *mw = userdata;
+
+       mw->efmbdn_length = 0;
+       mw->mbdn_not_provided = FALSE;
+
+       mw->ef_cphs_mbdn_length = 0;
+       mw->cphs_mbdn_not_provided = FALSE;
+
+       ofono_sim_remove_file_watch(mw->sim_context, mw->efmbdn_watch);
+       ofono_sim_remove_file_watch(mw->sim_context, mw->ef_cphs_mbdn_watch);
+
+       ofono_sim_read(mw->sim_context, SIM_EFMBI_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                       mw_mbi_read_cb, mw);
+}
+
+void ofono_message_waiting_register(struct ofono_message_waiting *mw)
+{
+       DBusConnection *conn;
+       const char *path;
+       struct ofono_modem *modem;
+
+       if (mw == NULL)
+               return;
+
+       conn = ofono_dbus_get_connection();
+       modem = __ofono_atom_get_modem(mw->atom);
+       path = __ofono_atom_get_path(mw->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_MESSAGE_WAITING_INTERFACE,
+                                       message_waiting_methods,
+                                       message_waiting_signals,
+                                       NULL, mw, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_MESSAGE_WAITING_INTERFACE);
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_MESSAGE_WAITING_INTERFACE);
+
+       mw->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+       if (mw->sim) {
+               /* Assume that if sim atom exists, it is ready */
+               mw->sim_context = ofono_sim_context_create(mw->sim);
+
+               /* Loads MWI states and MBDN from SIM */
+               ofono_sim_read(mw->sim_context, SIM_EFMWIS_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               mw_mwis_read_cb, mw);
+               ofono_sim_read(mw->sim_context, SIM_EFMBI_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               mw_mbi_read_cb, mw);
+
+               /* Also read CPHS MWIS field */
+               ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               mw_cphs_mwis_read_cb, mw);
+
+               /*
+                * The operator could send us SMS mwi updates, but let's be
+                * extra careful and track the file contents too.
+                */
+               ofono_sim_add_file_watch(mw->sim_context, SIM_EFMWIS_FILEID,
+                                               mw_mwis_changed, mw, NULL);
+               ofono_sim_add_file_watch(mw->sim_context,
+                                               SIM_EF_CPHS_MWIS_FILEID,
+                                               mw_cphs_mwis_changed, mw, NULL);
+
+               ofono_sim_add_file_watch(mw->sim_context, SIM_EFMBI_FILEID,
+                                               mw_mbi_changed, mw, NULL);
+       }
+
+       __ofono_atom_register(mw->atom, message_waiting_unregister);
+}
+
+static void mw_remove(struct ofono_atom *atom)
+{
+       struct ofono_message_waiting *mw = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (mw == NULL)
+               return;
+
+       g_free(mw);
+}
+
+struct ofono_message_waiting *ofono_message_waiting_create(struct ofono_modem *modem)
+{
+       struct ofono_message_waiting *mw;
+
+       mw = g_try_new0(struct ofono_message_waiting, 1);
+
+       if (mw == NULL)
+               return NULL;
+
+       mw->atom = __ofono_modem_add_atom(modem,
+                                       OFONO_ATOM_TYPE_MESSAGE_WAITING,
+                                       mw_remove, mw);
+
+       return mw;
+}
+
+void ofono_message_waiting_remove(struct ofono_message_waiting *mw)
+{
+       __ofono_atom_free(mw->atom);
+}
diff --git a/src/message.c b/src/message.c
new file mode 100644 (file)
index 0000000..7cc6538
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <gdbus.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "ofono.h"
+#include "message.h"
+
+struct message {
+       struct ofono_uuid uuid;
+       enum message_state state;
+       struct ofono_atom *atom;
+       void *data;
+};
+
+static const char *message_state_to_string(enum message_state s)
+{
+       switch (s) {
+       case MESSAGE_STATE_PENDING:
+               return "pending";
+       case MESSAGE_STATE_SENT:
+               return "sent";
+       case MESSAGE_STATE_FAILED:
+               return "failed";
+       case MESSAGE_STATE_CANCELLED:
+               return "cancelled";
+       }
+
+       return NULL;
+}
+
+static DBusMessage *message_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct message *m = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       message_append_properties(m, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *message_cancel(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct message *m = data;
+       int res;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (m->state != MESSAGE_STATE_PENDING)
+               return __ofono_error_not_available(msg);
+
+       res = __ofono_sms_txq_cancel(__ofono_atom_get_data(m->atom), &m->uuid);
+
+       switch (res) {
+       case -ENOENT:
+               return __ofono_error_not_found(msg);
+       case -EPERM:
+               return __ofono_error_access_denied(msg);
+       case 0:
+               return dbus_message_new_method_return(msg);
+       default:
+               return __ofono_error_failed(msg);
+       }
+}
+
+static GDBusMethodTable message_methods[] = {
+       { "GetProperties",  "",    "a{sv}",   message_get_properties },
+       { "Cancel",         "",    "",        message_cancel },
+       { }
+};
+
+static GDBusSignalTable message_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+struct message *message_create(const struct ofono_uuid *uuid,
+                                                       struct ofono_atom *atom)
+{
+       struct message *v;
+
+       v = g_try_new0(struct message, 1);
+       if (v == NULL)
+               return NULL;
+
+       memcpy(&v->uuid, uuid, sizeof(*uuid));
+
+       v->atom = atom;
+
+       return v;
+}
+
+static void message_destroy(gpointer userdata)
+{
+       struct message *m = userdata;
+
+       g_free(m);
+}
+
+gboolean message_dbus_register(struct message *m)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = message_path_from_uuid(m->atom, &m->uuid);
+
+       if (!g_dbus_register_interface(conn, path, OFONO_MESSAGE_INTERFACE,
+                                       message_methods, message_signals,
+                                       NULL, m, message_destroy)) {
+               ofono_error("Could not register Message %s", path);
+               message_destroy(m);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+void message_dbus_unregister(struct message *m)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = message_path_from_uuid(m->atom, &m->uuid);
+
+       g_dbus_unregister_interface(conn, path, OFONO_MESSAGE_INTERFACE);
+
+       return;
+}
+
+const struct ofono_uuid *message_get_uuid(const struct message *m)
+{
+       return &m->uuid;
+}
+
+void message_set_state(struct message *m, enum message_state new_state)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *state;
+
+       if (m->state == new_state)
+               return;
+
+       path = message_path_from_uuid(m->atom, &m->uuid);
+
+       m->state = new_state;
+       state = message_state_to_string(m->state);
+
+       ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_INTERFACE,
+                                                       "State",
+                                                       DBUS_TYPE_STRING,
+                                                       &state);
+}
+
+void message_append_properties(struct message *m, DBusMessageIter *dict)
+{
+       const char *state = message_state_to_string(m->state);
+
+       ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &state);
+}
+
+void message_emit_added(struct message *m, const char *interface)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *path;
+       const char *atompath = __ofono_atom_get_path(m->atom);
+
+       signal = dbus_message_new_signal(atompath, interface, "MessageAdded");
+       if (signal == NULL)
+               return;
+
+       path = message_path_from_uuid(m->atom, &m->uuid);
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       message_append_properties(m, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(ofono_dbus_get_connection(), signal);
+}
+
+void message_emit_removed(struct message *m, const char *interface)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *atompath = __ofono_atom_get_path(m->atom);
+       const char *path = message_path_from_uuid(m->atom, &m->uuid);
+
+       g_dbus_emit_signal(conn, atompath, interface, "MessageRemoved",
+                                                       DBUS_TYPE_OBJECT_PATH,
+                                                       &path,
+                                                       DBUS_TYPE_INVALID);
+}
+
+const char *message_path_from_uuid(struct ofono_atom *atom,
+                                               const struct ofono_uuid *uuid)
+{
+       static char path[256];
+       const char *atompath = __ofono_atom_get_path(atom);
+
+       snprintf(path, sizeof(path), "%s/message_%s", atompath,
+                                               ofono_uuid_to_str(uuid));
+
+       return path;
+}
+
+void *message_get_data(struct message *m)
+{
+       return m->data;
+}
+
+void message_set_data(struct message *m, void *data)
+{
+       m->data = data;
+}
diff --git a/src/message.h b/src/message.h
new file mode 100644 (file)
index 0000000..ad30798
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <ofono/types.h>
+
+enum message_state {
+       MESSAGE_STATE_PENDING,
+       MESSAGE_STATE_SENT,
+       MESSAGE_STATE_FAILED,
+       MESSAGE_STATE_CANCELLED,
+};
+
+struct ofono_atom;
+struct message;
+
+struct message *message_create(const struct ofono_uuid *uuid,
+                                               struct ofono_atom *atom);
+
+gboolean message_dbus_register(struct message *m);
+void message_dbus_unregister(struct message *m);
+
+const struct ofono_uuid *message_get_uuid(const struct message *m);
+
+void message_set_state(struct message *m, enum message_state new_state);
+
+void message_append_properties(struct message *m, DBusMessageIter *dict);
+
+void message_emit_added(struct message *m, const char *interface);
+
+void message_emit_removed(struct message *m, const char *interface);
+
+void *message_get_data(struct message *m);
+
+void message_set_data(struct message *m, void *data);
+
+const char *message_path_from_uuid(struct ofono_atom *atom,
+                                               const struct ofono_uuid *uuid);
diff --git a/src/modem.c b/src/modem.c
new file mode 100644 (file)
index 0000000..1a26991
--- /dev/null
@@ -0,0 +1,2219 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+
+static GSList *g_devinfo_drivers = NULL;
+static GSList *g_driver_list = NULL;
+static GSList *g_modem_list = NULL;
+
+static int next_modem_id = 0;
+static gboolean powering_down = FALSE;
+static int modems_remaining = 0;
+
+static struct ofono_watchlist *g_modemwatches = NULL;
+
+enum property_type {
+       PROPERTY_TYPE_INVALID = 0,
+       PROPERTY_TYPE_STRING,
+       PROPERTY_TYPE_INTEGER,
+       PROPERTY_TYPE_BOOLEAN,
+};
+
+enum modem_state {
+       MODEM_STATE_POWER_OFF,
+       MODEM_STATE_PRE_SIM,
+       MODEM_STATE_OFFLINE,
+       MODEM_STATE_ONLINE,
+};
+
+struct ofono_modem {
+       char                    *path;
+       enum modem_state        modem_state;
+       GSList                  *atoms;
+       struct ofono_watchlist  *atom_watches;
+       GSList                  *interface_list;
+       GSList                  *feature_list;
+       unsigned int            call_ids;
+       DBusMessage             *pending;
+       guint                   interface_update;
+       ofono_bool_t            powered;
+       ofono_bool_t            powered_pending;
+       ofono_bool_t            get_online;
+       ofono_bool_t            lockdown;
+       char                    *lock_owner;
+       guint                   lock_watch;
+       guint                   timeout;
+       ofono_bool_t            online;
+       struct ofono_watchlist  *online_watches;
+       struct ofono_watchlist  *powered_watches;
+       guint                   emergency;
+       GHashTable              *properties;
+       struct ofono_sim        *sim;
+       unsigned int            sim_watch;
+       unsigned int            sim_ready_watch;
+       const struct ofono_modem_driver *driver;
+       void                    *driver_data;
+       char                    *driver_type;
+       char                    *name;
+};
+
+struct ofono_devinfo {
+       char *manufacturer;
+       char *model;
+       char *revision;
+       char *serial;
+       unsigned int dun_watch;
+       const struct ofono_devinfo_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+struct ofono_atom {
+       enum ofono_atom_type type;
+       enum modem_state modem_state;
+       void (*destruct)(struct ofono_atom *atom);
+       void (*unregister)(struct ofono_atom *atom);
+       void *data;
+       struct ofono_modem *modem;
+};
+
+struct atom_watch {
+       struct ofono_watchlist_item item;
+       enum ofono_atom_type type;
+};
+
+struct modem_property {
+       enum property_type type;
+       void *value;
+};
+
+static const char *modem_type_to_string(enum ofono_modem_type type)
+{
+       switch (type) {
+       case OFONO_MODEM_TYPE_HARDWARE:
+               return "hardware";
+       case OFONO_MODEM_TYPE_HFP:
+               return "hfp";
+       case OFONO_MODEM_TYPE_SAP:
+               return "sap";
+       }
+
+       return "unknown";
+}
+
+unsigned int __ofono_modem_callid_next(struct ofono_modem *modem)
+{
+       unsigned int i;
+
+       for (i = 1; i < sizeof(modem->call_ids) * 8; i++) {
+               if (modem->call_ids & (1 << i))
+                       continue;
+
+               return i;
+       }
+
+       return 0;
+}
+
+void __ofono_modem_callid_hold(struct ofono_modem *modem, int id)
+{
+       modem->call_ids |= (1 << id);
+}
+
+void __ofono_modem_callid_release(struct ofono_modem *modem, int id)
+{
+       modem->call_ids &= ~(1 << id);
+}
+
+void ofono_modem_set_data(struct ofono_modem *modem, void *data)
+{
+       if (modem == NULL)
+               return;
+
+       modem->driver_data = data;
+}
+
+void *ofono_modem_get_data(struct ofono_modem *modem)
+{
+       if (modem == NULL)
+               return NULL;
+
+       return modem->driver_data;
+}
+
+const char *ofono_modem_get_path(struct ofono_modem *modem)
+{
+       if (modem)
+               return modem->path;
+
+       return NULL;
+}
+
+struct ofono_atom *__ofono_modem_add_atom(struct ofono_modem *modem,
+                                       enum ofono_atom_type type,
+                                       void (*destruct)(struct ofono_atom *),
+                                       void *data)
+{
+       struct ofono_atom *atom;
+
+       if (modem == NULL)
+               return NULL;
+
+       atom = g_new0(struct ofono_atom, 1);
+
+       atom->type = type;
+       atom->modem_state = modem->modem_state;
+       atom->destruct = destruct;
+       atom->data = data;
+       atom->modem = modem;
+
+       modem->atoms = g_slist_prepend(modem->atoms, atom);
+
+       return atom;
+}
+
+struct ofono_atom *__ofono_modem_add_atom_offline(struct ofono_modem *modem,
+                                       enum ofono_atom_type type,
+                                       void (*destruct)(struct ofono_atom *),
+                                       void *data)
+{
+       struct ofono_atom *atom;
+
+       atom = __ofono_modem_add_atom(modem, type, destruct, data);
+
+       atom->modem_state = MODEM_STATE_OFFLINE;
+
+       return atom;
+}
+
+void *__ofono_atom_get_data(struct ofono_atom *atom)
+{
+       return atom->data;
+}
+
+const char *__ofono_atom_get_path(struct ofono_atom *atom)
+{
+       return atom->modem->path;
+}
+
+struct ofono_modem *__ofono_atom_get_modem(struct ofono_atom *atom)
+{
+       return atom->modem;
+}
+
+static void call_watches(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond)
+{
+       struct ofono_modem *modem = atom->modem;
+       GSList *atom_watches = modem->atom_watches->items;
+       GSList *l;
+       struct atom_watch *watch;
+       ofono_atom_watch_func notify;
+
+       for (l = atom_watches; l; l = l->next) {
+               watch = l->data;
+
+               if (watch->type != atom->type)
+                       continue;
+
+               notify = watch->item.notify;
+               notify(atom, cond, watch->item.notify_data);
+       }
+}
+
+void __ofono_atom_register(struct ofono_atom *atom,
+                       void (*unregister)(struct ofono_atom *))
+{
+       if (unregister == NULL)
+               return;
+
+       atom->unregister = unregister;
+
+       call_watches(atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED);
+}
+
+void __ofono_atom_unregister(struct ofono_atom *atom)
+{
+       if (atom->unregister == NULL)
+               return;
+
+       call_watches(atom, OFONO_ATOM_WATCH_CONDITION_UNREGISTERED);
+
+       atom->unregister(atom);
+       atom->unregister = NULL;
+}
+
+gboolean __ofono_atom_get_registered(struct ofono_atom *atom)
+{
+       return atom->unregister ? TRUE : FALSE;
+}
+
+unsigned int __ofono_modem_add_atom_watch(struct ofono_modem *modem,
+                                       enum ofono_atom_type type,
+                                       ofono_atom_watch_func notify,
+                                       void *data, ofono_destroy_func destroy)
+{
+       struct atom_watch *watch;
+       unsigned int id;
+       GSList *l;
+       struct ofono_atom *atom;
+
+       if (notify == NULL)
+               return 0;
+
+       watch = g_new0(struct atom_watch, 1);
+
+       watch->type = type;
+       watch->item.notify = notify;
+       watch->item.destroy = destroy;
+       watch->item.notify_data = data;
+
+       id = __ofono_watchlist_add_item(modem->atom_watches,
+                                       (struct ofono_watchlist_item *)watch);
+
+       for (l = modem->atoms; l; l = l->next) {
+               atom = l->data;
+
+               if (atom->type != type || atom->unregister == NULL)
+                       continue;
+
+               notify(atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED, data);
+       }
+
+       return id;
+}
+
+gboolean __ofono_modem_remove_atom_watch(struct ofono_modem *modem,
+                                               unsigned int id)
+{
+       return __ofono_watchlist_remove_item(modem->atom_watches, id);
+}
+
+struct ofono_atom *__ofono_modem_find_atom(struct ofono_modem *modem,
+                                               enum ofono_atom_type type)
+{
+       GSList *l;
+       struct ofono_atom *atom;
+
+       if (modem == NULL)
+               return NULL;
+
+       for (l = modem->atoms; l; l = l->next) {
+               atom = l->data;
+
+               if (atom->type == type && atom->unregister != NULL)
+                       return atom;
+       }
+
+       return NULL;
+}
+
+void __ofono_modem_foreach_atom(struct ofono_modem *modem,
+                               enum ofono_atom_type type,
+                               ofono_atom_func callback, void *data)
+{
+       GSList *l;
+       struct ofono_atom *atom;
+
+       if (modem == NULL)
+               return;
+
+       for (l = modem->atoms; l; l = l->next) {
+               atom = l->data;
+
+               if (atom->type != type)
+                       continue;
+
+               callback(atom, data);
+       }
+}
+
+void __ofono_modem_foreach_registered_atom(struct ofono_modem *modem,
+                                               enum ofono_atom_type type,
+                                               ofono_atom_func callback,
+                                               void *data)
+{
+       GSList *l;
+       struct ofono_atom *atom;
+
+       if (modem == NULL)
+               return;
+
+       for (l = modem->atoms; l; l = l->next) {
+               atom = l->data;
+
+               if (atom->type != type)
+                       continue;
+
+               if (atom->unregister == NULL)
+                       continue;
+
+               callback(atom, data);
+       }
+}
+
+void __ofono_atom_free(struct ofono_atom *atom)
+{
+       struct ofono_modem *modem = atom->modem;
+
+       modem->atoms = g_slist_remove(modem->atoms, atom);
+
+       __ofono_atom_unregister(atom);
+
+       if (atom->destruct)
+               atom->destruct(atom);
+
+       g_free(atom);
+}
+
+static void flush_atoms(struct ofono_modem *modem, enum modem_state new_state)
+{
+       GSList *cur;
+       GSList *prev;
+       GSList *tmp;
+
+       DBG("");
+
+       prev = NULL;
+       cur = modem->atoms;
+
+       while (cur) {
+               struct ofono_atom *atom = cur->data;
+
+               if (atom->modem_state <= new_state) {
+                       prev = cur;
+                       cur = cur->next;
+                       continue;
+               }
+
+               __ofono_atom_unregister(atom);
+
+               if (atom->destruct)
+                       atom->destruct(atom);
+
+               g_free(atom);
+
+               if (prev)
+                       prev->next = cur->next;
+               else
+                       modem->atoms = cur->next;
+
+               tmp = cur;
+               cur = cur->next;
+               g_slist_free_1(tmp);
+       }
+}
+
+static void notify_online_watches(struct ofono_modem *modem)
+{
+       struct ofono_watchlist_item *item;
+       GSList *l;
+       ofono_modem_online_notify_func notify;
+
+       if (modem->online_watches == NULL)
+               return;
+
+       for (l = modem->online_watches->items; l; l = l->next) {
+               item = l->data;
+               notify = item->notify;
+               notify(modem, modem->online, item->notify_data);
+       }
+}
+
+static void notify_powered_watches(struct ofono_modem *modem)
+{
+       struct ofono_watchlist_item *item;
+       GSList *l;
+       ofono_modem_powered_notify_func notify;
+
+       if (modem->powered_watches == NULL)
+               return;
+
+       for (l = modem->powered_watches->items; l; l = l->next) {
+               item = l->data;
+               notify = item->notify;
+               notify(modem, modem->powered, item->notify_data);
+       }
+}
+
+static void set_online(struct ofono_modem *modem, ofono_bool_t new_online)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (new_online == modem->online)
+               return;
+
+       modem->online = new_online;
+
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Online", DBUS_TYPE_BOOLEAN,
+                                               &modem->online);
+
+       notify_online_watches(modem);
+}
+
+static void modem_change_state(struct ofono_modem *modem,
+                               enum modem_state new_state)
+{
+       struct ofono_modem_driver const *driver = modem->driver;
+       enum modem_state old_state = modem->modem_state;
+
+       DBG("old state: %d, new state: %d", old_state, new_state);
+
+       if (old_state == new_state)
+               return;
+
+       modem->modem_state = new_state;
+
+       if (old_state > new_state)
+               flush_atoms(modem, new_state);
+
+       switch (new_state) {
+       case MODEM_STATE_POWER_OFF:
+               modem->call_ids = 0;
+               break;
+
+       case MODEM_STATE_PRE_SIM:
+               if (old_state < MODEM_STATE_PRE_SIM && driver->pre_sim)
+                       driver->pre_sim(modem);
+               break;
+
+       case MODEM_STATE_OFFLINE:
+               if (old_state < MODEM_STATE_OFFLINE) {
+                       if (driver->post_sim)
+                               driver->post_sim(modem);
+
+                       __ofono_history_probe_drivers(modem);
+                       __ofono_nettime_probe_drivers(modem);
+               }
+
+               break;
+
+       case MODEM_STATE_ONLINE:
+               if (driver->post_online)
+                       driver->post_online(modem);
+
+               break;
+       }
+}
+
+unsigned int __ofono_modem_add_online_watch(struct ofono_modem *modem,
+                                       ofono_modem_online_notify_func notify,
+                                       void *data, ofono_destroy_func destroy)
+{
+       struct ofono_watchlist_item *item;
+
+       if (modem == NULL || notify == NULL)
+               return 0;
+
+       item = g_new0(struct ofono_watchlist_item, 1);
+
+       item->notify = notify;
+       item->destroy = destroy;
+       item->notify_data = data;
+
+       return __ofono_watchlist_add_item(modem->online_watches, item);
+}
+
+void __ofono_modem_remove_online_watch(struct ofono_modem *modem,
+                                       unsigned int id)
+{
+       __ofono_watchlist_remove_item(modem->online_watches, id);
+}
+
+unsigned int __ofono_modem_add_powered_watch(struct ofono_modem *modem,
+                                       ofono_modem_powered_notify_func notify,
+                                       void *data, ofono_destroy_func destroy)
+{
+       struct ofono_watchlist_item *item;
+
+       if (modem == NULL || notify == NULL)
+               return 0;
+
+       item = g_new0(struct ofono_watchlist_item, 1);
+
+       item->notify = notify;
+       item->destroy = destroy;
+       item->notify_data = data;
+
+       return __ofono_watchlist_add_item(modem->powered_watches, item);
+}
+
+void __ofono_modem_remove_powered_watch(struct ofono_modem *modem,
+                                       unsigned int id)
+{
+       __ofono_watchlist_remove_item(modem->powered_watches, id);
+}
+
+static gboolean modem_has_sim(struct ofono_modem *modem)
+{
+       GSList *l;
+       struct ofono_atom *atom;
+
+       for (l = modem->atoms; l; l = l->next) {
+               atom = l->data;
+
+               if (atom->type == OFONO_ATOM_TYPE_SIM)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+static void common_online_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_modem *modem = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               return;
+
+       /*
+        * If we need to get online after a silent reset this callback
+        * is called.  The callback should not consider the pending dbus
+        * message.
+        *
+        * Additionally, this process can be interrupted by the following
+        * events:
+        *      - Sim being removed or reset
+        *      - SetProperty(Powered, False) being called
+        *      - SetProperty(Lockdown, True) being called
+        *
+        * We should not set the modem to the online state in these cases.
+        */
+       switch (modem->modem_state) {
+       case MODEM_STATE_OFFLINE:
+               set_online(modem, TRUE);
+
+               /* Will this increase emergency call setup time??? */
+               modem_change_state(modem, MODEM_STATE_ONLINE);
+               break;
+       case MODEM_STATE_POWER_OFF:
+               /* The powered operation is pending */
+               break;
+       case MODEM_STATE_PRE_SIM:
+               /*
+                * Its valid to be in online even without a SIM/SIM being
+                * PIN locked. e.g.: Emergency mode
+                */
+               DBG("Online in PRE SIM state");
+
+               set_online(modem, TRUE);
+               break;
+       case MODEM_STATE_ONLINE:
+               ofono_error("Online called when the modem is already online!");
+               break;
+       };
+}
+
+static void online_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_modem *modem = data;
+       DBusMessage *reply;
+
+       if (!modem->pending)
+               goto out;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               reply = dbus_message_new_method_return(modem->pending);
+       else
+               reply = __ofono_error_failed(modem->pending);
+
+       __ofono_dbus_pending_reply(&modem->pending, reply);
+
+out:
+       common_online_cb(error, data);
+}
+
+static void offline_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_modem *modem = data;
+       DBusMessage *reply;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               reply = dbus_message_new_method_return(modem->pending);
+       else
+               reply = __ofono_error_failed(modem->pending);
+
+       __ofono_dbus_pending_reply(&modem->pending, reply);
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+               switch (modem->modem_state) {
+               case MODEM_STATE_PRE_SIM:
+                       set_online(modem, FALSE);
+                       break;
+               case MODEM_STATE_ONLINE:
+                       set_online(modem, FALSE);
+                       modem_change_state(modem, MODEM_STATE_OFFLINE);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void sim_state_watch(enum ofono_sim_state new_state, void *user)
+{
+       struct ofono_modem *modem = user;
+
+       switch (new_state) {
+       case OFONO_SIM_STATE_NOT_PRESENT:
+               modem_change_state(modem, MODEM_STATE_PRE_SIM);
+       case OFONO_SIM_STATE_INSERTED:
+               break;
+       case OFONO_SIM_STATE_LOCKED_OUT:
+               modem_change_state(modem, MODEM_STATE_PRE_SIM);
+               break;
+       case OFONO_SIM_STATE_READY:
+               modem_change_state(modem, MODEM_STATE_OFFLINE);
+
+               /*
+                * If we don't have the set_online method, also proceed
+                * straight to the online state
+                */
+               if (modem->driver->set_online == NULL)
+                       set_online(modem, TRUE);
+
+               if (modem->online == TRUE)
+                       modem_change_state(modem, MODEM_STATE_ONLINE);
+               else if (modem->get_online)
+                       modem->driver->set_online(modem, 1, common_online_cb,
+                                                       modem);
+
+               modem->get_online = FALSE;
+
+               break;
+       }
+}
+
+static DBusMessage *set_property_online(struct ofono_modem *modem,
+                                       DBusMessage *msg,
+                                       DBusMessageIter *var)
+{
+       ofono_bool_t online;
+       const struct ofono_modem_driver *driver = modem->driver;
+
+       if (modem->powered == FALSE)
+               return __ofono_error_not_available(msg);
+
+       if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(var, &online);
+
+       if (modem->pending != NULL)
+               return __ofono_error_busy(msg);
+
+       if (modem->online == online)
+               return dbus_message_new_method_return(msg);
+
+       if (ofono_modem_get_emergency_mode(modem) == TRUE)
+               return __ofono_error_emergency_active(msg);
+
+       if (driver->set_online == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       modem->pending = dbus_message_ref(msg);
+
+       driver->set_online(modem, online,
+                               online ? online_cb : offline_cb, modem);
+
+       return NULL;
+}
+
+ofono_bool_t ofono_modem_get_online(struct ofono_modem *modem)
+{
+       if (modem == NULL)
+               return FALSE;
+
+       return modem->online;
+}
+
+void __ofono_modem_append_properties(struct ofono_modem *modem,
+                                               DBusMessageIter *dict)
+{
+       char **interfaces;
+       char **features;
+       int i;
+       GSList *l;
+       struct ofono_devinfo *info;
+       dbus_bool_t emergency = ofono_modem_get_emergency_mode(modem);
+       const char *strtype;
+
+       ofono_dbus_dict_append(dict, "Online", DBUS_TYPE_BOOLEAN,
+                               &modem->online);
+
+       ofono_dbus_dict_append(dict, "Powered", DBUS_TYPE_BOOLEAN,
+                               &modem->powered);
+
+       ofono_dbus_dict_append(dict, "Lockdown", DBUS_TYPE_BOOLEAN,
+                               &modem->lockdown);
+
+       ofono_dbus_dict_append(dict, "Emergency", DBUS_TYPE_BOOLEAN,
+                               &emergency);
+
+       info = __ofono_atom_find(OFONO_ATOM_TYPE_DEVINFO, modem);
+       if (info) {
+               if (info->manufacturer)
+                       ofono_dbus_dict_append(dict, "Manufacturer",
+                                               DBUS_TYPE_STRING,
+                                               &info->manufacturer);
+
+               if (info->model)
+                       ofono_dbus_dict_append(dict, "Model", DBUS_TYPE_STRING,
+                                               &info->model);
+
+               if (info->revision)
+                       ofono_dbus_dict_append(dict, "Revision",
+                                               DBUS_TYPE_STRING,
+                                               &info->revision);
+
+               if (info->serial)
+                       ofono_dbus_dict_append(dict, "Serial",
+                                               DBUS_TYPE_STRING,
+                                               &info->serial);
+       }
+
+       interfaces = g_new0(char *, g_slist_length(modem->interface_list) + 1);
+       for (i = 0, l = modem->interface_list; l; l = l->next, i++)
+               interfaces[i] = l->data;
+       ofono_dbus_dict_append_array(dict, "Interfaces", DBUS_TYPE_STRING,
+                                       &interfaces);
+       g_free(interfaces);
+
+       features = g_new0(char *, g_slist_length(modem->feature_list) + 1);
+       for (i = 0, l = modem->feature_list; l; l = l->next, i++)
+               features[i] = l->data;
+       ofono_dbus_dict_append_array(dict, "Features", DBUS_TYPE_STRING,
+                                       &features);
+       g_free(features);
+
+       if (modem->name)
+               ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING,
+                                       &modem->name);
+
+       strtype = modem_type_to_string(modem->driver->modem_type);
+       ofono_dbus_dict_append(dict, "Type", DBUS_TYPE_STRING, &strtype);
+}
+
+static DBusMessage *modem_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       __ofono_modem_append_properties(modem, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static int set_powered(struct ofono_modem *modem, ofono_bool_t powered)
+{
+       const struct ofono_modem_driver *driver = modem->driver;
+       int err = -EINVAL;
+
+       if (modem->powered_pending == powered)
+               return -EALREADY;
+
+       /* Remove the atoms even if the driver is no longer available */
+       if (powered == FALSE)
+               modem_change_state(modem, MODEM_STATE_POWER_OFF);
+
+       modem->powered_pending = powered;
+
+       if (driver == NULL)
+               return -EINVAL;
+
+       if (powered == TRUE) {
+               if (driver->enable)
+                       err = driver->enable(modem);
+       } else {
+               if (driver->disable)
+                       err = driver->disable(modem);
+       }
+
+       if (err == 0) {
+               modem->powered = powered;
+               notify_powered_watches(modem);
+       } else if (err != -EINPROGRESS)
+               modem->powered_pending = modem->powered;
+
+       return err;
+}
+
+static void lockdown_remove(struct ofono_modem *modem)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (modem->lock_watch) {
+               g_dbus_remove_watch(conn, modem->lock_watch);
+               modem->lock_watch = 0;
+       }
+
+       g_free(modem->lock_owner);
+       modem->lock_owner = NULL;
+
+       modem->lockdown = FALSE;
+}
+
+static gboolean set_powered_timeout(gpointer user)
+{
+       struct ofono_modem *modem = user;
+
+       DBG("modem: %p", modem);
+
+       modem->timeout = 0;
+
+       if (modem->powered_pending == FALSE) {
+               DBusConnection *conn = ofono_dbus_get_connection();
+               dbus_bool_t powered = FALSE;
+
+               set_online(modem, FALSE);
+
+               modem->powered = FALSE;
+               notify_powered_watches(modem);
+
+               ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Powered", DBUS_TYPE_BOOLEAN,
+                                               &powered);
+       } else {
+               modem->powered_pending = modem->powered;
+       }
+
+       if (modem->pending != NULL) {
+               DBusMessage *reply;
+
+               reply = __ofono_error_timed_out(modem->pending);
+               __ofono_dbus_pending_reply(&modem->pending, reply);
+
+               if (modem->lockdown)
+                       lockdown_remove(modem);
+       }
+
+       return FALSE;
+}
+
+static void lockdown_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct ofono_modem *modem = user_data;
+
+       DBG("");
+
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                       OFONO_MODEM_INTERFACE,
+                                       "Lockdown", DBUS_TYPE_BOOLEAN,
+                                       &modem->lockdown);
+
+       modem->lock_watch = 0;
+       lockdown_remove(modem);
+}
+
+static DBusMessage *set_property_lockdown(struct ofono_modem *modem,
+                                       DBusMessage *msg,
+                                       DBusMessageIter *var)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       ofono_bool_t lockdown;
+       dbus_bool_t powered;
+       const char *caller;
+       int err;
+
+       if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(var, &lockdown);
+
+       if (modem->pending != NULL)
+               return __ofono_error_busy(msg);
+
+       caller = dbus_message_get_sender(msg);
+
+       if (modem->lockdown && g_strcmp0(caller, modem->lock_owner))
+               return __ofono_error_access_denied(msg);
+
+       if (modem->lockdown == lockdown)
+               return dbus_message_new_method_return(msg);
+
+       if (lockdown == FALSE) {
+               lockdown_remove(modem);
+               goto done;
+       }
+
+       if (ofono_modem_get_emergency_mode(modem) == TRUE)
+               return __ofono_error_emergency_active(msg);
+
+       modem->lock_owner = g_strdup(caller);
+
+       modem->lock_watch = g_dbus_add_disconnect_watch(conn,
+                               modem->lock_owner, lockdown_disconnect,
+                               modem, NULL);
+
+       if (modem->lock_watch == 0) {
+               g_free(modem->lock_owner);
+               modem->lock_owner = NULL;
+
+               return __ofono_error_failed(msg);
+       }
+
+       modem->lockdown = lockdown;
+
+       if (modem->powered == FALSE)
+               goto done;
+
+       err = set_powered(modem, FALSE);
+       if (err < 0) {
+               if (err != -EINPROGRESS) {
+                       lockdown_remove(modem);
+                       return __ofono_error_failed(msg);
+               }
+
+               modem->pending = dbus_message_ref(msg);
+               modem->timeout = g_timeout_add_seconds(20,
+                                               set_powered_timeout, modem);
+               return NULL;
+       }
+
+       set_online(modem, FALSE);
+
+       powered = FALSE;
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                       OFONO_MODEM_INTERFACE,
+                                       "Powered", DBUS_TYPE_BOOLEAN,
+                                       &powered);
+
+done:
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                       OFONO_MODEM_INTERFACE,
+                                       "Lockdown", DBUS_TYPE_BOOLEAN,
+                                       &lockdown);
+
+       return NULL;
+}
+
+static DBusMessage *modem_set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_modem *modem = data;
+       DBusMessageIter iter, var;
+       const char *name;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       if (powering_down == TRUE)
+               return __ofono_error_failed(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (g_str_equal(name, "Online"))
+               return set_property_online(modem, msg, &var);
+
+       if (g_str_equal(name, "Powered") == TRUE) {
+               ofono_bool_t powered;
+               int err;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &powered);
+
+               if (modem->pending != NULL)
+                       return __ofono_error_busy(msg);
+
+               if (modem->powered == powered)
+                       return dbus_message_new_method_return(msg);
+
+               if (ofono_modem_get_emergency_mode(modem) == TRUE)
+                       return __ofono_error_emergency_active(msg);
+
+               if (modem->lockdown)
+                       return __ofono_error_access_denied(msg);
+
+               err = set_powered(modem, powered);
+               if (err < 0) {
+                       if (err != -EINPROGRESS)
+                               return __ofono_error_failed(msg);
+
+                       modem->pending = dbus_message_ref(msg);
+                       modem->timeout = g_timeout_add_seconds(20,
+                                               set_powered_timeout, modem);
+                       return NULL;
+               }
+
+               g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+               ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Powered", DBUS_TYPE_BOOLEAN,
+                                               &powered);
+
+               if (powered) {
+                       modem_change_state(modem, MODEM_STATE_PRE_SIM);
+
+                       /* Force SIM Ready for devies with no sim atom */
+                       if (modem_has_sim(modem) == FALSE)
+                               sim_state_watch(OFONO_SIM_STATE_READY, modem);
+               } else {
+                       set_online(modem, FALSE);
+                       modem_change_state(modem, MODEM_STATE_POWER_OFF);
+               }
+
+               return NULL;
+       }
+
+       if (g_str_equal(name, "Lockdown"))
+               return set_property_lockdown(modem, msg, &var);
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable modem_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        modem_get_properties },
+       { "SetProperty",        "sv",   "",             modem_set_property,
+                                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable modem_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t dbus_powered = powered;
+
+       if (modem->timeout > 0) {
+               g_source_remove(modem->timeout);
+               modem->timeout = 0;
+       }
+
+       if (modem->powered_pending != modem->powered &&
+                                               modem->pending != NULL) {
+               DBusMessage *reply;
+
+               if (powered == modem->powered_pending)
+                       reply = dbus_message_new_method_return(modem->pending);
+               else
+                       reply = __ofono_error_failed(modem->pending);
+
+               __ofono_dbus_pending_reply(&modem->pending, reply);
+       }
+
+       modem->powered_pending = powered;
+
+       if (modem->powered == powered)
+               goto out;
+
+       modem->powered = powered;
+       notify_powered_watches(modem);
+
+       if (modem->lockdown)
+               ofono_dbus_signal_property_changed(conn, modem->path,
+                                       OFONO_MODEM_INTERFACE,
+                                       "Lockdown", DBUS_TYPE_BOOLEAN,
+                                       &modem->lockdown);
+
+       if (modem->driver == NULL) {
+               ofono_error("Calling ofono_modem_set_powered on a"
+                               "modem with no driver is not valid, "
+                               "please fix the modem driver.");
+               return;
+       }
+
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                       OFONO_MODEM_INTERFACE,
+                                       "Powered", DBUS_TYPE_BOOLEAN,
+                                       &dbus_powered);
+
+       if (powered) {
+               modem_change_state(modem, MODEM_STATE_PRE_SIM);
+
+               /* Force SIM Ready for devices with no sim atom */
+               if (modem_has_sim(modem) == FALSE)
+                       sim_state_watch(OFONO_SIM_STATE_READY, modem);
+       } else {
+               set_online(modem, FALSE);
+
+               modem_change_state(modem, MODEM_STATE_POWER_OFF);
+       }
+
+out:
+       if (powering_down && powered == FALSE) {
+               modems_remaining -= 1;
+
+               if (modems_remaining == 0)
+                       __ofono_exit();
+       }
+}
+
+ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem)
+{
+       if (modem == NULL)
+               return FALSE;
+
+       return modem->powered;
+}
+
+static gboolean trigger_interface_update(void *data)
+{
+       struct ofono_modem *modem = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       char **interfaces;
+       char **features;
+       GSList *l;
+       int i;
+
+       interfaces = g_new0(char *, g_slist_length(modem->interface_list) + 1);
+       for (i = 0, l = modem->interface_list; l; l = l->next, i++)
+               interfaces[i] = l->data;
+       ofono_dbus_signal_array_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Interfaces", DBUS_TYPE_STRING,
+                                               &interfaces);
+       g_free(interfaces);
+
+       features = g_new0(char *, g_slist_length(modem->feature_list) + 1);
+       for (i = 0, l = modem->feature_list; l; l = l->next, i++)
+               features[i] = l->data;
+       ofono_dbus_signal_array_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Features", DBUS_TYPE_STRING,
+                                               &features);
+       g_free(features);
+
+       modem->interface_update = 0;
+
+       return FALSE;
+}
+
+static const struct {
+       const char *interface;
+       const char *feature;
+} feature_map[] = {
+       { OFONO_NETWORK_REGISTRATION_INTERFACE,         "net"   },
+       { OFONO_RADIO_SETTINGS_INTERFACE,               "rat"   },
+       { OFONO_CELL_BROADCAST_INTERFACE,               "cbs"   },
+       { OFONO_MESSAGE_MANAGER_INTERFACE,              "sms"   },
+       { OFONO_SIM_MANAGER_INTERFACE,                  "sim"   },
+       { OFONO_STK_INTERFACE,                          "stk"   },
+       { OFONO_SUPPLEMENTARY_SERVICES_INTERFACE,       "ussd"  },
+       { OFONO_CONNECTION_MANAGER_INTERFACE,           "gprs"  },
+       { OFONO_TEXT_TELEPHONY_INTERFACE,               "tty"   },
+       { OFONO_LOCATION_REPORTING_INTERFACE,           "gps"   },
+       { },
+};
+
+static const char *get_feature(const char *interface)
+{
+       int i;
+
+       for (i = 0; feature_map[i].interface; i++) {
+               if (strcmp(feature_map[i].interface, interface) == 0)
+                       return feature_map[i].feature;
+       }
+
+       return NULL;
+}
+
+void ofono_modem_add_interface(struct ofono_modem *modem,
+                               const char *interface)
+{
+       const char *feature;
+
+       modem->interface_list = g_slist_prepend(modem->interface_list,
+                                               g_strdup(interface));
+
+       feature = get_feature(interface);
+       if (feature)
+               modem->feature_list = g_slist_prepend(modem->feature_list,
+                                                       g_strdup(feature));
+
+       if (modem->interface_update != 0)
+               return;
+
+       modem->interface_update = g_idle_add(trigger_interface_update, modem);
+}
+
+void ofono_modem_remove_interface(struct ofono_modem *modem,
+                               const char *interface)
+{
+       GSList *found;
+       const char *feature;
+
+       found = g_slist_find_custom(modem->interface_list, interface,
+                                               (GCompareFunc) strcmp);
+       if (found == NULL) {
+               ofono_error("Interface %s not found on the interface_list",
+                               interface);
+               return;
+       }
+
+       g_free(found->data);
+       modem->interface_list = g_slist_remove(modem->interface_list,
+                                               found->data);
+
+       feature = get_feature(interface);
+       if (feature) {
+               found = g_slist_find_custom(modem->feature_list, feature,
+                                               (GCompareFunc) strcmp);
+               if (found) {
+                       g_free(found->data);
+                       modem->feature_list =
+                               g_slist_remove(modem->feature_list,
+                                               found->data);
+               }
+       }
+
+       if (modem->interface_update != 0)
+               return;
+
+       modem->interface_update = g_idle_add(trigger_interface_update, modem);
+}
+
+static void query_serial_cb(const struct ofono_error *error,
+                               const char *serial, void *user)
+{
+       struct ofono_devinfo *info = user;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(info->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               return;
+
+       info->serial = g_strdup(serial);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Serial", DBUS_TYPE_STRING,
+                                               &info->serial);
+}
+
+static void query_serial(struct ofono_devinfo *info)
+{
+       if (info->driver->query_serial == NULL)
+               return;
+
+       info->driver->query_serial(info, query_serial_cb, info);
+}
+
+static void query_revision_cb(const struct ofono_error *error,
+                               const char *revision, void *user)
+{
+       struct ofono_devinfo *info = user;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(info->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               goto out;
+
+       info->revision = g_strdup(revision);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Revision", DBUS_TYPE_STRING,
+                                               &info->revision);
+
+out:
+       query_serial(info);
+}
+
+static void query_revision(struct ofono_devinfo *info)
+{
+       if (info->driver->query_revision == NULL) {
+               query_serial(info);
+               return;
+       }
+
+       info->driver->query_revision(info, query_revision_cb, info);
+}
+
+static void query_model_cb(const struct ofono_error *error,
+                               const char *model, void *user)
+{
+       struct ofono_devinfo *info = user;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(info->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               goto out;
+
+       info->model = g_strdup(model);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Model", DBUS_TYPE_STRING,
+                                               &info->model);
+
+out:
+       query_revision(info);
+}
+
+static void query_model(struct ofono_devinfo *info)
+{
+       if (info->driver->query_model == NULL) {
+               /* If model is not supported, don't bother querying revision */
+               query_serial(info);
+               return;
+       }
+
+       info->driver->query_model(info, query_model_cb, info);
+}
+
+static void query_manufacturer_cb(const struct ofono_error *error,
+                                       const char *manufacturer, void *user)
+{
+       struct ofono_devinfo *info = user;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(info->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               goto out;
+
+       info->manufacturer = g_strdup(manufacturer);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Manufacturer",
+                                               DBUS_TYPE_STRING,
+                                               &info->manufacturer);
+
+out:
+       query_model(info);
+}
+
+static gboolean query_manufacturer(gpointer user)
+{
+       struct ofono_devinfo *info = user;
+
+       if (info->driver->query_manufacturer == NULL) {
+               query_model(info);
+               return FALSE;
+       }
+
+       info->driver->query_manufacturer(info, query_manufacturer_cb, info);
+
+       return FALSE;
+}
+
+static void attr_template(struct ofono_emulator *em,
+                               struct ofono_emulator_request *req,
+                               const char *attr)
+{
+       struct ofono_error result;
+
+       if (attr == NULL)
+               attr = "Unknown";
+
+       result.error = 0;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
+               ofono_emulator_send_info(em, attr, TRUE);
+               result.type = OFONO_ERROR_TYPE_NO_ERROR;
+               ofono_emulator_send_final(em, &result);
+               break;
+       case OFONO_EMULATOR_REQUEST_TYPE_SUPPORT:
+               result.type = OFONO_ERROR_TYPE_NO_ERROR;
+               ofono_emulator_send_final(em, &result);
+               break;
+       default:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               ofono_emulator_send_final(em, &result);
+       };
+}
+
+static void gmi_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_devinfo *info = userdata;
+
+       attr_template(em, req, info->manufacturer);
+}
+
+static void gmm_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_devinfo *info = userdata;
+
+       attr_template(em, req, info->model);
+}
+
+static void gmr_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_devinfo *info = userdata;
+
+       attr_template(em, req, info->revision);
+}
+
+static void gcap_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       attr_template(em, req, "+GCAP: +CGSM");
+}
+
+static void dun_watch(struct ofono_atom *atom,
+                       enum ofono_atom_watch_condition cond, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED)
+               return;
+
+       ofono_emulator_add_handler(em, "+GMI", gmi_cb, data, NULL);
+       ofono_emulator_add_handler(em, "+GMM", gmm_cb, data, NULL);
+       ofono_emulator_add_handler(em, "+GMR", gmr_cb, data, NULL);
+       ofono_emulator_add_handler(em, "+GCAP", gcap_cb, data, NULL);
+}
+
+int ofono_devinfo_driver_register(const struct ofono_devinfo_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_devinfo_drivers = g_slist_prepend(g_devinfo_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_devinfo_driver_unregister(const struct ofono_devinfo_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_devinfo_drivers = g_slist_remove(g_devinfo_drivers, (void *) d);
+}
+
+static void devinfo_remove(struct ofono_atom *atom)
+{
+       struct ofono_devinfo *info = __ofono_atom_get_data(atom);
+       DBG("atom: %p", atom);
+
+       if (info == NULL)
+               return;
+
+       if (info->driver == NULL)
+               return;
+
+       if (info->driver->remove)
+               info->driver->remove(info);
+
+       g_free(info);
+}
+
+struct ofono_devinfo *ofono_devinfo_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data)
+{
+       struct ofono_devinfo *info;
+       GSList *l;
+
+       info = g_new0(struct ofono_devinfo, 1);
+
+       info->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_DEVINFO,
+                                               devinfo_remove, info);
+
+       for (l = g_devinfo_drivers; l; l = l->next) {
+               const struct ofono_devinfo_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(info, vendor, data) < 0)
+                       continue;
+
+               info->driver = drv;
+               break;
+       }
+
+       return info;
+}
+
+static void devinfo_unregister(struct ofono_atom *atom)
+{
+       struct ofono_devinfo *info = __ofono_atom_get_data(atom);
+
+       g_free(info->manufacturer);
+       info->manufacturer = NULL;
+
+       g_free(info->model);
+       info->model = NULL;
+
+       g_free(info->revision);
+       info->revision = NULL;
+
+       g_free(info->serial);
+       info->serial = NULL;
+}
+
+void ofono_devinfo_register(struct ofono_devinfo *info)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(info->atom);
+
+       __ofono_atom_register(info->atom, devinfo_unregister);
+
+       info->dun_watch = __ofono_modem_add_atom_watch(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_DUN,
+                                               dun_watch, info, NULL);
+
+       query_manufacturer(info);
+}
+
+void ofono_devinfo_remove(struct ofono_devinfo *info)
+{
+       __ofono_atom_free(info->atom);
+}
+
+void ofono_devinfo_set_data(struct ofono_devinfo *info, void *data)
+{
+       info->driver_data = data;
+}
+
+void *ofono_devinfo_get_data(struct ofono_devinfo *info)
+{
+       return info->driver_data;
+}
+
+static void unregister_property(gpointer data)
+{
+       struct modem_property *property = data;
+
+       DBG("property %p", property);
+
+       g_free(property->value);
+       g_free(property);
+}
+
+static int set_modem_property(struct ofono_modem *modem, const char *name,
+                               enum property_type type, const void *value)
+{
+       struct modem_property *property;
+
+       DBG("modem %p property %s", modem, name);
+
+       if (type != PROPERTY_TYPE_STRING &&
+                       type != PROPERTY_TYPE_INTEGER)
+               return -EINVAL;
+
+       property = g_try_new0(struct modem_property, 1);
+       if (property == NULL)
+               return -ENOMEM;
+
+       property->type = type;
+
+       switch (type) {
+       case PROPERTY_TYPE_STRING:
+               property->value = g_strdup((const char *) value);
+               break;
+       case PROPERTY_TYPE_INTEGER:
+               property->value = g_memdup(value, sizeof(int));
+               break;
+       case PROPERTY_TYPE_BOOLEAN:
+               property->value = g_memdup(value, sizeof(ofono_bool_t));
+               break;
+       default:
+               break;
+       }
+
+       g_hash_table_replace(modem->properties, g_strdup(name), property);
+
+       return 0;
+}
+
+static gboolean get_modem_property(struct ofono_modem *modem, const char *name,
+                                       enum property_type type,
+                                       void *value)
+{
+       struct modem_property *property;
+
+       DBG("modem %p property %s", modem, name);
+
+       property = g_hash_table_lookup(modem->properties, name);
+
+       if (property == NULL)
+               return FALSE;
+
+       if (property->type != type)
+               return FALSE;
+
+       switch (property->type) {
+       case PROPERTY_TYPE_STRING:
+               *((const char **) value) = property->value;
+               return TRUE;
+       case PROPERTY_TYPE_INTEGER:
+               memcpy(value, property->value, sizeof(int));
+               return TRUE;
+       case PROPERTY_TYPE_BOOLEAN:
+               memcpy(value, property->value, sizeof(ofono_bool_t));
+               return TRUE;
+       default:
+               return FALSE;
+       }
+}
+
+int ofono_modem_set_string(struct ofono_modem *modem,
+                               const char *key, const char *value)
+{
+       return set_modem_property(modem, key, PROPERTY_TYPE_STRING, value);
+}
+
+int ofono_modem_set_integer(struct ofono_modem *modem,
+                               const char *key, int value)
+{
+       return set_modem_property(modem, key, PROPERTY_TYPE_INTEGER, &value);
+}
+
+int ofono_modem_set_boolean(struct ofono_modem *modem,
+                               const char *key, ofono_bool_t value)
+{
+       return set_modem_property(modem, key, PROPERTY_TYPE_BOOLEAN, &value);
+}
+
+const char *ofono_modem_get_string(struct ofono_modem *modem, const char *key)
+{
+       const char *value;
+
+       if (get_modem_property(modem, key,
+                               PROPERTY_TYPE_STRING, &value) == FALSE)
+               return NULL;
+
+       return value;
+}
+
+int ofono_modem_get_integer(struct ofono_modem *modem, const char *key)
+{
+       int value;
+
+       if (get_modem_property(modem, key,
+                               PROPERTY_TYPE_INTEGER, &value) == FALSE)
+               return 0;
+
+       return value;
+}
+
+ofono_bool_t ofono_modem_get_boolean(struct ofono_modem *modem, const char *key)
+{
+       ofono_bool_t value;
+
+       if (get_modem_property(modem, key,
+                               PROPERTY_TYPE_BOOLEAN, &value) == FALSE)
+               return FALSE;
+
+       return value;
+}
+
+void ofono_modem_set_name(struct ofono_modem *modem, const char *name)
+{
+       if (modem->name)
+               g_free(modem->name);
+
+       modem->name = g_strdup(name);
+
+       if (modem->driver) {
+               DBusConnection *conn = ofono_dbus_get_connection();
+
+               ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Name", DBUS_TYPE_STRING,
+                                               &modem->name);
+       }
+}
+
+struct ofono_modem *ofono_modem_create(const char *name, const char *type)
+{
+       struct ofono_modem *modem;
+       char path[128];
+
+       DBG("name: %s, type: %s", name, type);
+
+       if (strlen(type) > 16)
+               return NULL;
+
+       if (name && strlen(name) > 64)
+               return NULL;
+
+       if (name == NULL)
+               snprintf(path, sizeof(path), "/%s_%d", type, next_modem_id);
+       else
+               snprintf(path, sizeof(path), "/%s", name);
+
+       if (__ofono_dbus_valid_object_path(path) == FALSE)
+               return NULL;
+
+       modem = g_try_new0(struct ofono_modem, 1);
+
+       if (modem == NULL)
+               return modem;
+
+       modem->path = g_strdup(path);
+       modem->driver_type = g_strdup(type);
+       modem->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, unregister_property);
+
+       g_modem_list = g_slist_prepend(g_modem_list, modem);
+
+       if (name == NULL)
+               next_modem_id += 1;
+
+       return modem;
+}
+
+static void sim_watch(struct ofono_atom *atom,
+                       enum ofono_atom_watch_condition cond, void *data)
+{
+       struct ofono_modem *modem = data;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               modem->sim_ready_watch = 0;
+               return;
+       }
+
+       modem->sim = __ofono_atom_get_data(atom);
+       modem->sim_ready_watch = ofono_sim_add_state_watch(modem->sim,
+                                                       sim_state_watch,
+                                                       modem, NULL);
+}
+
+void __ofono_modemwatch_init(void)
+{
+       g_modemwatches = __ofono_watchlist_new(g_free);
+}
+
+void __ofono_modemwatch_cleanup(void)
+{
+       __ofono_watchlist_free(g_modemwatches);
+}
+
+unsigned int __ofono_modemwatch_add(ofono_modemwatch_cb_t cb, void *user,
+                                       ofono_destroy_func destroy)
+{
+       struct ofono_watchlist_item *watch;
+
+       if (cb == NULL)
+               return 0;
+
+       watch = g_new0(struct ofono_watchlist_item, 1);
+
+       watch->notify = cb;
+       watch->destroy = destroy;
+       watch->notify_data = user;
+
+       return __ofono_watchlist_add_item(g_modemwatches, watch);
+}
+
+gboolean __ofono_modemwatch_remove(unsigned int id)
+{
+       return __ofono_watchlist_remove_item(g_modemwatches, id);
+}
+
+static void call_modemwatches(struct ofono_modem *modem, gboolean added)
+{
+       GSList *l;
+       struct ofono_watchlist_item *watch;
+       ofono_modemwatch_cb_t notify;
+
+       DBG("%p added:%d", modem, added);
+
+       for (l = g_modemwatches->items; l; l = l->next) {
+               watch = l->data;
+
+               notify = watch->notify;
+               notify(modem, added, watch->notify_data);
+       }
+}
+
+static void emit_modem_added(struct ofono_modem *modem)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *path;
+
+       DBG("%p", modem);
+
+       signal = dbus_message_new_signal(OFONO_MANAGER_PATH,
+                                               OFONO_MANAGER_INTERFACE,
+                                               "ModemAdded");
+
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       path = modem->path;
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       __ofono_modem_append_properties(modem, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(ofono_dbus_get_connection(), signal);
+}
+
+ofono_bool_t ofono_modem_is_registered(struct ofono_modem *modem)
+{
+       if (modem == NULL)
+               return FALSE;
+
+       if (modem->driver == NULL)
+               return FALSE;
+
+       return TRUE;
+}
+
+int ofono_modem_register(struct ofono_modem *modem)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       GSList *l;
+
+       DBG("%p", modem);
+
+       if (modem == NULL)
+               return -EINVAL;
+
+       if (powering_down == TRUE)
+               return -EBUSY;
+
+       if (modem->driver != NULL)
+               return -EALREADY;
+
+       for (l = g_driver_list; l; l = l->next) {
+               const struct ofono_modem_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, modem->driver_type))
+                       continue;
+
+               if (drv->probe(modem) < 0)
+                       continue;
+
+               modem->driver = drv;
+               break;
+       }
+
+       if (modem->driver == NULL)
+               return -ENODEV;
+
+       if (!g_dbus_register_interface(conn, modem->path,
+                                       OFONO_MODEM_INTERFACE,
+                                       modem_methods, modem_signals, NULL,
+                                       modem, NULL)) {
+               ofono_error("Modem register failed on path %s", modem->path);
+
+               if (modem->driver->remove)
+                       modem->driver->remove(modem);
+
+               modem->driver = NULL;
+
+               return -EIO;
+       }
+
+       g_free(modem->driver_type);
+       modem->driver_type = NULL;
+
+       modem->atom_watches = __ofono_watchlist_new(g_free);
+       modem->online_watches = __ofono_watchlist_new(g_free);
+       modem->powered_watches = __ofono_watchlist_new(g_free);
+
+       emit_modem_added(modem);
+       call_modemwatches(modem, TRUE);
+
+       modem->sim_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_SIM,
+                                       sim_watch, modem, NULL);
+
+       return 0;
+}
+
+static void emit_modem_removed(struct ofono_modem *modem)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = modem->path;
+
+       DBG("%p", modem);
+
+       g_dbus_emit_signal(conn, OFONO_MANAGER_PATH, OFONO_MANAGER_INTERFACE,
+                               "ModemRemoved", DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+}
+
+static void modem_unregister(struct ofono_modem *modem)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       DBG("%p", modem);
+
+       if (modem->powered == TRUE)
+               set_powered(modem, FALSE);
+
+       __ofono_watchlist_free(modem->atom_watches);
+       modem->atom_watches = NULL;
+
+       __ofono_watchlist_free(modem->online_watches);
+       modem->online_watches = NULL;
+
+       __ofono_watchlist_free(modem->powered_watches);
+       modem->powered_watches = NULL;
+
+       modem->sim_watch = 0;
+       modem->sim_ready_watch = 0;
+
+       g_slist_foreach(modem->interface_list, (GFunc) g_free, NULL);
+       g_slist_free(modem->interface_list);
+       modem->interface_list = NULL;
+
+       g_slist_foreach(modem->feature_list, (GFunc) g_free, NULL);
+       g_slist_free(modem->feature_list);
+       modem->feature_list = NULL;
+
+       if (modem->timeout) {
+               g_source_remove(modem->timeout);
+               modem->timeout = 0;
+       }
+
+       if (modem->pending) {
+               dbus_message_unref(modem->pending);
+               modem->pending = NULL;
+       }
+
+       if (modem->interface_update) {
+               g_source_remove(modem->interface_update);
+               modem->interface_update = 0;
+       }
+
+       if (modem->lock_watch) {
+               lockdown_remove(modem);
+
+               ofono_dbus_signal_property_changed(conn, modem->path,
+                                       OFONO_MODEM_INTERFACE,
+                                       "Lockdown", DBUS_TYPE_BOOLEAN,
+                                       &modem->lockdown);
+       }
+
+       g_dbus_unregister_interface(conn, modem->path, OFONO_MODEM_INTERFACE);
+
+       if (modem->driver && modem->driver->remove)
+               modem->driver->remove(modem);
+
+       g_hash_table_destroy(modem->properties);
+       modem->properties = NULL;
+
+       modem->driver = NULL;
+
+       emit_modem_removed(modem);
+       call_modemwatches(modem, FALSE);
+}
+
+void ofono_modem_remove(struct ofono_modem *modem)
+{
+       DBG("%p", modem);
+
+       if (modem == NULL)
+               return;
+
+       if (modem->driver)
+               modem_unregister(modem);
+
+       g_modem_list = g_slist_remove(g_modem_list, modem);
+
+       g_free(modem->driver_type);
+       g_free(modem->name);
+       g_free(modem->path);
+       g_free(modem);
+}
+
+void ofono_modem_reset(struct ofono_modem *modem)
+{
+       int err;
+
+       DBG("%p", modem);
+
+       if (modem->pending) {
+               DBusMessage *reply = __ofono_error_failed(modem->pending);
+               __ofono_dbus_pending_reply(&modem->pending, reply);
+       }
+
+       if (modem->modem_state == MODEM_STATE_ONLINE)
+               modem->get_online = TRUE;
+
+       ofono_modem_set_powered(modem, FALSE);
+
+       err = set_powered(modem, TRUE);
+       if (err == -EINPROGRESS)
+               return;
+
+       modem_change_state(modem, MODEM_STATE_PRE_SIM);
+}
+
+void __ofono_modem_sim_reset(struct ofono_modem *modem)
+{
+       DBG("%p", modem);
+
+       modem_change_state(modem, MODEM_STATE_PRE_SIM);
+}
+
+int ofono_modem_driver_register(const struct ofono_modem_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_driver_list = g_slist_prepend(g_driver_list, (void *) d);
+
+       return 0;
+}
+
+void ofono_modem_driver_unregister(const struct ofono_modem_driver *d)
+{
+       GSList *l;
+       struct ofono_modem *modem;
+
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_driver_list = g_slist_remove(g_driver_list, (void *) d);
+
+       for (l = g_modem_list; l; l = l->next) {
+               modem = l->data;
+
+               if (modem->driver != d)
+                       continue;
+
+               modem_unregister(modem);
+       }
+}
+
+void __ofono_modem_shutdown(void)
+{
+       struct ofono_modem *modem;
+       GSList *l;
+
+       powering_down = TRUE;
+
+       for (l = g_modem_list; l; l = l->next) {
+               modem = l->data;
+
+               if (modem->driver == NULL)
+                       continue;
+
+               if (modem->powered == FALSE && modem->powered_pending == FALSE)
+                       continue;
+
+               if (set_powered(modem, FALSE) == -EINPROGRESS)
+                       modems_remaining += 1;
+       }
+
+       if (modems_remaining == 0)
+               __ofono_exit();
+}
+
+void __ofono_modem_foreach(ofono_modem_foreach_func func, void *userdata)
+{
+       struct ofono_modem *modem;
+       GSList *l;
+
+       for (l = g_modem_list; l; l = l->next) {
+               modem = l->data;
+               func(modem, userdata);
+       }
+}
+
+ofono_bool_t ofono_modem_get_emergency_mode(struct ofono_modem *modem)
+{
+       return modem->emergency != 0;
+}
+
+void __ofono_modem_inc_emergency_mode(struct ofono_modem *modem)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t emergency = TRUE;
+
+       if (++modem->emergency > 1)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Emergency", DBUS_TYPE_BOOLEAN,
+                                               &emergency);
+}
+
+void __ofono_modem_dec_emergency_mode(struct ofono_modem *modem)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t emergency = FALSE;
+
+       if (modem->emergency == 0) {
+               ofono_error("emergency mode is already deactivated!!!");
+               return;
+       }
+
+       if (modem->emergency > 1)
+               goto out;
+
+       ofono_dbus_signal_property_changed(conn, modem->path,
+                                               OFONO_MODEM_INTERFACE,
+                                               "Emergency", DBUS_TYPE_BOOLEAN,
+                                               &emergency);
+
+out:
+       modem->emergency--;
+}
diff --git a/src/nettime.c b/src/nettime.c
new file mode 100644 (file)
index 0000000..0624259
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "ofono.h"
+
+static GSList *nettime_drivers = NULL;
+
+static struct ofono_nettime_context *nettime_context_create(
+                                       struct ofono_modem *modem,
+                                       struct ofono_nettime_driver *driver)
+{
+       struct ofono_nettime_context *context;
+
+       if (driver->probe == NULL)
+               return NULL;
+
+       context = g_try_new0(struct ofono_nettime_context, 1);
+
+       if (context == NULL)
+               return NULL;
+
+       context->driver = driver;
+       context->modem = modem;
+
+       if (driver->probe(context) < 0) {
+               g_free(context);
+               return NULL;
+       }
+
+       return context;
+}
+
+static void context_remove(struct ofono_atom *atom)
+{
+       struct ofono_nettime_context *context = __ofono_atom_get_data(atom);
+
+       if (context->driver->remove)
+               context->driver->remove(context);
+
+       g_free(context);
+}
+
+void __ofono_nettime_probe_drivers(struct ofono_modem *modem)
+{
+       struct ofono_nettime_driver *driver;
+       struct ofono_nettime_context *context;
+       GSList *l;
+
+       for (l = nettime_drivers; l; l = l->next) {
+               driver = l->data;
+
+               context = nettime_context_create(modem, driver);
+               if (context == NULL)
+                       continue;
+
+               __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_NETTIME,
+                                               context_remove, context);
+       }
+}
+
+static void nettime_info_received(struct ofono_atom *atom, void *data)
+{
+       struct ofono_nettime_context *context = __ofono_atom_get_data(atom);
+       struct ofono_network_time *info = data;
+
+       if (context->driver->info_received == NULL)
+               return;
+
+       context->driver->info_received(context, info);
+}
+
+void __ofono_nettime_info_received(struct ofono_modem *modem,
+                                       struct ofono_network_time *info)
+{
+       __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_NETTIME,
+                                       nettime_info_received, info);
+}
+
+int ofono_nettime_driver_register(const struct ofono_nettime_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       nettime_drivers = g_slist_prepend(nettime_drivers, (void *) driver);
+
+       return 0;
+}
+
+void ofono_nettime_driver_unregister(const struct ofono_nettime_driver *driver)
+{
+       DBG("driver: %p name: %s", driver, driver->name);
+
+       nettime_drivers = g_slist_remove(nettime_drivers, driver);
+}
diff --git a/src/network.c b/src/network.c
new file mode 100644 (file)
index 0000000..1b9b363
--- /dev/null
@@ -0,0 +1,2118 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "simutil.h"
+#include "util.h"
+#include "storage.h"
+
+#define SETTINGS_STORE "netreg"
+#define SETTINGS_GROUP "Settings"
+
+#define NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN       0x1
+#define NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN     0x2
+#define NETWORK_REGISTRATION_FLAG_READING_PNN          0x4
+
+enum network_registration_mode {
+       NETWORK_REGISTRATION_MODE_AUTO =        0,
+       NETWORK_REGISTRATION_MODE_MANUAL =      2,
+       NETWORK_REGISTRATION_MODE_AUTO_ONLY =   5, /* Out of range of 27.007 */
+};
+
+/* 27.007 Section 7.3 <stat> */
+enum operator_status {
+       OPERATOR_STATUS_UNKNOWN =       0,
+       OPERATOR_STATUS_AVAILABLE =     1,
+       OPERATOR_STATUS_CURRENT =       2,
+       OPERATOR_STATUS_FORBIDDEN =     3,
+};
+
+struct ofono_netreg {
+       int status;
+       int location;
+       int cellid;
+       int technology;
+       int mode;
+       char *base_station;
+       struct network_operator_data *current_operator;
+       GSList *operator_list;
+       struct ofono_network_registration_ops *ops;
+       int flags;
+       DBusMessage *pending;
+       int signal_strength;
+       struct sim_spdi *spdi;
+       struct sim_eons *eons;
+       struct ofono_sim *sim;
+       struct ofono_sim_context *sim_context;
+       GKeyFile *settings;
+       char *imsi;
+       struct ofono_watchlist *status_watches;
+       const struct ofono_netreg_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       unsigned int hfp_watch;
+       unsigned int spn_watch;
+};
+
+struct network_operator_data {
+       char name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1];
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       int status;
+       unsigned int techs;
+       const struct sim_eons_operator_info *eons_info;
+       struct ofono_netreg *netreg;
+};
+
+static GSList *g_drivers = NULL;
+
+static const char *registration_mode_to_string(int mode)
+{
+       switch (mode) {
+       case NETWORK_REGISTRATION_MODE_AUTO:
+               return "auto";
+       case NETWORK_REGISTRATION_MODE_AUTO_ONLY:
+               return "auto-only";
+       case NETWORK_REGISTRATION_MODE_MANUAL:
+               return "manual";
+       }
+
+       return "unknown";
+}
+
+static inline const char *network_operator_status_to_string(int status)
+{
+       switch (status) {
+       case OPERATOR_STATUS_AVAILABLE:
+               return "available";
+       case OPERATOR_STATUS_CURRENT:
+               return "current";
+       case OPERATOR_STATUS_FORBIDDEN:
+               return "forbidden";
+       }
+
+       return "unknown";
+}
+
+static char **network_operator_technologies(struct network_operator_data *opd)
+{
+       unsigned int ntechs = 0;
+       char **techs;
+       unsigned int i;
+
+       for (i = 0; i < sizeof(opd->techs) * 8; i++) {
+               if (opd->techs & (1 << i))
+                       ntechs += 1;
+       }
+
+       techs = g_new0(char *, ntechs + 1);
+       ntechs = 0;
+
+       for (i = 0; i < sizeof(opd->techs) * 8; i++) {
+               if (!(opd->techs & (1 << i)))
+                       continue;
+
+               techs[ntechs++] = g_strdup(registration_tech_to_string(i));
+       }
+
+       return techs;
+}
+
+static void registration_status_callback(const struct ofono_error *error,
+                                       int status, int lac, int ci, int tech,
+                                       void *data)
+{
+       struct ofono_netreg *netreg = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during registration status query");
+               return;
+       }
+
+       ofono_netreg_status_notify(netreg, status, lac, ci, tech);
+}
+
+static void init_register(const struct ofono_error *error, void *data)
+{
+       struct ofono_netreg *netreg = data;
+
+       if (netreg->driver->registration_status == NULL)
+               return;
+
+       netreg->driver->registration_status(netreg,
+                                       registration_status_callback, netreg);
+}
+
+static void enforce_auto_only(struct ofono_netreg *netreg)
+{
+       if (netreg->mode != NETWORK_REGISTRATION_MODE_MANUAL)
+               return;
+
+       if (netreg->driver->register_auto == NULL)
+               return;
+
+       netreg->driver->register_auto(netreg, init_register, netreg);
+}
+
+static void set_registration_mode(struct ofono_netreg *netreg, int mode)
+{
+       DBusConnection *conn;
+       const char *strmode;
+       const char *path;
+
+       if (netreg->mode == mode)
+               return;
+
+       if (mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
+               enforce_auto_only(netreg);
+
+       netreg->mode = mode;
+
+       if (netreg->settings) {
+               const char *mode;
+
+               if (netreg->mode == NETWORK_REGISTRATION_MODE_MANUAL)
+                       mode = "manual";
+               else
+                       mode = "auto";
+
+               g_key_file_set_string(netreg->settings, SETTINGS_GROUP,
+                                       "Mode", mode);
+               storage_sync(netreg->imsi, SETTINGS_STORE, netreg->settings);
+       }
+
+       strmode = registration_mode_to_string(mode);
+
+       conn = ofono_dbus_get_connection();
+       path = __ofono_atom_get_path(netreg->atom);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "Mode", DBUS_TYPE_STRING, &strmode);
+}
+
+static void register_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       DBusMessage *reply;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               reply = dbus_message_new_method_return(netreg->pending);
+       else
+               reply = __ofono_error_failed(netreg->pending);
+
+       __ofono_dbus_pending_reply(&netreg->pending, reply);
+
+       if (netreg->driver->registration_status == NULL)
+               return;
+
+       netreg->driver->registration_status(netreg,
+                                               registration_status_callback,
+                                               netreg);
+}
+
+static struct network_operator_data *
+       network_operator_create(const struct ofono_network_operator *op)
+{
+       struct network_operator_data *opd;
+
+       opd = g_new0(struct network_operator_data, 1);
+
+       memcpy(&opd->name, op->name, sizeof(opd->name));
+       memcpy(&opd->mcc, op->mcc, sizeof(opd->mcc));
+       memcpy(&opd->mnc, op->mnc, sizeof(opd->mnc));
+
+       opd->status = op->status;
+
+       if (op->tech != -1)
+               opd->techs |= 1 << op->tech;
+
+       return opd;
+}
+
+static void network_operator_destroy(gpointer user_data)
+{
+       struct network_operator_data *op = user_data;
+
+       g_free(op);
+}
+
+static gint network_operator_compare(gconstpointer a, gconstpointer b)
+{
+       const struct network_operator_data *opda = a;
+       const struct ofono_network_operator *opb = b;
+
+       int comp1;
+       int comp2;
+
+       comp1 = strcmp(opda->mcc, opb->mcc);
+       comp2 = strcmp(opda->mnc, opb->mnc);
+
+       return comp1 != 0 ? comp1 : comp2;
+}
+
+static gint network_operator_data_compare(gconstpointer a, gconstpointer b)
+{
+       const struct network_operator_data *opa = a;
+       const struct network_operator_data *opb = b;
+
+       int comp1;
+       int comp2;
+
+       comp1 = strcmp(opa->mcc, opb->mcc);
+       comp2 = strcmp(opa->mnc, opb->mnc);
+
+       return comp1 != 0 ? comp1 : comp2;
+}
+
+static const char *network_operator_build_path(struct ofono_netreg *netreg,
+                                                       const char *mcc,
+                                                       const char *mnc)
+{
+       static char path[256];
+
+       snprintf(path, sizeof(path), "%s/operator/%s%s",
+                       __ofono_atom_get_path(netreg->atom),
+                       mcc, mnc);
+
+       return path;
+}
+
+static void set_network_operator_status(struct network_operator_data *opd,
+                                       int status)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_netreg *netreg = opd->netreg;
+       const char *status_str;
+       const char *path;
+
+       if (opd->status == status)
+               return;
+
+       opd->status = status;
+
+       /* Don't emit for the case where only operator name is reported */
+       if (opd->mcc[0] == '\0' && opd->mnc[0] == '\0')
+               return;
+
+       status_str = network_operator_status_to_string(status);
+       path = network_operator_build_path(netreg, opd->mcc, opd->mnc);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_OPERATOR_INTERFACE,
+                                       "Status", DBUS_TYPE_STRING,
+                                       &status_str);
+}
+
+static void set_network_operator_techs(struct network_operator_data *opd,
+                                       unsigned int techs)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_netreg *netreg = opd->netreg;
+       char **technologies;
+       const char *path;
+
+       if (opd->techs == techs)
+               return;
+
+       opd->techs = techs;
+       technologies = network_operator_technologies(opd);
+       path = network_operator_build_path(netreg, opd->mcc, opd->mnc);
+
+       ofono_dbus_signal_array_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "Technologies", DBUS_TYPE_STRING,
+                                       &technologies);
+       g_strfreev(technologies);
+}
+
+static char *get_operator_display_name(struct ofono_netreg *netreg)
+{
+       struct network_operator_data *opd = netreg->current_operator;
+       const char *plmn;
+       const char *spn;
+       static char name[1024];
+       static char mccmnc[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1];
+       int len = sizeof(name);
+       int home_or_spdi;
+
+       /*
+        * The name displayed to user depends on whether we're in a home
+        * PLMN or roaming and on configuration bits from the SIM, all
+        * together there are four cases to consider.
+        */
+
+       if (opd == NULL) {
+               g_strlcpy(name, "", len);
+               return name;
+       }
+
+       plmn = opd->name;
+
+       /*
+        * This is a fallback on some really broken hardware which do not
+        * report the COPS name
+        */
+       if (plmn[0] == '\0') {
+               snprintf(mccmnc, sizeof(mccmnc), "%s%s", opd->mcc, opd->mnc);
+               plmn = mccmnc;
+       }
+
+       if (opd->eons_info && opd->eons_info->longname)
+               plmn = opd->eons_info->longname;
+
+       spn = ofono_sim_get_spn(netreg->sim);
+
+       if (spn == NULL || strlen(spn) == 0) {
+               g_strlcpy(name, plmn, len);
+               return name;
+       }
+
+       if (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED)
+               home_or_spdi = TRUE;
+       else
+               home_or_spdi = sim_spdi_lookup(netreg->spdi,
+                                                       opd->mcc, opd->mnc);
+
+       if (home_or_spdi)
+               if (netreg->flags & NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN)
+                       /* Case 1 */
+                       snprintf(name, len, "%s (%s)", spn, plmn);
+               else
+                       /* Case 2 */
+                       snprintf(name, len, "%s", spn);
+       else
+               if (netreg->flags & NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN)
+                       /* Case 3 */
+                       snprintf(name, len, "%s (%s)", spn, plmn);
+               else
+                       /* Case 4 */
+                       snprintf(name, len, "%s", plmn);
+
+       return name;
+}
+
+static void netreg_emit_operator_display_name(struct ofono_netreg *netreg)
+{
+       const char *operator = get_operator_display_name(netreg);
+
+       ofono_dbus_signal_property_changed(ofono_dbus_get_connection(),
+                                       __ofono_atom_get_path(netreg->atom),
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "Name", DBUS_TYPE_STRING, &operator);
+}
+
+static void set_network_operator_name(struct network_operator_data *opd,
+                                       const char *name)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_netreg *netreg = opd->netreg;
+       const char *path;
+
+       if (name[0] == '\0')
+               return;
+
+       if (!strncmp(opd->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH))
+               return;
+
+       strncpy(opd->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
+       opd->name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+
+       /*
+        * If we have Enhanced Operator Name info on the SIM, we always use
+        * that, so do not need to emit the signal here
+        */
+       if (opd->eons_info && opd->eons_info->longname)
+               return;
+
+       if (opd == netreg->current_operator)
+               netreg_emit_operator_display_name(netreg);
+
+       /* Don't emit when only operator name is reported */
+       if (opd->mcc[0] == '\0' && opd->mnc[0] == '\0')
+               return;
+
+       path = network_operator_build_path(netreg, opd->mcc, opd->mnc);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_OPERATOR_INTERFACE,
+                                       "Name", DBUS_TYPE_STRING, &name);
+}
+
+static void set_network_operator_eons_info(struct network_operator_data *opd,
+                               const struct sim_eons_operator_info *eons_info)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_netreg *netreg = opd->netreg;
+       const struct sim_eons_operator_info *old_eons_info = opd->eons_info;
+       const char *path;
+       const char *oldname;
+       const char *newname;
+       const char *oldinfo;
+       const char *newinfo;
+
+       if (old_eons_info == NULL && eons_info == NULL)
+               return;
+
+       path = network_operator_build_path(netreg, opd->mcc, opd->mnc);
+       opd->eons_info = eons_info;
+
+       if (old_eons_info && old_eons_info->longname)
+               oldname = old_eons_info->longname;
+       else
+               oldname = opd->name;
+
+       if (eons_info && eons_info->longname)
+               newname = eons_info->longname;
+       else
+               newname = opd->name;
+
+       if (oldname != newname && strcmp(oldname, newname)) {
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_OPERATOR_INTERFACE,
+                                       "Name", DBUS_TYPE_STRING, &newname);
+
+               if (opd == netreg->current_operator)
+                       netreg_emit_operator_display_name(netreg);
+       }
+
+       if (old_eons_info && old_eons_info->info)
+               oldinfo = old_eons_info->info;
+       else
+               oldinfo = "";
+
+       if (eons_info && eons_info->info)
+               newinfo = eons_info->info;
+       else
+               newinfo = "";
+
+       if (oldinfo != newinfo && strcmp(oldinfo, newinfo))
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_OPERATOR_INTERFACE,
+                                       "AdditionalInformation",
+                                       DBUS_TYPE_STRING, &newinfo);
+}
+
+static void append_operator_properties(struct network_operator_data *opd,
+                                       DBusMessageIter *dict)
+{
+       const char *name = opd->name;
+       const char *status = network_operator_status_to_string(opd->status);
+       char mccmnc[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1];
+
+       if (opd->eons_info && opd->eons_info->longname)
+               name = opd->eons_info->longname;
+
+       if (name[0] == '\0') {
+               snprintf(mccmnc, sizeof(mccmnc), "%s%s", opd->mcc, opd->mnc);
+               name = mccmnc;
+       }
+
+       ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &name);
+
+       ofono_dbus_dict_append(dict, "Status", DBUS_TYPE_STRING, &status);
+
+       if (*opd->mcc != '\0') {
+               const char *mcc = opd->mcc;
+               ofono_dbus_dict_append(dict, "MobileCountryCode",
+                                       DBUS_TYPE_STRING, &mcc);
+       }
+
+       if (*opd->mnc != '\0') {
+               const char *mnc = opd->mnc;
+               ofono_dbus_dict_append(dict, "MobileNetworkCode",
+                                       DBUS_TYPE_STRING, &mnc);
+       }
+
+       if (opd->techs != 0) {
+               char **technologies = network_operator_technologies(opd);
+
+               ofono_dbus_dict_append_array(dict, "Technologies",
+                                               DBUS_TYPE_STRING,
+                                               &technologies);
+
+               g_strfreev(technologies);
+       }
+
+       if (opd->eons_info && opd->eons_info->info) {
+               const char *additional = opd->eons_info->info;
+
+               ofono_dbus_dict_append(dict, "AdditionalInformation",
+                                       DBUS_TYPE_STRING, &additional);
+       }
+}
+
+static DBusMessage *network_operator_get_properties(DBusConnection *conn,
+                                                       DBusMessage *msg,
+                                                       void *data)
+{
+       struct network_operator_data *opd = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       append_operator_properties(opd, &dict);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *network_operator_register(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct network_operator_data *opd = data;
+       struct ofono_netreg *netreg = opd->netreg;
+
+       if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
+               return __ofono_error_access_denied(msg);
+
+       if (netreg->pending)
+               return __ofono_error_busy(msg);
+
+       if (netreg->driver->register_manual == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       netreg->pending = dbus_message_ref(msg);
+
+       netreg->driver->register_manual(netreg, opd->mcc, opd->mnc,
+                                       register_callback, netreg);
+
+       set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_MANUAL);
+
+       return NULL;
+}
+
+static GDBusMethodTable network_operator_methods[] = {
+       { "GetProperties",  "",  "a{sv}",  network_operator_get_properties },
+       { "Register",       "",  "",       network_operator_register,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable network_operator_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static gboolean network_operator_dbus_register(struct ofono_netreg *netreg,
+                                       struct network_operator_data *opd)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+
+       path = network_operator_build_path(netreg, opd->mcc, opd->mnc);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_NETWORK_OPERATOR_INTERFACE,
+                                       network_operator_methods,
+                                       network_operator_signals,
+                                       NULL, opd,
+                                       network_operator_destroy)) {
+               ofono_error("Could not register NetworkOperator %s", path);
+               return FALSE;
+       }
+
+       opd->netreg = netreg;
+       opd->eons_info = NULL;
+
+       if (netreg->eons)
+               opd->eons_info = sim_eons_lookup(netreg->eons,
+                                                       opd->mcc, opd->mnc);
+
+       return TRUE;
+}
+
+static gboolean network_operator_dbus_unregister(struct ofono_netreg *netreg,
+                                       struct network_operator_data *opd)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+
+       path = network_operator_build_path(netreg, opd->mcc, opd->mnc);
+
+       return g_dbus_unregister_interface(conn, path,
+                                       OFONO_NETWORK_OPERATOR_INTERFACE);
+}
+
+static GSList *compress_operator_list(const struct ofono_network_operator *list,
+                                       int total)
+{
+       GSList *oplist = 0;
+       GSList *o;
+       int i;
+       struct network_operator_data *opd;
+
+       for (i = 0; i < total; i++) {
+               o = NULL;
+
+               if (list[i].mcc[0] == '\0' || list[i].mnc[0] == '\0')
+                       continue;
+
+               if (oplist)
+                       o = g_slist_find_custom(oplist, &list[i],
+                                               network_operator_compare);
+
+               if (o == NULL) {
+                       opd = network_operator_create(&list[i]);
+                       oplist = g_slist_prepend(oplist, opd);
+               } else if (o && list[i].tech != -1) {
+                       opd = o->data;
+                       opd->techs |= 1 << list[i].tech;
+               }
+       }
+
+       if (oplist)
+               oplist = g_slist_reverse(oplist);
+
+       return oplist;
+}
+
+static gboolean update_operator_list(struct ofono_netreg *netreg, int total,
+                               const struct ofono_network_operator *list)
+{
+       GSList *n = NULL;
+       GSList *o;
+       GSList *compressed;
+       GSList *c;
+       gboolean changed = FALSE;
+
+       compressed = compress_operator_list(list, total);
+
+       for (c = compressed; c; c = c->next) {
+               struct network_operator_data *copd = c->data;
+
+               o = g_slist_find_custom(netreg->operator_list, copd,
+                                       network_operator_data_compare);
+
+               if (o) { /* Update and move to a new list */
+                       set_network_operator_status(o->data, copd->status);
+                       set_network_operator_techs(o->data, copd->techs);
+                       set_network_operator_name(o->data, copd->name);
+
+                       n = g_slist_prepend(n, o->data);
+                       netreg->operator_list =
+                               g_slist_remove(netreg->operator_list, o->data);
+               } else {
+                       /* New operator */
+                       struct network_operator_data *opd;
+
+                       opd = g_memdup(copd,
+                                       sizeof(struct network_operator_data));
+
+                       if (!network_operator_dbus_register(netreg, opd)) {
+                               g_free(opd);
+                               continue;
+                       }
+
+                       n = g_slist_prepend(n, opd);
+                       changed = TRUE;
+               }
+       }
+
+       g_slist_foreach(compressed, (GFunc)g_free, NULL);
+       g_slist_free(compressed);
+
+       if (n)
+               n = g_slist_reverse(n);
+
+       if (netreg->operator_list)
+               changed = TRUE;
+
+       for (o = netreg->operator_list; o; o = o->next)
+               network_operator_dbus_unregister(netreg, o->data);
+
+       g_slist_free(netreg->operator_list);
+
+       netreg->operator_list = n;
+
+       return changed;
+}
+
+static DBusMessage *network_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       const char *status = registration_status_to_string(netreg->status);
+       const char *operator;
+       const char *mode = registration_mode_to_string(netreg->mode);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status);
+       ofono_dbus_dict_append(&dict, "Mode", DBUS_TYPE_STRING, &mode);
+
+       if (netreg->location != -1) {
+               dbus_uint16_t location = netreg->location;
+               ofono_dbus_dict_append(&dict, "LocationAreaCode",
+                                       DBUS_TYPE_UINT16, &location);
+       }
+
+       if (netreg->cellid != -1) {
+               dbus_uint32_t cellid = netreg->cellid;
+               ofono_dbus_dict_append(&dict, "CellId",
+                                       DBUS_TYPE_UINT32, &cellid);
+       }
+
+       if (netreg->technology != -1) {
+               const char *technology =
+                       registration_tech_to_string(netreg->technology);
+
+               ofono_dbus_dict_append(&dict, "Technology", DBUS_TYPE_STRING,
+                                       &technology);
+       }
+
+       if (netreg->current_operator) {
+               if (netreg->current_operator->mcc[0] != '\0') {
+                       const char *mcc = netreg->current_operator->mcc;
+                       ofono_dbus_dict_append(&dict, "MobileCountryCode",
+                                               DBUS_TYPE_STRING, &mcc);
+               }
+
+               if (netreg->current_operator->mnc[0] != '\0') {
+                       const char *mnc = netreg->current_operator->mnc;
+                       ofono_dbus_dict_append(&dict, "MobileNetworkCode",
+                                               DBUS_TYPE_STRING, &mnc);
+               }
+       }
+
+       operator = get_operator_display_name(netreg);
+       ofono_dbus_dict_append(&dict, "Name", DBUS_TYPE_STRING, &operator);
+
+       if (netreg->signal_strength != -1) {
+               unsigned char strength = netreg->signal_strength;
+
+               ofono_dbus_dict_append(&dict, "Strength", DBUS_TYPE_BYTE,
+                                       &strength);
+       }
+
+       if (netreg->base_station)
+               ofono_dbus_dict_append(&dict, "BaseStation", DBUS_TYPE_STRING,
+                                       &netreg->base_station);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *network_register(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+
+       if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
+               return __ofono_error_access_denied(msg);
+
+       if (netreg->pending)
+               return __ofono_error_busy(msg);
+
+       if (netreg->driver->register_auto == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       netreg->pending = dbus_message_ref(msg);
+
+       netreg->driver->register_auto(netreg, register_callback, netreg);
+
+       set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO);
+
+       return NULL;
+}
+
+static void append_operator_struct(struct ofono_netreg *netreg,
+                                       struct network_operator_data *opd,
+                                       DBusMessageIter *iter)
+{
+       DBusMessageIter entry, dict;
+       const char *path;
+
+       path = network_operator_build_path(netreg, opd->mcc, opd->mnc);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path);
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       append_operator_properties(opd, &dict);
+       dbus_message_iter_close_container(&entry, &dict);
+       dbus_message_iter_close_container(iter, &entry);
+}
+
+static void append_operator_struct_list(struct ofono_netreg *netreg,
+                                       DBusMessageIter *array)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       char **children;
+       char path[256];
+       GSList *l;
+
+       snprintf(path, sizeof(path), "%s/operator",
+                       __ofono_atom_get_path(netreg->atom));
+
+       if (!dbus_connection_list_registered(conn, path, &children)) {
+               DBG("Unable to obtain registered NetworkOperator(s)");
+               return;
+       }
+
+       /*
+        * Quoting 27.007: "The list of operators shall be in order: home
+        * network, networks referenced in SIM or active application in the
+        * UICC (GSM or USIM) in the following order: HPLMN selector, User
+        * controlled PLMN selector, Operator controlled PLMN selector and
+        * PLMN selector (in the SIM or GSM application), and other networks."
+        * Thus we must make sure we return the list in the same order,
+        * if possible.  Luckily the operator_list is stored in order already
+        */
+       for (l = netreg->operator_list; l; l = l->next) {
+               struct network_operator_data *opd = l->data;
+               char mnc[OFONO_MAX_MNC_LENGTH + 1];
+               char mcc[OFONO_MAX_MCC_LENGTH + 1];
+               int j;
+
+               for (j = 0; children[j]; j++) {
+                       sscanf(children[j], "%3[0-9]%[0-9]", mcc, mnc);
+
+                       if (!strcmp(opd->mcc, mcc) && !strcmp(opd->mnc, mnc))
+                               append_operator_struct(netreg, opd, array);
+               }
+       }
+
+       dbus_free_string_array(children);
+}
+
+static void operator_list_callback(const struct ofono_error *error, int total,
+                               const struct ofono_network_operator *list,
+                               void *data)
+{
+       struct ofono_netreg *netreg = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter array;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error occurred during operator list");
+               __ofono_dbus_pending_reply(&netreg->pending,
+                                       __ofono_error_failed(netreg->pending));
+               return;
+       }
+
+       update_operator_list(netreg, total, list);
+
+       reply = dbus_message_new_method_return(netreg->pending);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+       append_operator_struct_list(netreg, &array);
+       dbus_message_iter_close_container(&iter, &array);
+
+       __ofono_dbus_pending_reply(&netreg->pending, reply);
+}
+
+static DBusMessage *network_scan(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+
+       if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
+               return __ofono_error_access_denied(msg);
+
+       if (netreg->pending)
+               return __ofono_error_busy(msg);
+
+       if (netreg->driver->list_operators == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       netreg->pending = dbus_message_ref(msg);
+
+       netreg->driver->list_operators(netreg, operator_list_callback, netreg);
+
+       return NULL;
+}
+
+static DBusMessage *network_get_operators(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter array;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+       append_operator_struct_list(netreg, &array);
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static GDBusMethodTable network_registration_methods[] = {
+       { "GetProperties",  "",  "a{sv}",       network_get_properties },
+       { "Register",       "",  "",            network_register,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "GetOperators",   "",  "a(oa{sv})",   network_get_operators },
+       { "Scan",           "",  "a(oa{sv})",   network_scan,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable network_registration_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static void set_registration_status(struct ofono_netreg *netreg, int status)
+{
+       const char *str_status = registration_status_to_string(status);
+       const char *path = __ofono_atom_get_path(netreg->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       netreg->status = status;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "Status", DBUS_TYPE_STRING,
+                                       &str_status);
+}
+
+static void set_registration_location(struct ofono_netreg *netreg, int lac)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(netreg->atom);
+       dbus_uint16_t dbus_lac = lac;
+
+       if (lac > 0xffff)
+               return;
+
+       netreg->location = lac;
+
+       if (netreg->location == -1)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "LocationAreaCode",
+                                       DBUS_TYPE_UINT16, &dbus_lac);
+}
+
+static void set_registration_cellid(struct ofono_netreg *netreg, int ci)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(netreg->atom);
+       dbus_uint32_t dbus_ci = ci;
+
+       netreg->cellid = ci;
+
+       if (netreg->cellid == -1)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "CellId", DBUS_TYPE_UINT32, &dbus_ci);
+}
+
+static void set_registration_technology(struct ofono_netreg *netreg, int tech)
+{
+       const char *tech_str = registration_tech_to_string(tech);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(netreg->atom);
+
+       netreg->technology = tech;
+
+       if (netreg->technology == -1)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "Technology", DBUS_TYPE_STRING,
+                                       &tech_str);
+}
+
+void __ofono_netreg_set_base_station_name(struct ofono_netreg *netreg,
+                                               const char *name)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(netreg->atom);
+       const char *base_station = name ? name : "";
+
+       /* Cell ID changed, but we don't have a cell name, nothing to do */
+       if (netreg->base_station == NULL && name == NULL)
+               return;
+
+       if (netreg->base_station)
+               g_free(netreg->base_station);
+
+       if (name == NULL) {
+               netreg->base_station = NULL;
+
+               /*
+                * We just got unregistered, set name to NULL
+                * but don't emit signal
+                */
+               if (netreg->current_operator == NULL)
+                       return;
+       } else {
+               netreg->base_station = g_strdup(name);
+       }
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "BaseStation", DBUS_TYPE_STRING,
+                                       &base_station);
+}
+
+unsigned int __ofono_netreg_add_status_watch(struct ofono_netreg *netreg,
+                               ofono_netreg_status_notify_cb_t notify,
+                               void *data, ofono_destroy_func destroy)
+{
+       struct ofono_watchlist_item *item;
+
+       DBG("%p", netreg);
+
+       if (netreg == NULL)
+               return 0;
+
+       if (notify == NULL)
+               return 0;
+
+       item = g_new0(struct ofono_watchlist_item, 1);
+
+       item->notify = notify;
+       item->destroy = destroy;
+       item->notify_data = data;
+
+       return __ofono_watchlist_add_item(netreg->status_watches, item);
+}
+
+gboolean __ofono_netreg_remove_status_watch(struct ofono_netreg *netreg,
+                                               unsigned int id)
+{
+       DBG("%p", netreg);
+
+       return __ofono_watchlist_remove_item(netreg->status_watches, id);
+}
+
+static void notify_status_watches(struct ofono_netreg *netreg)
+{
+       struct ofono_watchlist_item *item;
+       GSList *l;
+       ofono_netreg_status_notify_cb_t notify;
+       const char *mcc = NULL;
+       const char *mnc = NULL;
+
+       if (netreg->current_operator) {
+               mcc = netreg->current_operator->mcc;
+               mnc = netreg->current_operator->mnc;
+       }
+
+       for (l = netreg->status_watches->items; l; l = l->next) {
+               item = l->data;
+               notify = item->notify;
+
+               notify(netreg->status, netreg->location, netreg->cellid,
+                       netreg->technology, mcc, mnc, item->notify_data);
+       }
+}
+
+static void reset_available(struct network_operator_data *old,
+                               const struct ofono_network_operator *new)
+{
+       if (old == NULL)
+               return;
+
+       if (new == NULL || network_operator_compare(old, new) != 0)
+               set_network_operator_status(old, OPERATOR_STATUS_AVAILABLE);
+}
+
+static void current_operator_callback(const struct ofono_error *error,
+                               const struct ofono_network_operator *current,
+                               void *data)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_netreg *netreg = data;
+       const char *path = __ofono_atom_get_path(netreg->atom);
+       GSList *op = NULL;
+
+       DBG("%p, %p", netreg, netreg->current_operator);
+
+       /*
+        * Sometimes we try to query COPS right when we roam off the cell,
+        * in which case the operator information frequently comes in bogus.
+        * We ignore it here
+        */
+       if (netreg->status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
+                       netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING)
+               current = NULL;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during current operator");
+               return;
+       }
+
+       if (netreg->current_operator == NULL && current == NULL)
+               return;
+
+       /* We got a new network operator, reset the previous one's status */
+       /* It will be updated properly later */
+       reset_available(netreg->current_operator, current);
+
+       if (current)
+               op = g_slist_find_custom(netreg->operator_list, current,
+                                       network_operator_compare);
+
+       if (op) {
+               struct network_operator_data *opd = op->data;
+               unsigned int techs = opd->techs;
+
+               if (current->tech != -1) {
+                       techs |= 1 << current->tech;
+                       set_network_operator_techs(opd, techs);
+               }
+
+               set_network_operator_status(opd, OPERATOR_STATUS_CURRENT);
+               set_network_operator_name(opd, current->name);
+
+               if (netreg->current_operator == op->data)
+                       return;
+
+               netreg->current_operator = op->data;
+               goto emit;
+       }
+
+       if (current) {
+               struct network_operator_data *opd;
+
+               opd = network_operator_create(current);
+
+               if (opd->mcc[0] != '\0' && opd->mnc[0] != '\0' &&
+                               !network_operator_dbus_register(netreg, opd)) {
+                       g_free(opd);
+                       return;
+               } else
+                       opd->netreg = netreg;
+
+               netreg->current_operator = opd;
+               netreg->operator_list = g_slist_append(netreg->operator_list,
+                                                       opd);
+       } else {
+               /* We don't free this here because operator is registered */
+               /* Taken care of elsewhere */
+               netreg->current_operator = NULL;
+       }
+
+emit:
+       netreg_emit_operator_display_name(netreg);
+
+       if (netreg->current_operator) {
+               if (netreg->current_operator->mcc[0] != '\0') {
+                       const char *mcc = netreg->current_operator->mcc;
+                       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "MobileCountryCode",
+                                       DBUS_TYPE_STRING, &mcc);
+               }
+
+               if (netreg->current_operator->mnc[0] != '\0') {
+                       const char *mnc = netreg->current_operator->mnc;
+                       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "MobileNetworkCode",
+                                       DBUS_TYPE_STRING, &mnc);
+               }
+       }
+
+       notify_status_watches(netreg);
+}
+
+static void signal_strength_callback(const struct ofono_error *error,
+                                       int strength, void *data)
+{
+       struct ofono_netreg *netreg = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during signal strength query");
+               return;
+       }
+
+       ofono_netreg_strength_notify(netreg, strength);
+}
+
+static void notify_emulator_status(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       switch (GPOINTER_TO_INT(data)) {
+       case NETWORK_REGISTRATION_STATUS_REGISTERED:
+               ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_SERVICE, 1);
+               ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_ROAMING, 0);
+               break;
+       case NETWORK_REGISTRATION_STATUS_ROAMING:
+               ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_SERVICE, 1);
+               ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_ROAMING, 1);
+               break;
+       default:
+               ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_SERVICE, 0);
+               ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_ROAMING, 0);
+       }
+}
+
+void ofono_netreg_status_notify(struct ofono_netreg *netreg, int status,
+                       int lac, int ci, int tech)
+{
+       if (netreg == NULL)
+               return;
+
+       if (netreg->status != status) {
+               struct ofono_modem *modem;
+
+               set_registration_status(netreg, status);
+
+               modem = __ofono_atom_get_modem(netreg->atom);
+               __ofono_modem_foreach_registered_atom(modem,
+                                       OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                       notify_emulator_status,
+                                       GINT_TO_POINTER(netreg->status));
+       }
+
+       if (netreg->location != lac)
+               set_registration_location(netreg, lac);
+
+       if (netreg->cellid != ci)
+               set_registration_cellid(netreg, ci);
+
+       if (netreg->technology != tech)
+               set_registration_technology(netreg, tech);
+
+       if (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED ||
+               netreg->status == NETWORK_REGISTRATION_STATUS_ROAMING) {
+               if (netreg->driver->current_operator != NULL)
+                       netreg->driver->current_operator(netreg,
+                                       current_operator_callback, netreg);
+
+               if (netreg->driver->strength != NULL)
+                       netreg->driver->strength(netreg,
+                                       signal_strength_callback, netreg);
+       } else {
+               struct ofono_error error;
+
+               error.type = OFONO_ERROR_TYPE_NO_ERROR;
+               error.error = 0;
+
+               current_operator_callback(&error, NULL, netreg);
+               __ofono_netreg_set_base_station_name(netreg, NULL);
+
+               netreg->signal_strength = -1;
+       }
+
+       notify_status_watches(netreg);
+}
+
+void ofono_netreg_time_notify(struct ofono_netreg *netreg,
+                               struct ofono_network_time *info)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(netreg->atom);
+
+       if (info == NULL)
+               return;
+
+       __ofono_nettime_info_received(modem, info);
+}
+
+static void sim_csp_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int i = 0;
+
+       if (!ok)
+               return;
+
+       if (total_length < 18)
+               return;
+
+       /*
+        * According to CPHS 4.2, EFcsp is an array of two-byte service
+        * entries, each consisting of a one byte service group
+        * identifier followed by 8 bits; each bit is indicating
+        * availability of a specific service or feature.
+        *
+        * The PLMN mode bit, if present, indicates whether manual
+        * operator selection should be disabled or enabled. When
+        * unset, the device is forced to automatic mode; when set,
+        * manual selection is to be enabled. The latter is also the
+        * default.
+        */
+       while (i < total_length &&
+                       data[i] != SIM_CSP_ENTRY_VALUE_ADDED_SERVICES)
+               i += 2;
+
+       if (i == total_length)
+               return;
+
+       if ((data[i + 1] & 0x80) != 0) {
+               if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
+                       set_registration_mode(netreg,
+                                               NETWORK_REGISTRATION_MODE_AUTO);
+
+               return;
+       }
+
+       set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO_ONLY);
+}
+
+static void sim_csp_changed(int id, void *userdata)
+{
+       struct ofono_netreg *netreg = userdata;
+
+       ofono_sim_read(netreg->sim_context, SIM_EF_CPHS_CSP_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_csp_read_cb, netreg);
+}
+
+static void init_registration_status(const struct ofono_error *error,
+                                       int status, int lac, int ci, int tech,
+                                       void *data)
+{
+       struct ofono_netreg *netreg = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during registration status query");
+               return;
+       }
+
+       ofono_netreg_status_notify(netreg, status, lac, ci, tech);
+
+       /*
+        * Bootstrap our signal strength value without waiting for the
+        * stack to report it
+        */
+       if (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED ||
+               netreg->status == NETWORK_REGISTRATION_STATUS_ROAMING) {
+               if (netreg->driver->strength != NULL)
+                       netreg->driver->strength(netreg,
+                                       signal_strength_callback, netreg);
+       }
+
+       if (netreg->mode != NETWORK_REGISTRATION_MODE_MANUAL &&
+               (status == NETWORK_REGISTRATION_STATUS_NOT_REGISTERED ||
+                       status == NETWORK_REGISTRATION_STATUS_DENIED ||
+                       status == NETWORK_REGISTRATION_STATUS_UNKNOWN)) {
+               if (netreg->driver->register_auto != NULL)
+                       netreg->driver->register_auto(netreg, init_register,
+                                                       netreg);
+       }
+
+       if (netreg->driver->register_manual == NULL) {
+               set_registration_mode(netreg,
+                                       NETWORK_REGISTRATION_MODE_AUTO_ONLY);
+               return;
+       }
+
+       if (netreg->sim_context) {
+               ofono_sim_read(netreg->sim_context, SIM_EF_CPHS_CSP_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               sim_csp_read_cb, netreg);
+
+               ofono_sim_add_file_watch(netreg->sim_context,
+                                               SIM_EF_CPHS_CSP_FILEID,
+                                               sim_csp_changed, netreg, NULL);
+       }
+}
+
+static void notify_emulator_strength(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+       int val = 0;
+
+       if (GPOINTER_TO_INT(data) > 0)
+               val = (GPOINTER_TO_INT(data) - 1) / 20 + 1;
+
+       ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_SIGNAL, val);
+}
+
+void ofono_netreg_strength_notify(struct ofono_netreg *netreg, int strength)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem;
+
+       if (netreg->signal_strength == strength)
+               return;
+
+       /*
+        * Theoretically we can get signal strength even when not registered
+        * to any network.  However, what do we do with it in that case?
+        */
+       if (netreg->status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
+                       netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING)
+               return;
+
+       netreg->signal_strength = strength;
+
+       if (strength != -1) {
+               const char *path = __ofono_atom_get_path(netreg->atom);
+               unsigned char strength = netreg->signal_strength;
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       "Strength", DBUS_TYPE_BYTE,
+                                       &strength);
+       }
+
+       modem = __ofono_atom_get_modem(netreg->atom);
+       __ofono_modem_foreach_registered_atom(modem,
+                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                               notify_emulator_strength,
+                               GINT_TO_POINTER(netreg->signal_strength));
+}
+
+static void sim_opl_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int total;
+       GSList *l;
+
+       if (!ok) {
+               if (record > 0)
+                       goto optimize;
+
+               return;
+       }
+
+       if (record_length < 8 || length < record_length)
+               return;
+
+       total = length / record_length;
+
+       sim_eons_add_opl_record(netreg->eons, data, record_length);
+
+       if (record != total)
+               return;
+
+optimize:
+       sim_eons_optimize(netreg->eons);
+
+       for (l = netreg->operator_list; l; l = l->next) {
+               struct network_operator_data *opd = l->data;
+               const struct sim_eons_operator_info *eons_info;
+
+               eons_info = sim_eons_lookup(netreg->eons, opd->mcc, opd->mnc);
+
+               set_network_operator_eons_info(opd, eons_info);
+       }
+}
+
+static void sim_pnn_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int total;
+
+       if (!ok)
+               goto check;
+
+       if (length < 3 || record_length < 3 || length < record_length)
+               goto check;
+
+       total = length / record_length;
+
+       if (netreg->eons == NULL)
+               netreg->eons = sim_eons_new(total);
+
+       sim_eons_add_pnn_record(netreg->eons, record, data, record_length);
+
+       if (record != total)
+               return;
+
+check:
+       netreg->flags &= ~NETWORK_REGISTRATION_FLAG_READING_PNN;
+
+       /*
+        * If PNN is not present then OPL is not useful, don't
+        * retrieve it.  If OPL is not there then PNN[1] will
+        * still be used for the HPLMN and/or EHPLMN, if PNN
+        * is present.
+        */
+       if (netreg->eons && !sim_eons_pnn_is_empty(netreg->eons))
+               ofono_sim_read(netreg->sim_context, SIM_EFOPL_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               sim_opl_read_cb, netreg);
+}
+
+static void sim_spdi_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+
+       if (!ok)
+               return;
+
+       netreg->spdi = sim_spdi_new(data, length);
+
+       if (netreg->current_operator == NULL)
+               return;
+
+       if (netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING)
+               return;
+
+       if (!sim_spdi_lookup(netreg->spdi, netreg->current_operator->mcc,
+                               netreg->current_operator->mnc))
+               return;
+
+       netreg_emit_operator_display_name(netreg);
+}
+
+static void sim_spn_display_condition_parse(struct ofono_netreg *netreg,
+                                               guint8 dcbyte)
+{
+       if (dcbyte & SIM_EFSPN_DC_HOME_PLMN_BIT)
+               netreg->flags |= NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN;
+
+       if (!(dcbyte & SIM_EFSPN_DC_ROAMING_SPN_BIT))
+               netreg->flags |= NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN;
+}
+
+static void spn_read_cb(const char *spn, const char *dc, void *data)
+{
+       struct ofono_netreg *netreg = data;
+
+       netreg->flags &= ~(NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN |
+                               NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN);
+
+       if (dc)
+               sim_spn_display_condition_parse(netreg, *dc);
+
+       if (netreg->current_operator)
+               netreg_emit_operator_display_name(netreg);
+}
+
+int ofono_netreg_get_location(struct ofono_netreg *netreg)
+{
+       if (netreg == NULL)
+               return -1;
+
+       return netreg->location;
+}
+
+int ofono_netreg_get_cellid(struct ofono_netreg *netreg)
+{
+       if (netreg == NULL)
+               return -1;
+
+       return netreg->cellid;
+}
+
+int ofono_netreg_get_status(struct ofono_netreg *netreg)
+{
+       if (netreg == NULL)
+               return -1;
+
+       return netreg->status;
+}
+
+int ofono_netreg_get_technology(struct ofono_netreg *netreg)
+{
+       if (netreg == NULL)
+               return -1;
+
+       return netreg->technology;
+}
+
+const char *ofono_netreg_get_mcc(struct ofono_netreg *netreg)
+{
+       if (netreg == NULL)
+               return NULL;
+
+       if (netreg->current_operator == NULL)
+               return NULL;
+
+       return netreg->current_operator->mcc;
+}
+
+const char *ofono_netreg_get_mnc(struct ofono_netreg *netreg)
+{
+       if (netreg == NULL)
+               return NULL;
+
+       if (netreg->current_operator == NULL)
+               return NULL;
+
+       return netreg->current_operator->mnc;
+}
+
+int ofono_netreg_driver_register(const struct ofono_netreg_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_netreg_driver_unregister(const struct ofono_netreg_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void emulator_remove_handler(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       ofono_emulator_remove_handler(em, data);
+}
+
+static void netreg_unregister(struct ofono_atom *atom)
+{
+       struct ofono_netreg *netreg = __ofono_atom_get_data(atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+       GSList *l;
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               notify_emulator_status,
+                                               GINT_TO_POINTER(0));
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               notify_emulator_strength,
+                                               GINT_TO_POINTER(0));
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "+COPS");
+
+       __ofono_modem_remove_atom_watch(modem, netreg->hfp_watch);
+
+       __ofono_watchlist_free(netreg->status_watches);
+       netreg->status_watches = NULL;
+
+       for (l = netreg->operator_list; l; l = l->next) {
+               struct network_operator_data *opd = l->data;
+
+               if (opd->mcc[0] == '\0' && opd->mnc[0] == '\0') {
+                       g_free(opd);
+                       continue;
+               }
+
+               network_operator_dbus_unregister(netreg, l->data);
+       }
+
+       g_slist_free(netreg->operator_list);
+       netreg->operator_list = NULL;
+
+       if (netreg->base_station) {
+               g_free(netreg->base_station);
+               netreg->base_station = NULL;
+       }
+
+       if (netreg->settings) {
+               storage_close(netreg->imsi, SETTINGS_STORE,
+                               netreg->settings, TRUE);
+
+               g_free(netreg->imsi);
+               netreg->imsi = NULL;
+               netreg->settings = NULL;
+       }
+
+       if (netreg->spn_watch)
+               ofono_sim_remove_spn_watch(netreg->sim, &netreg->spn_watch);
+
+       if (netreg->sim_context) {
+               ofono_sim_context_free(netreg->sim_context);
+               netreg->sim_context = NULL;
+       }
+
+       netreg->sim = NULL;
+
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE);
+       ofono_modem_remove_interface(modem,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE);
+}
+
+static void netreg_remove(struct ofono_atom *atom)
+{
+       struct ofono_netreg *netreg = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (netreg == NULL)
+               return;
+
+       if (netreg->driver != NULL && netreg->driver->remove != NULL)
+               netreg->driver->remove(netreg);
+
+       sim_eons_free(netreg->eons);
+       sim_spdi_free(netreg->spdi);
+
+       g_free(netreg);
+}
+
+struct ofono_netreg *ofono_netreg_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_netreg *netreg;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       netreg = g_try_new0(struct ofono_netreg, 1);
+
+       if (netreg == NULL)
+               return NULL;
+
+       netreg->status = NETWORK_REGISTRATION_STATUS_UNKNOWN;
+       netreg->location = -1;
+       netreg->cellid = -1;
+       netreg->technology = -1;
+       netreg->signal_strength = -1;
+
+       netreg->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_NETREG,
+                                               netreg_remove, netreg);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_netreg_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(netreg, vendor, data) < 0)
+                       continue;
+
+               netreg->driver = drv;
+               break;
+       }
+
+       return netreg;
+}
+
+static void netreg_load_settings(struct ofono_netreg *netreg)
+{
+       const char *imsi;
+       char *strmode;
+       gboolean upgrade = FALSE;
+
+       if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
+               return;
+
+       imsi = ofono_sim_get_imsi(netreg->sim);
+       if (imsi == NULL)
+               return;
+
+       netreg->settings = storage_open(imsi, SETTINGS_STORE);
+
+       if (netreg->settings == NULL)
+               return;
+
+       netreg->imsi = g_strdup(imsi);
+
+       strmode = g_key_file_get_string(netreg->settings, SETTINGS_GROUP,
+                                       "Mode", NULL);
+
+       if (strmode == NULL)
+               upgrade = TRUE;
+       else if (g_str_equal(strmode, "auto"))
+               netreg->mode = NETWORK_REGISTRATION_MODE_AUTO;
+       else if (g_str_equal(strmode, "manual"))
+               netreg->mode = NETWORK_REGISTRATION_MODE_MANUAL;
+       else {
+               int mode;
+
+               mode = g_key_file_get_integer(netreg->settings, SETTINGS_GROUP,
+                                               "Mode", NULL);
+
+               switch (mode) {
+               case NETWORK_REGISTRATION_MODE_AUTO:
+               case NETWORK_REGISTRATION_MODE_MANUAL:
+                       netreg->mode = mode;
+                       break;
+               }
+
+               upgrade = TRUE;
+       }
+
+       g_free(strmode);
+
+       if (upgrade == FALSE)
+               return;
+
+       if (netreg->mode == NETWORK_REGISTRATION_MODE_MANUAL)
+               strmode = "manual";
+       else
+               strmode = "auto";
+
+       g_key_file_set_string(netreg->settings, SETTINGS_GROUP,
+                               "Mode", strmode);
+}
+
+static void sim_pnn_opl_changed(int id, void *userdata)
+{
+       struct ofono_netreg *netreg = userdata;
+       GSList *l;
+
+       if (netreg->flags & NETWORK_REGISTRATION_FLAG_READING_PNN)
+               return;
+       /*
+        * Free references to structures on the netreg->eons list and
+        * update the operator info on D-bus.  If EFpnn/EFopl read succeeds,
+        * operator info will be updated again, otherwise it won't be
+        * updated again.
+        */
+       for (l = netreg->operator_list; l; l = l->next)
+               set_network_operator_eons_info(l->data, NULL);
+
+       sim_eons_free(netreg->eons);
+       netreg->eons = NULL;
+
+       netreg->flags |= NETWORK_REGISTRATION_FLAG_READING_PNN;
+       ofono_sim_read(netreg->sim_context, SIM_EFPNN_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                       sim_pnn_read_cb, netreg);
+}
+
+static void sim_spdi_changed(int id, void *userdata)
+{
+       struct ofono_netreg *netreg = userdata;
+
+       sim_spdi_free(netreg->spdi);
+       netreg->spdi = NULL;
+
+       if (netreg->current_operator &&
+                       netreg->status == NETWORK_REGISTRATION_STATUS_ROAMING)
+               netreg_emit_operator_display_name(netreg);
+
+       ofono_sim_read(netreg->sim_context, SIM_EFSPDI_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_spdi_read_cb, netreg);
+}
+
+static void emulator_cops_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_netreg *netreg = userdata;
+       struct ofono_error result;
+       int val;
+       char name[17];
+       char buf[32];
+
+       result.error = 0;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_SET:
+               ofono_emulator_request_next_number(req, &val);
+               if (val != 3)
+                       goto fail;
+
+               ofono_emulator_request_next_number(req, &val);
+               if (val != 0)
+                       goto fail;
+
+               result.type = OFONO_ERROR_TYPE_NO_ERROR;
+               ofono_emulator_send_final(em, &result);
+               break;
+
+       case OFONO_EMULATOR_REQUEST_TYPE_QUERY:
+               strncpy(name, get_operator_display_name(netreg), 16);
+               name[16] = '\0';
+               sprintf(buf, "+COPS: %d,0,\"%s\"", netreg->mode, name);
+               ofono_emulator_send_info(em, buf, TRUE);
+               result.type = OFONO_ERROR_TYPE_NO_ERROR;
+               ofono_emulator_send_final(em, &result);
+               break;
+
+       default:
+fail:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               ofono_emulator_send_final(em, &result);
+       };
+}
+
+static void emulator_hfp_init(struct ofono_atom *atom, void *data)
+{
+       struct ofono_netreg *netreg = data;
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       notify_emulator_status(atom, GINT_TO_POINTER(netreg->status));
+       notify_emulator_strength(atom,
+                               GINT_TO_POINTER(netreg->signal_strength));
+
+       ofono_emulator_add_handler(em, "+COPS", emulator_cops_cb, data, NULL);
+}
+
+static void emulator_hfp_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED)
+               emulator_hfp_init(atom, data);
+}
+
+void ofono_netreg_register(struct ofono_netreg *netreg)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(netreg->atom);
+       const char *path = __ofono_atom_get_path(netreg->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_NETWORK_REGISTRATION_INTERFACE,
+                                       network_registration_methods,
+                                       network_registration_signals,
+                                       NULL, netreg, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_NETWORK_REGISTRATION_INTERFACE);
+
+               return;
+       }
+
+       netreg->status_watches = __ofono_watchlist_new(g_free);
+
+       ofono_modem_add_interface(modem, OFONO_NETWORK_REGISTRATION_INTERFACE);
+
+       if (netreg->driver->registration_status != NULL)
+               netreg->driver->registration_status(netreg,
+                                       init_registration_status, netreg);
+
+       netreg->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+       if (netreg->sim != NULL) {
+               /* Assume that if sim atom exists, it is ready */
+               netreg->sim_context = ofono_sim_context_create(netreg->sim);
+
+               netreg_load_settings(netreg);
+
+               netreg->flags |= NETWORK_REGISTRATION_FLAG_READING_PNN;
+               ofono_sim_read(netreg->sim_context, SIM_EFPNN_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               sim_pnn_read_cb, netreg);
+               ofono_sim_add_file_watch(netreg->sim_context, SIM_EFPNN_FILEID,
+                                               sim_pnn_opl_changed, netreg,
+                                               NULL);
+               ofono_sim_add_file_watch(netreg->sim_context, SIM_EFOPL_FILEID,
+                                               sim_pnn_opl_changed, netreg,
+                                               NULL);
+
+               ofono_sim_add_spn_watch(netreg->sim, &netreg->spn_watch,
+                                               spn_read_cb, netreg, NULL);
+
+               if (__ofono_sim_service_available(netreg->sim,
+                               SIM_UST_SERVICE_PROVIDER_DISPLAY_INFO,
+                               SIM_SST_SERVICE_PROVIDER_DISPLAY_INFO)) {
+                       ofono_sim_read(netreg->sim_context, SIM_EFSPDI_FILEID,
+                                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                                       sim_spdi_read_cb, netreg);
+
+                       ofono_sim_add_file_watch(netreg->sim_context,
+                                                       SIM_EFSPDI_FILEID,
+                                                       sim_spdi_changed,
+                                                       netreg, NULL);
+               }
+       }
+
+       __ofono_atom_register(netreg->atom, netreg_unregister);
+
+       netreg->hfp_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                       emulator_hfp_watch, netreg, NULL);
+}
+
+void ofono_netreg_remove(struct ofono_netreg *netreg)
+{
+       __ofono_atom_free(netreg->atom);
+}
+
+void ofono_netreg_set_data(struct ofono_netreg *netreg, void *data)
+{
+       netreg->driver_data = data;
+}
+
+void *ofono_netreg_get_data(struct ofono_netreg *netreg)
+{
+       return netreg->driver_data;
+}
diff --git a/src/ofono.conf b/src/ofono.conf
new file mode 100644 (file)
index 0000000..8a83cd0
--- /dev/null
@@ -0,0 +1,27 @@
+<!-- This configuration file specifies the required security policies
+     for oFono core daemon to work. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- ../system.conf have denied everything, so we just punch some holes -->
+
+  <policy user="root">
+    <allow own="org.ofono"/>
+    <allow send_destination="org.ofono"/>
+    <allow send_interface="org.ofono.SimToolkitAgent"/>
+    <allow send_interface="org.ofono.PushNotificationAgent"/>
+    <allow send_interface="org.ofono.SmartMessagingAgent"/>
+    <allow send_interface="org.ofono.PositioningRequestAgent"/>
+  </policy>
+
+  <policy at_console="true">
+    <allow send_destination="org.ofono"/>
+  </policy>
+
+  <policy context="default">
+    <deny send_destination="org.ofono"/>
+  </policy>
+
+</busconfig>
diff --git a/src/ofono.h b/src/ofono.h
new file mode 100644 (file)
index 0000000..a42e153
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+
+#include <ofono/types.h>
+
+void __ofono_exit(void);
+
+int __ofono_manager_init(void);
+void __ofono_manager_cleanup(void);
+
+void __ofono_modem_shutdown(void);
+
+#include <ofono/log.h>
+
+int __ofono_log_init(const char *program, const char *debug,
+                                               ofono_bool_t detach);
+void __ofono_log_cleanup(void);
+void __ofono_log_enable(struct ofono_debug_desc *start,
+                                       struct ofono_debug_desc *stop);
+
+#include <ofono/dbus.h>
+
+int __ofono_dbus_init(DBusConnection *conn);
+void __ofono_dbus_cleanup(void);
+
+DBusMessage *__ofono_error_invalid_args(DBusMessage *msg);
+DBusMessage *__ofono_error_invalid_format(DBusMessage *msg);
+DBusMessage *__ofono_error_not_implemented(DBusMessage *msg);
+DBusMessage *__ofono_error_failed(DBusMessage *msg);
+DBusMessage *__ofono_error_busy(DBusMessage *msg);
+DBusMessage *__ofono_error_not_found(DBusMessage *msg);
+DBusMessage *__ofono_error_not_active(DBusMessage *msg);
+DBusMessage *__ofono_error_not_supported(DBusMessage *msg);
+DBusMessage *__ofono_error_not_available(DBusMessage *msg);
+DBusMessage *__ofono_error_timed_out(DBusMessage *msg);
+DBusMessage *__ofono_error_sim_not_ready(DBusMessage *msg);
+DBusMessage *__ofono_error_in_use(DBusMessage *msg);
+DBusMessage *__ofono_error_not_attached(DBusMessage *msg);
+DBusMessage *__ofono_error_attach_in_progress(DBusMessage *msg);
+DBusMessage *__ofono_error_not_registered(DBusMessage *msg);
+DBusMessage *__ofono_error_canceled(DBusMessage *msg);
+DBusMessage *__ofono_error_access_denied(DBusMessage *msg);
+DBusMessage *__ofono_error_emergency_active(DBusMessage *msg);
+
+void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply);
+
+gboolean __ofono_dbus_valid_object_path(const char *path);
+
+struct ofono_watchlist_item {
+       unsigned int id;
+       void *notify;
+       void *notify_data;
+       ofono_destroy_func destroy;
+};
+
+struct ofono_watchlist {
+       int next_id;
+       GSList *items;
+       ofono_destroy_func destroy;
+};
+
+struct ofono_watchlist *__ofono_watchlist_new(ofono_destroy_func destroy);
+unsigned int __ofono_watchlist_add_item(struct ofono_watchlist *watchlist,
+                                       struct ofono_watchlist_item *item);
+gboolean __ofono_watchlist_remove_item(struct ofono_watchlist *watchlist,
+                                       unsigned int id);
+void __ofono_watchlist_free(struct ofono_watchlist *watchlist);
+
+#include <ofono/plugin.h>
+
+int __ofono_plugin_init(const char *pattern, const char *exclude);
+void __ofono_plugin_cleanup(void);
+
+#include <ofono/modem.h>
+
+typedef void (*ofono_modem_foreach_func)(struct ofono_modem *modem,
+                                               void *data);
+void __ofono_modem_foreach(ofono_modem_foreach_func cb, void *userdata);
+
+unsigned int __ofono_modem_callid_next(struct ofono_modem *modem);
+void __ofono_modem_callid_hold(struct ofono_modem *modem, int id);
+void __ofono_modem_callid_release(struct ofono_modem *modem, int id);
+void __ofono_modem_append_properties(struct ofono_modem *modem,
+                                               DBusMessageIter *dict);
+
+struct ofono_atom;
+
+enum ofono_atom_type {
+       OFONO_ATOM_TYPE_DEVINFO,
+       OFONO_ATOM_TYPE_CALL_BARRING,
+       OFONO_ATOM_TYPE_CALL_FORWARDING,
+       OFONO_ATOM_TYPE_CALL_METER,
+       OFONO_ATOM_TYPE_CALL_SETTINGS,
+       OFONO_ATOM_TYPE_NETREG,
+       OFONO_ATOM_TYPE_PHONEBOOK,
+       OFONO_ATOM_TYPE_SMS,
+       OFONO_ATOM_TYPE_SIM,
+       OFONO_ATOM_TYPE_USSD,
+       OFONO_ATOM_TYPE_VOICECALL,
+       OFONO_ATOM_TYPE_HISTORY,
+       OFONO_ATOM_TYPE_SSN,
+       OFONO_ATOM_TYPE_MESSAGE_WAITING,
+       OFONO_ATOM_TYPE_CBS,
+       OFONO_ATOM_TYPES_CALL_VOLUME,
+       OFONO_ATOM_TYPE_GPRS,
+       OFONO_ATOM_TYPE_GPRS_CONTEXT,
+       OFONO_ATOM_TYPE_RADIO_SETTINGS,
+       OFONO_ATOM_TYPE_AUDIO_SETTINGS,
+       OFONO_ATOM_TYPE_STK,
+       OFONO_ATOM_TYPE_NETTIME,
+       OFONO_ATOM_TYPE_CTM,
+       OFONO_ATOM_TYPE_CDMA_VOICECALL_MANAGER,
+       OFONO_ATOM_TYPE_CDMA_CONNMAN,
+       OFONO_ATOM_TYPE_SIM_AUTH,
+       OFONO_ATOM_TYPE_EMULATOR_DUN,
+       OFONO_ATOM_TYPE_EMULATOR_HFP,
+       OFONO_ATOM_TYPE_LOCATION_REPORTING,
+       OFONO_ATOM_TYPE_GNSS,
+       OFONO_ATOM_TYPE_CDMA_SMS,
+       OFONO_ATOM_TYPE_CDMA_NETREG,
+       OFONO_ATOM_TYPE_HANDSFREE,
+};
+
+enum ofono_atom_watch_condition {
+       OFONO_ATOM_WATCH_CONDITION_REGISTERED,
+       OFONO_ATOM_WATCH_CONDITION_UNREGISTERED
+};
+
+typedef void (*ofono_atom_watch_func)(struct ofono_atom *atom,
+                                       enum ofono_atom_watch_condition cond,
+                                       void *data);
+
+typedef void (*ofono_atom_func)(struct ofono_atom *atom, void *data);
+
+struct ofono_atom *__ofono_modem_add_atom(struct ofono_modem *modem,
+                                       enum ofono_atom_type type,
+                                       void (*destruct)(struct ofono_atom *),
+                                       void *data);
+
+struct ofono_atom *__ofono_modem_add_atom_offline(struct ofono_modem *modem,
+                                       enum ofono_atom_type type,
+                                       void (*destruct)(struct ofono_atom *),
+                                       void *data);
+
+struct ofono_atom *__ofono_modem_find_atom(struct ofono_modem *modem,
+                                               enum ofono_atom_type type);
+
+void __ofono_modem_foreach_atom(struct ofono_modem *modem,
+                               enum ofono_atom_type type,
+                               ofono_atom_func callback, void *data);
+
+void __ofono_modem_foreach_registered_atom(struct ofono_modem *modem,
+                                               enum ofono_atom_type type,
+                                               ofono_atom_func callback,
+                                               void *data);
+
+void *__ofono_atom_get_data(struct ofono_atom *atom);
+const char *__ofono_atom_get_path(struct ofono_atom *atom);
+struct ofono_modem *__ofono_atom_get_modem(struct ofono_atom *atom);
+
+#define __ofono_atom_find(enum_type, modem)                    \
+({                                                             \
+       struct ofono_atom *atom =                               \
+               __ofono_modem_find_atom(modem, enum_type);      \
+                                                               \
+       atom ? __ofono_atom_get_data(atom) : NULL;              \
+})
+
+void __ofono_atom_register(struct ofono_atom *atom,
+                               void (*unregister)(struct ofono_atom *));
+void __ofono_atom_unregister(struct ofono_atom *atom);
+
+gboolean __ofono_atom_get_registered(struct ofono_atom *atom);
+
+unsigned int __ofono_modem_add_atom_watch(struct ofono_modem *modem,
+                                       enum ofono_atom_type type,
+                                       ofono_atom_watch_func notify,
+                                       void *data,
+                                       ofono_destroy_func destroy);
+gboolean __ofono_modem_remove_atom_watch(struct ofono_modem *modem,
+                                               unsigned int id);
+
+void __ofono_atom_free(struct ofono_atom *atom);
+
+typedef void (*ofono_modemwatch_cb_t)(struct ofono_modem *modem,
+                                       gboolean added, void *data);
+void __ofono_modemwatch_init(void);
+void __ofono_modemwatch_cleanup(void);
+unsigned int __ofono_modemwatch_add(ofono_modemwatch_cb_t cb, void *user,
+                                       ofono_destroy_func destroy);
+gboolean __ofono_modemwatch_remove(unsigned int id);
+
+typedef void (*ofono_modem_online_notify_func)(struct ofono_modem *modem,
+                                               ofono_bool_t online,
+                                               void *data);
+unsigned int __ofono_modem_add_online_watch(struct ofono_modem *modem,
+                                       ofono_modem_online_notify_func notify,
+                                       void *data, ofono_destroy_func destroy);
+void __ofono_modem_remove_online_watch(struct ofono_modem *modem,
+                                       unsigned int id);
+
+typedef void (*ofono_modem_powered_notify_func)(struct ofono_modem *modem,
+                                               ofono_bool_t powered,
+                                               void *data);
+
+unsigned int __ofono_modem_add_powered_watch(struct ofono_modem *modem,
+                                       ofono_modem_online_notify_func notify,
+                                       void *data, ofono_destroy_func destroy);
+void __ofono_modem_remove_powered_watch(struct ofono_modem *modem,
+                                       unsigned int id);
+
+void __ofono_modem_sim_reset(struct ofono_modem *modem);
+
+void __ofono_modem_inc_emergency_mode(struct ofono_modem *modem);
+void __ofono_modem_dec_emergency_mode(struct ofono_modem *modem);
+
+#include <ofono/call-barring.h>
+
+gboolean __ofono_call_barring_is_busy(struct ofono_call_barring *cb);
+
+#include <ofono/call-forwarding.h>
+
+gboolean __ofono_call_forwarding_is_busy(struct ofono_call_forwarding *cf);
+
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+
+gboolean __ofono_call_settings_is_busy(struct ofono_call_settings *cs);
+
+#include <ofono/cbs.h>
+#include <ofono/devinfo.h>
+#include <ofono/phonebook.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/radio-settings.h>
+#include <ofono/audio-settings.h>
+#include <ofono/ctm.h>
+#include <ofono/location-reporting.h>
+
+#include <ofono/voicecall.h>
+
+enum ofono_voicecall_interaction {
+       OFONO_VOICECALL_INTERACTION_NONE        = 0,
+       OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD = 1,
+       OFONO_VOICECALL_INTERACTION_DISCONNECT  = 2,
+};
+
+typedef void (*ofono_voicecall_dial_cb_t)(struct ofono_call *call, void *data);
+typedef void (*ofono_voicecall_tone_cb_t)(int error, void *data);
+
+ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc,
+                                       enum ofono_voicecall_interaction type);
+
+int __ofono_voicecall_dial(struct ofono_voicecall *vc,
+                               const char *addr, int addr_type,
+                               const char *message, unsigned char icon_id,
+                               enum ofono_voicecall_interaction interaction,
+                               ofono_voicecall_dial_cb_t cb, void *user_data);
+void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc);
+
+void __ofono_voicecall_set_alpha_and_icon_id(struct ofono_voicecall *vc,
+                                               const char *addr, int addr_type,
+                                               const char *message,
+                                               unsigned char icon_id);
+void __ofono_voicecall_clear_alpha_and_icon_id(struct ofono_voicecall *vc);
+
+int __ofono_voicecall_tone_send(struct ofono_voicecall *vc,
+                               const char *tone_str,
+                               ofono_voicecall_tone_cb_t cb, void *user_data);
+void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id);
+
+struct ofono_call *__ofono_voicecall_find_call_with_status(
+                               struct ofono_voicecall *vc, int status);
+
+#include <ofono/sms.h>
+
+struct sms;
+
+enum ofono_sms_submit_flag {
+       OFONO_SMS_SUBMIT_FLAG_REQUEST_SR =      0x1,
+       OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY =  0x2,
+       OFONO_SMS_SUBMIT_FLAG_RETRY =           0x4,
+       OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS =     0x8,
+       OFONO_SMS_SUBMIT_FLAG_REUSE_UUID =      0x10,
+};
+
+typedef void (*ofono_sms_txq_submit_cb_t)(gboolean ok, void *data);
+typedef void (*ofono_sms_txq_queued_cb_t)(struct ofono_sms *sms,
+                                               const struct ofono_uuid *uuid,
+                                               void *data);
+typedef void (*ofono_sms_text_notify_cb_t)(const char *from,
+                                               const struct tm *remote,
+                                               const struct tm *local,
+                                               const char *text,
+                                               void *data);
+typedef void (*ofono_sms_datagram_notify_cb_t)(const char *from,
+                                               const struct tm *remote,
+                                               const struct tm *local,
+                                               int dst, int src,
+                                               const unsigned char *buffer,
+                                               unsigned int len,
+                                               void *data);
+
+int __ofono_sms_txq_submit(struct ofono_sms *sms, GSList *list,
+                               unsigned int flags, struct ofono_uuid *uuid,
+                               ofono_sms_txq_queued_cb_t, void *data);
+
+int __ofono_sms_txq_set_submit_notify(struct ofono_sms *sms,
+                                       struct ofono_uuid *uuid,
+                                       ofono_sms_txq_submit_cb_t cb,
+                                       void *data,
+                                       ofono_destroy_func destroy);
+
+int __ofono_sms_txq_cancel(struct ofono_sms *sms,
+                               const struct ofono_uuid *uuid);
+
+const char *__ofono_sms_message_path_from_uuid(struct ofono_sms *sms,
+                                               const struct ofono_uuid *uuid);
+
+unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms,
+                                       ofono_sms_text_notify_cb_t cb,
+                                       void *data, ofono_destroy_func destroy);
+gboolean __ofono_sms_text_watch_remove(struct ofono_sms *sms,
+                                       unsigned int id);
+
+unsigned int __ofono_sms_datagram_watch_add(struct ofono_sms *sms,
+                                       ofono_sms_datagram_notify_cb_t cb,
+                                       int dst, int src, void *data,
+                                       ofono_destroy_func destroy);
+gboolean __ofono_sms_datagram_watch_remove(struct ofono_sms *sms,
+                                       unsigned int id);
+
+unsigned short __ofono_sms_get_next_ref(struct ofono_sms *sms);
+
+#include <ofono/sim.h>
+
+ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim,
+                                               int ust_service,
+                                               int sst_service);
+ofono_bool_t __ofono_sim_cphs_service_available(struct ofono_sim *sim,
+                                               int cphs_service);
+
+ofono_bool_t __ofono_is_valid_sim_pin(const char *pin,
+                                       enum ofono_sim_password_type type);
+
+ofono_bool_t __ofono_is_valid_net_pin(const char *pin);
+
+void __ofono_sim_refresh(struct ofono_sim *sim, GSList *file_list,
+                               ofono_bool_t full_file_change,
+                               ofono_bool_t naa_init);
+
+void __ofono_sim_recheck_pin(struct ofono_sim *sim);
+
+#include <ofono/stk.h>
+
+typedef void (*__ofono_sms_sim_download_cb_t)(ofono_bool_t ok,
+                                               const unsigned char *tp_ud,
+                                               int len, void *data);
+
+struct cbs;
+void __ofono_cbs_sim_download(struct ofono_stk *stk, const struct cbs *msg);
+
+struct sms;
+int __ofono_sms_sim_download(struct ofono_stk *stk, const struct sms *msg,
+                               __ofono_sms_sim_download_cb_t cb, void *data);
+
+#include <ofono/ussd.h>
+
+typedef gboolean (*ofono_ussd_ssc_cb_t)(int type,
+                                       const char *sc,
+                                       const char *sia, const char *sib,
+                                       const char *sic, const char *dn,
+                                       DBusMessage *msg, void *data);
+
+typedef gboolean (*ofono_ussd_passwd_cb_t)(const char *sc,
+                                       const char *old, const char *new,
+                                       DBusMessage *msg, void *data);
+
+typedef void (*ofono_ussd_request_cb_t)(int error, int dcs,
+                                       const unsigned char *pdu, int len,
+                                       void *data);
+
+gboolean __ofono_ussd_ssc_register(struct ofono_ussd *ussd, const char *sc,
+                                       ofono_ussd_ssc_cb_t cb, void *data,
+                                       ofono_destroy_func destroy);
+void __ofono_ussd_ssc_unregister(struct ofono_ussd *ussd, const char *sc);
+
+gboolean __ofono_ussd_passwd_register(struct ofono_ussd *ussd, const char *sc,
+                                       ofono_ussd_passwd_cb_t cb, void *data,
+                                       ofono_destroy_func destroy);
+void __ofono_ussd_passwd_unregister(struct ofono_ussd *ussd, const char *sc);
+gboolean __ofono_ussd_is_busy(struct ofono_ussd *ussd);
+
+int __ofono_ussd_initiate(struct ofono_ussd *ussd, int dcs,
+                       const unsigned char *pdu, int len,
+                       ofono_ussd_request_cb_t cb, void *user_data);
+void __ofono_ussd_initiate_cancel(struct ofono_ussd *ussd);
+
+#include <ofono/netreg.h>
+
+typedef void (*ofono_netreg_status_notify_cb_t)(int status, int lac, int ci,
+                       int tech, const char *mcc, const char *mnc,
+                       void *data);
+
+unsigned int __ofono_netreg_add_status_watch(struct ofono_netreg *netreg,
+                               ofono_netreg_status_notify_cb_t cb,
+                               void *data, ofono_destroy_func destroy);
+
+gboolean __ofono_netreg_remove_status_watch(struct ofono_netreg *netreg,
+                                               unsigned int id);
+
+void __ofono_netreg_set_base_station_name(struct ofono_netreg *netreg,
+                                               const char *name);
+
+#include <ofono/history.h>
+
+void __ofono_history_probe_drivers(struct ofono_modem *modem);
+
+void __ofono_history_call_ended(struct ofono_modem *modem,
+                               const struct ofono_call *call,
+                               time_t start, time_t end);
+
+void __ofono_history_call_missed(struct ofono_modem *modem,
+                               const struct ofono_call *call, time_t when);
+
+void __ofono_history_sms_received(struct ofono_modem *modem,
+                                       const struct ofono_uuid *uuid,
+                                       const char *from,
+                                       const struct tm *remote,
+                                       const struct tm *local,
+                                       const char *text);
+
+void __ofono_history_sms_send_pending(struct ofono_modem *modem,
+                                       const struct ofono_uuid *uuid,
+                                       const char *to,
+                                       time_t when, const char *text);
+
+void __ofono_history_sms_send_status(struct ofono_modem *modem,
+                                       const struct ofono_uuid *uuid,
+                                       time_t when,
+                                       enum ofono_history_sms_status status);
+
+#include <ofono/message-waiting.h>
+
+struct sms;
+
+void __ofono_message_waiting_mwi(struct ofono_message_waiting *mw,
+                               struct sms *sms, gboolean *out_discard);
+
+const struct ofono_phone_number *__ofono_message_waiting_get_mbdn(
+                                       struct ofono_message_waiting *mw,
+                                       unsigned int index);
+
+#include <ofono/nettime.h>
+
+void __ofono_nettime_probe_drivers(struct ofono_modem *modem);
+
+void __ofono_nettime_info_received(struct ofono_modem *modem,
+                                       struct ofono_network_time *info);
+
+#include <ofono/cdma-voicecall.h>
+#include <ofono/cdma-connman.h>
+#include <ofono/sim-auth.h>
+
+#include <ofono/gprs-provision.h>
+ofono_bool_t __ofono_gprs_provision_get_settings(const char *mcc,
+                               const char *mnc, const char *spn,
+                               struct ofono_gprs_provision_data **settings,
+                               int *count);
+void __ofono_gprs_provision_free_settings(
+                               struct ofono_gprs_provision_data *settings,
+                               int count);
+
+#include <ofono/emulator.h>
+#include <ofono/gnss.h>
+#include <ofono/cdma-sms.h>
+#include <ofono/cdma-netreg.h>
+
+#include <ofono/cdma-provision.h>
+ofono_bool_t __ofono_cdma_provision_get_name(const char *sid, char **name);
+
+#include <ofono/private-network.h>
+
+void __ofono_private_network_release(int id);
+ofono_bool_t __ofono_private_network_request(ofono_private_network_cb_t cb,
+                                               int *id, void *data);
diff --git a/src/ofono.service.in b/src/ofono.service.in
new file mode 100644 (file)
index 0000000..f4531a8
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Telephony service
+After=syslog.target
+
+[Service]
+Type=dbus
+BusName=org.ofono
+ExecStart=@prefix@/sbin/ofonod -n
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/ofono.ver b/src/ofono.ver
new file mode 100644 (file)
index 0000000..3dce6f3
--- /dev/null
@@ -0,0 +1,7 @@
+{
+       global:
+               ofono_*;
+               g_dbus_*;
+       local:
+               *;
+};
diff --git a/src/phonebook.c b/src/phonebook.c
new file mode 100644 (file)
index 0000000..6baf7bb
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+
+#define LEN_MAX 128
+#define TYPE_INTERNATIONAL 145
+
+#define PHONEBOOK_FLAG_CACHED 0x1
+
+static GSList *g_drivers = NULL;
+
+enum phonebook_number_type {
+       TEL_TYPE_HOME,
+       TEL_TYPE_MOBILE,
+       TEL_TYPE_FAX,
+       TEL_TYPE_WORK,
+       TEL_TYPE_OTHER,
+};
+
+struct ofono_phonebook {
+       DBusMessage *pending;
+       int storage_index; /* go through all supported storage */
+       int flags;
+       GString *vcards; /* entries with vcard 3.0 format */
+       GSList *merge_list; /* cache the entries that may need a merge */
+       const struct ofono_phonebook_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+struct phonebook_number {
+       char *number;
+       int type;
+       enum phonebook_number_type category;
+};
+
+struct phonebook_person {
+       GSList *number_list; /* one person may have more than one numbers */
+       char *text;
+       int hidden;
+       char *group;
+       char *email;
+       char *sip_uri;
+};
+
+static const char *storage_support[] = { "SM", "ME", NULL };
+static void export_phonebook(struct ofono_phonebook *pb);
+
+/* according to RFC 2425, the output string may need folding */
+static void vcard_printf(GString *str, const char *fmt, ...)
+{
+       char buf[1024];
+       va_list ap;
+       int len_temp, line_number, i;
+       unsigned int line_delimit = 75;
+
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+
+       line_number = strlen(buf) / line_delimit + 1;
+
+       for (i = 0; i < line_number; i++) {
+               len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
+               g_string_append_len(str,  buf + line_delimit * i, len_temp);
+               if (i != line_number - 1)
+                       g_string_append(str, "\r\n ");
+       }
+
+       g_string_append(str, "\r\n");
+}
+
+/*
+ * According to RFC 2426, we need escape following characters:
+ * '\n', '\r', ';', ',', '\'.
+ */
+static void add_slash(char *dest, const char *src, int len_max, int len)
+{
+       int i, j;
+
+       for (i = 0, j = 0; i < len && j < len_max; i++, j++) {
+               switch (src[i]) {
+               case '\n':
+                       dest[j++] = '\\';
+                       dest[j] = 'n';
+                       break;
+               case '\r':
+                       dest[j++] = '\\';
+                       dest[j] = 'r';
+                       break;
+               case '\\':
+               case ';':
+               case ',':
+                       dest[j++] = '\\';
+               default:
+                       dest[j] = src[i];
+                       break;
+               }
+       }
+       dest[j] = 0;
+       return;
+}
+
+static void vcard_printf_begin(GString *vcards)
+{
+       vcard_printf(vcards, "BEGIN:VCARD");
+       vcard_printf(vcards, "VERSION:3.0");
+}
+
+static void vcard_printf_text(GString *vcards, const char *text)
+{
+       char field[LEN_MAX];
+       add_slash(field, text, LEN_MAX, strlen(text));
+       vcard_printf(vcards, "FN:%s", field);
+}
+
+static void vcard_printf_number(GString *vcards, const char *number, int type,
+                                       enum phonebook_number_type category)
+{
+       char *pref = "", *intl = "", *category_string = "";
+       char buf[128];
+
+       if (number == NULL || !strlen(number) || !type)
+               return;
+
+       switch (category) {
+       case TEL_TYPE_HOME:
+               category_string = "HOME,VOICE";
+               break;
+       case TEL_TYPE_MOBILE:
+               category_string = "CELL,VOICE";
+               break;
+       case TEL_TYPE_FAX:
+               category_string = "FAX";
+               break;
+       case TEL_TYPE_WORK:
+               category_string = "WORK,VOICE";
+               break;
+       case TEL_TYPE_OTHER:
+               category_string = "VOICE";
+               break;
+       }
+
+       if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
+               intl = "+";
+
+       snprintf(buf, sizeof(buf), "TEL;TYPE=\%s%s:\%s\%s", pref,
+                       category_string, intl, number);
+       vcard_printf(vcards, buf, number);
+}
+
+static void vcard_printf_group(GString *vcards,        const char *group)
+{
+       int len = 0;
+
+       if (group)
+               len = strlen(group);
+
+       if (len) {
+               char field[LEN_MAX];
+               add_slash(field, group, LEN_MAX, len);
+               vcard_printf(vcards, "CATEGORIES:%s", field);
+       }
+}
+
+static void vcard_printf_email(GString *vcards, const char *email)
+{
+       int len = 0;
+
+       if (email)
+               len = strlen(email);
+
+       if (len) {
+               char field[LEN_MAX];
+               add_slash(field, email, LEN_MAX, len);
+               vcard_printf(vcards,
+                               "EMAIL;TYPE=INTERNET:%s", field);
+       }
+}
+
+static void vcard_printf_sip_uri(GString *vcards, const char *sip_uri)
+{
+       int len = 0;
+
+       if (sip_uri)
+               len = strlen(sip_uri);
+
+       if (len) {
+               char field[LEN_MAX];
+               add_slash(field, sip_uri, LEN_MAX, len);
+               vcard_printf(vcards, "IMPP;TYPE=SIP:%s", field);
+       }
+}
+
+static void vcard_printf_end(GString *vcards)
+{
+       vcard_printf(vcards, "END:VCARD");
+       vcard_printf(vcards, "");
+}
+
+static void print_number(struct phonebook_number *pn, GString *vcards)
+{
+       vcard_printf_number(vcards, pn->number, pn->type, pn->category);
+}
+
+static void destroy_number(struct phonebook_number *pn)
+{
+       g_free(pn->number);
+       g_free(pn);
+}
+
+static void print_merged_entry(struct phonebook_person *person, GString *vcards)
+{
+       vcard_printf_begin(vcards);
+       vcard_printf_text(vcards, person->text);
+
+       g_slist_foreach(person->number_list, (GFunc) print_number, vcards);
+
+       vcard_printf_group(vcards, person->group);
+       vcard_printf_email(vcards, person->email);
+       vcard_printf_sip_uri(vcards, person->sip_uri);
+       vcard_printf_end(vcards);
+}
+
+static void destroy_merged_entry(struct phonebook_person *person)
+{
+       g_free(person->text);
+       g_free(person->group);
+       g_free(person->email);
+       g_free(person->sip_uri);
+
+       g_slist_foreach(person->number_list, (GFunc) destroy_number, NULL);
+       g_slist_free(person->number_list);
+
+       g_free(person);
+}
+
+static DBusMessage *generate_export_entries_reply(struct ofono_phonebook *pb,
+                                                       DBusMessage *msg)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, pb->vcards);
+
+       return reply;
+}
+
+static gboolean need_merge(const char *text)
+{
+       int len;
+       char c;
+
+       if (text == NULL)
+               return FALSE;
+
+       len = strlen(text);
+
+       if (len < 2)
+               return FALSE;
+
+       c = tolower(text[len-1]);
+
+       if ((text[len-2] == '/') &&
+                       ((c == 'w') || (c == 'h') || (c == 'm') || (c == 'o')))
+               return TRUE;
+
+       return FALSE;
+}
+
+static void merge_field_generic(char **str1, const char *str2)
+{
+       if ((*str1 == NULL) && (str2 != NULL) && (strlen(str2) != 0))
+               *str1 = g_strdup(str2);
+}
+
+static void merge_field_number(GSList **l, const char *number, int type, char c)
+{
+       struct phonebook_number *pn = g_new0(struct phonebook_number, 1);
+       enum phonebook_number_type category;
+
+       pn->number = g_strdup(number);
+       pn->type = type;
+       switch (tolower(c)) {
+       case 'w':
+               category = TEL_TYPE_WORK;
+               break;
+       case 'h':
+               category = TEL_TYPE_HOME;
+               break;
+       case 'm':
+               category = TEL_TYPE_MOBILE;
+               break;
+       case 'f':
+               category = TEL_TYPE_FAX;
+               break;
+       case 'o':
+       default:
+               category = TEL_TYPE_OTHER;
+               break;
+       }
+       pn->category = category;
+       *l = g_slist_append(*l, pn);
+}
+
+void ofono_phonebook_entry(struct ofono_phonebook *phonebook, int index,
+                               const char *number, int type,
+                               const char *text, int hidden,
+                               const char *group,
+                               const char *adnumber, int adtype,
+                               const char *secondtext, const char *email,
+                               const char *sip_uri, const char *tel_uri)
+{
+       /* There's really nothing to do */
+       if ((number == NULL || number[0] == '\0') &&
+                       (text == NULL || text[0] == '\0'))
+               return;
+
+       /*
+        * We need to collect all the entries that belong to one person,
+        * so that only one vCard will be generated at last.
+        * Entries only differ with '/w', '/h', '/m', etc. in field text
+        * are deemed as entries of one person.
+        */
+       if (need_merge(text)) {
+               GSList *l;
+               size_t len_text = strlen(text) - 2;
+               struct phonebook_person *person;
+
+               for (l = phonebook->merge_list; l; l = l->next) {
+                       person = l->data;
+                       if (!strncmp(text, person->text, len_text) &&
+                                       (strlen(person->text) == len_text))
+                               break;
+               }
+
+               if (l == NULL) {
+                       person = g_new0(struct phonebook_person, 1);
+                       phonebook->merge_list =
+                               g_slist_prepend(phonebook->merge_list, person);
+                       person->text = g_strndup(text, len_text);
+               }
+
+               merge_field_number(&(person->number_list), number, type,
+                                       text[len_text + 1]);
+               merge_field_number(&(person->number_list), adnumber, adtype,
+                                       text[len_text + 1]);
+
+               merge_field_generic(&(person->group), group);
+               merge_field_generic(&(person->email), email);
+               merge_field_generic(&(person->sip_uri), sip_uri);
+
+               return;
+       }
+
+       vcard_printf_begin(phonebook->vcards);
+
+       if (text == NULL || text[0] == '\0')
+               vcard_printf_text(phonebook->vcards, number);
+       else
+               vcard_printf_text(phonebook->vcards, text);
+
+       vcard_printf_number(phonebook->vcards, number, type, TEL_TYPE_OTHER);
+       vcard_printf_number(phonebook->vcards, adnumber, adtype,
+                               TEL_TYPE_OTHER);
+       vcard_printf_group(phonebook->vcards, group);
+       vcard_printf_email(phonebook->vcards, email);
+       vcard_printf_sip_uri(phonebook->vcards, sip_uri);
+       vcard_printf_end(phonebook->vcards);
+}
+
+static void export_phonebook_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_phonebook *phonebook = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               ofono_error("export_entries_one_storage_cb with %s failed",
+                               storage_support[phonebook->storage_index]);
+
+       /* convert the collected entries that are already merged to vcard */
+       phonebook->merge_list = g_slist_reverse(phonebook->merge_list);
+       g_slist_foreach(phonebook->merge_list, (GFunc) print_merged_entry,
+                               phonebook->vcards);
+       g_slist_foreach(phonebook->merge_list, (GFunc) destroy_merged_entry,
+                               NULL);
+       g_slist_free(phonebook->merge_list);
+       phonebook->merge_list = NULL;
+
+       phonebook->storage_index++;
+       export_phonebook(phonebook);
+       return;
+}
+
+static void export_phonebook(struct ofono_phonebook *phonebook)
+{
+       DBusMessage *reply;
+       const char *pb = storage_support[phonebook->storage_index];
+
+       if (pb) {
+               phonebook->driver->export_entries(phonebook, pb,
+                                               export_phonebook_cb, phonebook);
+               return;
+       }
+
+       reply = generate_export_entries_reply(phonebook, phonebook->pending);
+       if (reply == NULL) {
+               dbus_message_unref(phonebook->pending);
+               return;
+       }
+
+       __ofono_dbus_pending_reply(&phonebook->pending, reply);
+       phonebook->flags |= PHONEBOOK_FLAG_CACHED;
+}
+
+static DBusMessage *import_entries(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_phonebook *phonebook = data;
+       DBusMessage *reply;
+
+       if (phonebook->pending) {
+               reply = __ofono_error_busy(phonebook->pending);
+               g_dbus_send_message(conn, reply);
+               return NULL;
+       }
+
+       if (phonebook->flags & PHONEBOOK_FLAG_CACHED) {
+               reply = generate_export_entries_reply(phonebook, msg);
+               g_dbus_send_message(conn, reply);
+               return NULL;
+       }
+
+       g_string_set_size(phonebook->vcards, 0);
+       phonebook->storage_index = 0;
+
+       phonebook->pending = dbus_message_ref(msg);
+       export_phonebook(phonebook);
+
+       return NULL;
+}
+
+static GDBusMethodTable phonebook_methods[] = {
+       { "Import",     "",     "s",    import_entries,
+                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable phonebook_signals[] = {
+       { }
+};
+
+int ofono_phonebook_driver_register(const struct ofono_phonebook_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_phonebook_driver_unregister(const struct ofono_phonebook_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void phonebook_unregister(struct ofono_atom *atom)
+{
+       struct ofono_phonebook *pb = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(pb->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(pb->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_PHONEBOOK_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_PHONEBOOK_INTERFACE);
+}
+
+static void phonebook_remove(struct ofono_atom *atom)
+{
+       struct ofono_phonebook *pb = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (pb == NULL)
+               return;
+
+       if (pb->driver && pb->driver->remove)
+               pb->driver->remove(pb);
+
+       g_string_free(pb->vcards, TRUE);
+       g_free(pb);
+}
+
+struct ofono_phonebook *ofono_phonebook_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver, void *data)
+{
+       struct ofono_phonebook *pb;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       pb = g_try_new0(struct ofono_phonebook, 1);
+
+       if (pb == NULL)
+               return NULL;
+
+       pb->vcards = g_string_new(NULL);
+       pb->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_PHONEBOOK,
+                                               phonebook_remove, pb);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_phonebook_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(pb, vendor, data) < 0)
+                       continue;
+
+               pb->driver = drv;
+               break;
+       }
+
+       return pb;
+}
+
+void ofono_phonebook_register(struct ofono_phonebook *pb)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(pb->atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(pb->atom);
+
+       if (!g_dbus_register_interface(conn, path, OFONO_PHONEBOOK_INTERFACE,
+                                       phonebook_methods, phonebook_signals,
+                                       NULL, pb, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_PHONEBOOK_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_PHONEBOOK_INTERFACE);
+
+       __ofono_atom_register(pb->atom, phonebook_unregister);
+}
+
+void ofono_phonebook_remove(struct ofono_phonebook *pb)
+{
+       __ofono_atom_free(pb->atom);
+}
+
+void ofono_phonebook_set_data(struct ofono_phonebook *pb, void *data)
+{
+       pb->driver_data = data;
+}
+
+void *ofono_phonebook_get_data(struct ofono_phonebook *pb)
+{
+       return pb->driver_data;
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644 (file)
index 0000000..2c9c619
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dlfcn.h>
+
+#include <glib.h>
+
+#include "ofono.h"
+
+static GSList *plugins = NULL;
+
+struct ofono_plugin {
+       void *handle;
+       gboolean active;
+       struct ofono_plugin_desc *desc;
+};
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_plugin *plugin1 = a;
+       const struct ofono_plugin *plugin2 = b;
+
+       return plugin2->desc->priority - plugin1->desc->priority;
+}
+
+static gboolean add_plugin(void *handle, struct ofono_plugin_desc *desc)
+{
+       struct ofono_plugin *plugin;
+
+       if (desc->init == NULL)
+               return FALSE;
+
+       if (g_str_equal(desc->version, OFONO_VERSION) == FALSE) {
+               ofono_error("Invalid version %s for %s", desc->version,
+                                                       desc->description);
+               return FALSE;
+       }
+
+       plugin = g_try_new0(struct ofono_plugin, 1);
+       if (plugin == NULL)
+               return FALSE;
+
+       plugin->handle = handle;
+       plugin->active = FALSE;
+       plugin->desc = desc;
+
+       __ofono_log_enable(desc->debug_start, desc->debug_stop);
+
+       plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
+
+       return TRUE;
+}
+
+static gboolean check_plugin(struct ofono_plugin_desc *desc,
+                               char **patterns, char **excludes)
+{
+       if (excludes) {
+               for (; *excludes; excludes++)
+                       if (g_pattern_match_simple(*excludes, desc->name))
+                               break;
+               if (*excludes) {
+                       ofono_info("Excluding %s", desc->description);
+                       return FALSE;
+               }
+       }
+
+       if (patterns) {
+               for (; *patterns; patterns++)
+                       if (g_pattern_match_simple(*patterns, desc->name))
+                               break;
+               if (*patterns == NULL) {
+                       ofono_info("Ignoring %s", desc->description);
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+#include "builtin.h"
+
+int __ofono_plugin_init(const char *pattern, const char *exclude)
+{
+       gchar **patterns = NULL;
+       gchar **excludes = NULL;
+       GSList *list;
+       GDir *dir;
+       const gchar *file;
+       gchar *filename;
+       unsigned int i;
+
+       DBG("");
+
+       if (pattern)
+               patterns = g_strsplit_set(pattern, ":, ", -1);
+
+       if (exclude)
+               excludes = g_strsplit_set(exclude, ":, ", -1);
+
+       for (i = 0; __ofono_builtin[i]; i++) {
+               if (check_plugin(__ofono_builtin[i],
+                                       patterns, excludes) == FALSE)
+                       continue;
+
+               add_plugin(NULL, __ofono_builtin[i]);
+       }
+
+       dir = g_dir_open(PLUGINDIR, 0, NULL);
+       if (dir != NULL) {
+               while ((file = g_dir_read_name(dir)) != NULL) {
+                       void *handle;
+                       struct ofono_plugin_desc *desc;
+
+                       if (g_str_has_prefix(file, "lib") == TRUE ||
+                                       g_str_has_suffix(file, ".so") == FALSE)
+                               continue;
+
+                       filename = g_build_filename(PLUGINDIR, file, NULL);
+
+                       handle = dlopen(filename, RTLD_NOW);
+                       if (handle == NULL) {
+                               ofono_error("Can't load %s: %s",
+                                                       filename, dlerror());
+                               g_free(filename);
+                               continue;
+                       }
+
+                       g_free(filename);
+
+                       desc = dlsym(handle, "ofono_plugin_desc");
+                       if (desc == NULL) {
+                               ofono_error("Can't load symbol: %s",
+                                                               dlerror());
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (check_plugin(desc, patterns, excludes) == FALSE) {
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (add_plugin(handle, desc) == FALSE)
+                               dlclose(handle);
+               }
+
+               g_dir_close(dir);
+       }
+
+       for (list = plugins; list; list = list->next) {
+               struct ofono_plugin *plugin = list->data;
+
+               if (plugin->desc->init() < 0)
+                       continue;
+
+               plugin->active = TRUE;
+       }
+
+       g_strfreev(patterns);
+       g_strfreev(excludes);
+
+       return 0;
+}
+
+void __ofono_plugin_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = plugins; list; list = list->next) {
+               struct ofono_plugin *plugin = list->data;
+
+               if (plugin->active == TRUE && plugin->desc->exit)
+                       plugin->desc->exit();
+
+               if (plugin->handle)
+                       dlclose(plugin->handle);
+
+               g_free(plugin);
+       }
+
+       g_slist_free(plugins);
+}
diff --git a/src/private-network.c b/src/private-network.c
new file mode 100644 (file)
index 0000000..4da68f8
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include "ofono.h"
+
+static GSList *g_drivers = NULL;
+
+void __ofono_private_network_release(int id)
+{
+       GSList *d;
+
+       DBG("");
+
+       for (d = g_drivers; d; d = d->next) {
+               const struct ofono_private_network_driver *driver = d->data;
+
+               if (!driver->release)
+                       continue;
+
+               driver->release(id);
+
+               break;
+       }
+}
+
+ofono_bool_t __ofono_private_network_request(ofono_private_network_cb_t cb,
+                                               int *id, void *data)
+{
+       GSList *d;
+       int uid;
+
+       DBG("");
+
+       for (d = g_drivers; d; d = d->next) {
+               const struct ofono_private_network_driver *driver = d->data;
+
+               if (!driver->request)
+                       continue;
+
+               uid = driver->request(cb, data);
+               if (uid <= 0)
+                       continue;
+
+               *id = uid;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+int ofono_private_network_driver_register(
+                       const struct ofono_private_network_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_private_network_driver_unregister(
+                       const struct ofono_private_network_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
diff --git a/src/radio-settings.c b/src/radio-settings.c
new file mode 100644 (file)
index 0000000..a8637bb
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+#include "common.h"
+
+#define RADIO_SETTINGS_FLAG_CACHED 0x1
+
+static GSList *g_drivers = NULL;
+
+struct ofono_radio_settings {
+       DBusMessage *pending;
+       int flags;
+       enum ofono_radio_access_mode mode;
+       enum ofono_radio_band_gsm band_gsm;
+       enum ofono_radio_band_umts band_umts;
+       ofono_bool_t fast_dormancy;
+       enum ofono_radio_access_mode pending_mode;
+       enum ofono_radio_band_gsm pending_band_gsm;
+       enum ofono_radio_band_umts pending_band_umts;
+       ofono_bool_t fast_dormancy_pending;
+       const struct ofono_radio_settings_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+static const char *radio_access_mode_to_string(enum ofono_radio_access_mode m)
+{
+       switch (m) {
+       case OFONO_RADIO_ACCESS_MODE_ANY:
+               return "any";
+       case OFONO_RADIO_ACCESS_MODE_GSM:
+               return "gsm";
+       case OFONO_RADIO_ACCESS_MODE_UMTS:
+               return "umts";
+       case OFONO_RADIO_ACCESS_MODE_LTE:
+               return "lte";
+       default:
+               return "";
+       }
+}
+
+static gboolean radio_access_mode_from_string(const char *str,
+                                       enum ofono_radio_access_mode *mode)
+
+{
+       if (g_str_equal(str, "any")) {
+               *mode = OFONO_RADIO_ACCESS_MODE_ANY;
+               return TRUE;
+       } else if (g_str_equal(str, "gsm")) {
+               *mode = OFONO_RADIO_ACCESS_MODE_GSM;
+               return TRUE;
+       } else if (g_str_equal(str, "umts")) {
+               *mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+               return TRUE;
+       } else if (g_str_equal(str, "lte")) {
+               *mode = OFONO_RADIO_ACCESS_MODE_LTE;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static const char *radio_band_gsm_to_string(enum ofono_radio_band_gsm band)
+{
+       switch (band) {
+       case OFONO_RADIO_BAND_GSM_ANY:
+               return "any";
+       case OFONO_RADIO_BAND_GSM_850:
+               return "850";
+       case OFONO_RADIO_BAND_GSM_900P:
+               return "900P";
+       case OFONO_RADIO_BAND_GSM_900E:
+               return "900E";
+       case OFONO_RADIO_BAND_GSM_1800:
+               return "1800";
+       case OFONO_RADIO_BAND_GSM_1900:
+               return "1900";
+       }
+
+       return "";
+}
+
+static gboolean radio_band_gsm_from_string(const char *str,
+                                       enum ofono_radio_band_gsm *band)
+{
+       if (g_str_equal(str, "any")) {
+               *band = OFONO_RADIO_BAND_GSM_ANY;
+               return TRUE;
+       } else if (g_str_equal(str, "850")) {
+               *band = OFONO_RADIO_BAND_GSM_850;
+               return TRUE;
+       } else if (g_str_equal(str, "900P")) {
+               *band = OFONO_RADIO_BAND_GSM_900P;
+               return TRUE;
+       } else if (g_str_equal(str, "900E")) {
+               *band = OFONO_RADIO_BAND_GSM_900E;
+               return TRUE;
+       } else if (g_str_equal(str, "1800")) {
+               *band = OFONO_RADIO_BAND_GSM_1800;
+               return TRUE;
+       } else if (g_str_equal(str, "1900")) {
+               *band = OFONO_RADIO_BAND_GSM_1900;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static const char *radio_band_umts_to_string(enum ofono_radio_band_umts band)
+{
+       switch (band) {
+       case OFONO_RADIO_BAND_UMTS_ANY:
+               return "any";
+       case OFONO_RADIO_BAND_UMTS_850:
+               return "850";
+       case OFONO_RADIO_BAND_UMTS_900:
+               return "900";
+       case OFONO_RADIO_BAND_UMTS_1700AWS:
+               return "1700AWS";
+       case OFONO_RADIO_BAND_UMTS_1900:
+               return "1900";
+       case OFONO_RADIO_BAND_UMTS_2100:
+               return "2100";
+       }
+
+       return "";
+}
+
+static gboolean radio_band_umts_from_string(const char *str,
+                                       enum ofono_radio_band_umts *band)
+{
+       if (g_str_equal(str, "any")) {
+               *band = OFONO_RADIO_BAND_GSM_ANY;
+               return TRUE;
+       } else if (g_str_equal(str, "850")) {
+               *band = OFONO_RADIO_BAND_UMTS_850;
+               return TRUE;
+       } else if (g_str_equal(str, "900")) {
+               *band = OFONO_RADIO_BAND_UMTS_900;
+               return TRUE;
+       } else if (g_str_equal(str, "1700AWS")) {
+               *band = OFONO_RADIO_BAND_UMTS_1700AWS;
+               return TRUE;
+       } else if (g_str_equal(str, "1900")) {
+               *band = OFONO_RADIO_BAND_UMTS_1900;
+               return TRUE;
+       } else if (g_str_equal(str, "2100")) {
+               *band = OFONO_RADIO_BAND_UMTS_2100;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static DBusMessage *radio_get_properties_reply(DBusMessage *msg,
+                                               struct ofono_radio_settings *rs)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       const char *mode = radio_access_mode_to_string(rs->mode);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "TechnologyPreference",
+                                       DBUS_TYPE_STRING, &mode);
+
+       if (rs->driver->query_band) {
+               const char *band = radio_band_gsm_to_string(rs->band_gsm);
+
+               ofono_dbus_dict_append(&dict, "GsmBand",
+                                       DBUS_TYPE_STRING, &band);
+
+               band = radio_band_umts_to_string(rs->band_umts);
+
+               ofono_dbus_dict_append(&dict, "UmtsBand",
+                                       DBUS_TYPE_STRING, &band);
+       }
+
+       if (rs->driver->query_fast_dormancy) {
+               dbus_bool_t value = rs->fast_dormancy;
+               ofono_dbus_dict_append(&dict, "FastDormancy",
+                                       DBUS_TYPE_BOOLEAN, &value);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void radio_set_fast_dormancy(struct ofono_radio_settings *rs,
+                                       ofono_bool_t enable)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(rs->atom);
+       dbus_bool_t value = enable;
+
+       if (rs->fast_dormancy == enable)
+               return;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_RADIO_SETTINGS_INTERFACE,
+                                               "FastDormancy",
+                                               DBUS_TYPE_BOOLEAN, &value);
+       rs->fast_dormancy = enable;
+}
+
+static void radio_fast_dormancy_set_callback(const struct ofono_error *error,
+                                               void *data)
+{
+       struct ofono_radio_settings *rs = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error setting fast dormancy");
+
+               rs->fast_dormancy_pending = rs->fast_dormancy;
+
+               reply = __ofono_error_failed(rs->pending);
+               __ofono_dbus_pending_reply(&rs->pending, reply);
+
+               return;
+       }
+
+       reply = dbus_message_new_method_return(rs->pending);
+       __ofono_dbus_pending_reply(&rs->pending, reply);
+
+       radio_set_fast_dormancy(rs, rs->fast_dormancy_pending);
+}
+
+static void radio_set_band(struct ofono_radio_settings *rs)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *str_band;
+
+       path = __ofono_atom_get_path(rs->atom);
+
+       if (rs->band_gsm != rs->pending_band_gsm) {
+               rs->band_gsm = rs->pending_band_gsm;
+               str_band = radio_band_gsm_to_string(rs->band_gsm);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_RADIO_SETTINGS_INTERFACE,
+                                               "GsmBand", DBUS_TYPE_STRING,
+                                               &str_band);
+       }
+
+       if (rs->band_umts != rs->pending_band_umts) {
+               rs->band_umts = rs->pending_band_umts;
+               str_band = radio_band_umts_to_string(rs->band_umts);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_RADIO_SETTINGS_INTERFACE,
+                                               "UmtsBand", DBUS_TYPE_STRING,
+                                               &str_band);
+       }
+
+}
+
+static void radio_band_set_callback(const struct ofono_error *error,
+                                       void *data)
+{
+       struct ofono_radio_settings *rs = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error setting radio frequency band");
+
+               rs->pending_band_gsm = rs->band_gsm;
+               rs->pending_band_umts = rs->band_umts;
+
+               reply = __ofono_error_failed(rs->pending);
+               __ofono_dbus_pending_reply(&rs->pending, reply);
+
+               return;
+       }
+
+       reply = dbus_message_new_method_return(rs->pending);
+       __ofono_dbus_pending_reply(&rs->pending, reply);
+
+       radio_set_band(rs);
+}
+
+static void radio_set_rat_mode(struct ofono_radio_settings *rs,
+                               enum ofono_radio_access_mode mode)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *str_mode;
+
+       if (rs->mode == mode)
+               return;
+
+       rs->mode = mode;
+
+       path = __ofono_atom_get_path(rs->atom);
+       str_mode = radio_access_mode_to_string(rs->mode);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_RADIO_SETTINGS_INTERFACE,
+                                               "TechnologyPreference",
+                                               DBUS_TYPE_STRING, &str_mode);
+}
+
+static void radio_mode_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_radio_settings *rs = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error setting radio access mode");
+
+               rs->pending_mode = rs->mode;
+
+               reply = __ofono_error_failed(rs->pending);
+               __ofono_dbus_pending_reply(&rs->pending, reply);
+
+               return;
+       }
+
+       reply = dbus_message_new_method_return(rs->pending);
+       __ofono_dbus_pending_reply(&rs->pending, reply);
+
+       radio_set_rat_mode(rs, rs->pending_mode);
+}
+
+static void radio_send_properties_reply(struct ofono_radio_settings *rs)
+{
+       DBusMessage *reply;
+
+       rs->flags |= RADIO_SETTINGS_FLAG_CACHED;
+
+       reply = radio_get_properties_reply(rs->pending, rs);
+       __ofono_dbus_pending_reply(&rs->pending, reply);
+}
+
+static void radio_fast_dormancy_query_callback(const struct ofono_error *error,
+                                               ofono_bool_t enable, void *data)
+{
+       struct ofono_radio_settings *rs = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during fast dormancy query");
+
+               reply = __ofono_error_failed(rs->pending);
+               __ofono_dbus_pending_reply(&rs->pending, reply);
+
+               return;
+       }
+
+       radio_set_fast_dormancy(rs, enable);
+       radio_send_properties_reply(rs);
+}
+
+static void radio_query_fast_dormancy(struct ofono_radio_settings *rs)
+{
+       if (rs->driver->query_fast_dormancy == NULL) {
+               radio_send_properties_reply(rs);
+               return;
+       }
+
+       rs->driver->query_fast_dormancy(rs, radio_fast_dormancy_query_callback,
+                                       rs);
+}
+
+static void radio_band_query_callback(const struct ofono_error *error,
+                                       enum ofono_radio_band_gsm band_gsm,
+                                       enum ofono_radio_band_umts band_umts,
+                                       void *data)
+{
+       struct ofono_radio_settings *rs = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during radio frequency band query");
+
+               reply = __ofono_error_failed(rs->pending);
+               __ofono_dbus_pending_reply(&rs->pending, reply);
+
+               return;
+       }
+
+       rs->pending_band_gsm = band_gsm;
+       rs->pending_band_umts = band_umts;
+
+       radio_set_band(rs);
+       radio_query_fast_dormancy(rs);
+}
+
+static void radio_query_band(struct ofono_radio_settings *rs)
+{
+       if (rs->driver->query_band == NULL) {
+               radio_query_fast_dormancy(rs);
+               return;
+       }
+
+       rs->driver->query_band(rs, radio_band_query_callback, rs);
+}
+
+static void radio_rat_mode_query_callback(const struct ofono_error *error,
+                                       enum ofono_radio_access_mode mode,
+                                       void *data)
+{
+       struct ofono_radio_settings *rs = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Error during radio access mode query");
+
+               reply = __ofono_error_failed(rs->pending);
+               __ofono_dbus_pending_reply(&rs->pending, reply);
+
+               return;
+       }
+
+       radio_set_rat_mode(rs, mode);
+       radio_query_band(rs);
+}
+
+static DBusMessage *radio_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_radio_settings *rs = data;
+
+       if (rs->flags & RADIO_SETTINGS_FLAG_CACHED)
+               return radio_get_properties_reply(msg, rs);
+
+       if (rs->driver->query_rat_mode == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (rs->pending)
+               return __ofono_error_busy(msg);
+
+       rs->pending = dbus_message_ref(msg);
+       rs->driver->query_rat_mode(rs, radio_rat_mode_query_callback, rs);
+
+       return NULL;
+}
+
+static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_radio_settings *rs = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+
+       if (rs->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (g_strcmp0(property, "TechnologyPreference") == 0) {
+               const char *value;
+               enum ofono_radio_access_mode mode;
+
+               if (rs->driver->set_rat_mode == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+               if (radio_access_mode_from_string(value, &mode) == FALSE)
+                       return __ofono_error_invalid_args(msg);
+
+               if (rs->mode == mode)
+                       return dbus_message_new_method_return(msg);
+
+               rs->pending = dbus_message_ref(msg);
+               rs->pending_mode = mode;
+
+               rs->driver->set_rat_mode(rs, mode, radio_mode_set_callback, rs);
+
+               return NULL;
+       } else if (g_strcmp0(property, "GsmBand") == 0) {
+               const char *value;
+               enum ofono_radio_band_gsm band;
+
+               if (rs->driver->set_band == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+               if (radio_band_gsm_from_string(value, &band) == FALSE)
+                       return __ofono_error_invalid_args(msg);
+
+               if (rs->band_gsm == band)
+                       return dbus_message_new_method_return(msg);
+
+               rs->pending = dbus_message_ref(msg);
+               rs->pending_band_gsm = band;
+
+               rs->driver->set_band(rs, band, rs->band_umts,
+                                       radio_band_set_callback, rs);
+
+               return NULL;
+       } else if (g_strcmp0(property, "UmtsBand") == 0) {
+               const char *value;
+               enum ofono_radio_band_umts band;
+
+               if (rs->driver->set_band == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+               if (radio_band_umts_from_string(value, &band) == FALSE)
+                       return __ofono_error_invalid_args(msg);
+
+               if (rs->band_umts == band)
+                       return dbus_message_new_method_return(msg);
+
+               rs->pending = dbus_message_ref(msg);
+               rs->pending_band_umts = band;
+
+               rs->driver->set_band(rs, rs->band_gsm, band,
+                                       radio_band_set_callback, rs);
+
+               return NULL;
+       } else if (g_strcmp0(property, "FastDormancy") == 0) {
+               dbus_bool_t value;
+               int target;
+
+               if (rs->driver->set_fast_dormancy == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+               target = value;
+
+               if (rs->fast_dormancy_pending == target)
+                       return dbus_message_new_method_return(msg);
+
+               rs->pending = dbus_message_ref(msg);
+               rs->fast_dormancy_pending = target;
+
+               rs->driver->set_fast_dormancy(rs, target,
+                                       radio_fast_dormancy_set_callback, rs);
+               return NULL;
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static GDBusMethodTable radio_methods[] = {
+       { "GetProperties",  "",    "a{sv}",  radio_get_properties,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SetProperty",    "sv",  "",       radio_set_property,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable radio_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+int ofono_radio_settings_driver_register(const struct ofono_radio_settings_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d == NULL || d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_radio_settings_driver_unregister(const struct ofono_radio_settings_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d == NULL)
+               return;
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void radio_settings_unregister(struct ofono_atom *atom)
+{
+       struct ofono_radio_settings *rs = __ofono_atom_get_data(atom);
+       const char *path = __ofono_atom_get_path(rs->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(rs->atom);
+
+       ofono_modem_remove_interface(modem, OFONO_RADIO_SETTINGS_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_RADIO_SETTINGS_INTERFACE);
+}
+
+static void radio_settings_remove(struct ofono_atom *atom)
+{
+       struct ofono_radio_settings *rs = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (rs == NULL)
+               return;
+
+       if (rs->driver && rs->driver->remove)
+               rs->driver->remove(rs);
+
+       g_free(rs);
+}
+
+struct ofono_radio_settings *ofono_radio_settings_create(struct ofono_modem *modem,
+                                                       unsigned int vendor,
+                                                       const char *driver,
+                                                       void *data)
+{
+       struct ofono_radio_settings *rs;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       rs = g_try_new0(struct ofono_radio_settings, 1);
+       if (rs == NULL)
+               return NULL;
+
+       rs->mode = -1;
+
+       rs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_RADIO_SETTINGS,
+                                               radio_settings_remove, rs);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_radio_settings_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver) != 0)
+                       continue;
+
+               if (drv->probe(rs, vendor, data) < 0)
+                       continue;
+
+               rs->driver = drv;
+               break;
+       }
+
+       return rs;
+}
+
+void ofono_radio_settings_register(struct ofono_radio_settings *rs)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(rs->atom);
+       const char *path = __ofono_atom_get_path(rs->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_RADIO_SETTINGS_INTERFACE,
+                                       radio_methods, radio_signals,
+                                       NULL, rs, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_RADIO_SETTINGS_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_RADIO_SETTINGS_INTERFACE);
+       __ofono_atom_register(rs->atom, radio_settings_unregister);
+}
+
+void ofono_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+       __ofono_atom_free(rs->atom);
+}
+
+void ofono_radio_settings_set_data(struct ofono_radio_settings *rs,
+                                       void *data)
+{
+       rs->driver_data = data;
+}
+
+void *ofono_radio_settings_get_data(struct ofono_radio_settings *rs)
+{
+       return rs->driver_data;
+}
diff --git a/src/sim-auth.c b/src/sim-auth.c
new file mode 100644 (file)
index 0000000..5d2f075
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+
+#include <glib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "ofono.h"
+
+#include "simutil.h"
+
+static GSList *g_drivers = NULL;
+
+struct ofono_sim_auth {
+       const struct ofono_sim_auth_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
+int ofono_sim_auth_driver_register(const struct ofono_sim_auth_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_sim_auth_driver_unregister(const struct ofono_sim_auth_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void sim_auth_unregister(struct ofono_atom *atom)
+{
+}
+
+static void sim_auth_remove(struct ofono_atom *atom)
+{
+       struct ofono_sim_auth *sa = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (sa == NULL)
+               return;
+
+       if (sa->driver && sa->driver->remove)
+               sa->driver->remove(sa);
+
+       g_free(sa);
+}
+
+struct ofono_sim_auth *ofono_sim_auth_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver, void *data)
+{
+       struct ofono_sim_auth *sa;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       sa = g_try_new0(struct ofono_sim_auth, 1);
+
+       if (sa == NULL)
+               return NULL;
+
+       sa->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SIM_AUTH,
+                                               sim_auth_remove, sa);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_sim_auth_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(sa, vendor, data) < 0)
+                       continue;
+
+               sa->driver = drv;
+               break;
+       }
+
+       return sa;
+}
+
+void ofono_sim_auth_register(struct ofono_sim_auth *sa)
+{
+       __ofono_atom_register(sa->atom, sim_auth_unregister);
+}
+
+void ofono_sim_auth_remove(struct ofono_sim_auth *sa)
+{
+       __ofono_atom_free(sa->atom);
+}
+
+void ofono_sim_auth_set_data(struct ofono_sim_auth *sa, void *data)
+{
+       sa->driver_data = data;
+}
+
+void *ofono_sim_auth_get_data(struct ofono_sim_auth *sa)
+{
+       return sa->driver_data;
+}
diff --git a/src/sim.c b/src/sim.c
new file mode 100644 (file)
index 0000000..eb25e07
--- /dev/null
+++ b/src/sim.c
@@ -0,0 +1,3078 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <gdbus.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "util.h"
+#include "smsutil.h"
+#include "simutil.h"
+#include "storage.h"
+#include "simfs.h"
+#include "stkutil.h"
+
+#define SIM_FLAG_READING_SPN   0x1
+
+struct ofono_sim {
+       int flags;
+
+       /* Contents of the SIM file system, in rough initialization order */
+       char *iccid;
+
+       char **language_prefs;
+       unsigned char *efli;
+       unsigned char efli_length;
+       gboolean language_prefs_update;
+
+       enum ofono_sim_password_type pin_type;
+       gboolean locked_pins[OFONO_SIM_PASSWORD_SIM_PUK]; /* Number of PINs */
+
+       int pin_retries[OFONO_SIM_PASSWORD_INVALID];
+
+       enum ofono_sim_phase phase;
+       unsigned char mnc_length;
+       enum ofono_sim_cphs_phase cphs_phase;
+       unsigned char cphs_service_table[2];
+       unsigned char *efust;
+       unsigned char efust_length;
+       unsigned char *efest;
+       unsigned char efest_length;
+       unsigned char *efsst;
+       unsigned char efsst_length;
+       gboolean fixed_dialing;
+       gboolean barred_dialing;
+
+       char *imsi;
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+
+       GSList *own_numbers;
+       GSList *new_numbers;
+       unsigned char efmsisdn_length;
+       unsigned char efmsisdn_records;
+
+       GSList *service_numbers;
+       gboolean sdn_ready;
+
+       unsigned char *efimg;
+       unsigned short efimg_length;
+
+       enum ofono_sim_state state;
+       struct ofono_watchlist *state_watches;
+
+       char *spn;
+       char *spn_dc;
+       struct ofono_watchlist *spn_watches;
+       unsigned int ef_spn_watch;
+       unsigned int cphs_spn_watch;
+       unsigned int cphs_spn_short_watch;
+
+       struct sim_fs *simfs;
+       struct ofono_sim_context *context;
+       struct ofono_sim_context *early_context;
+
+       unsigned char *iidf_image;
+       unsigned int *iidf_watch_ids;
+
+       DBusMessage *pending;
+       const struct ofono_sim_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       unsigned int hfp_watch;
+};
+
+struct msisdn_set_request {
+       struct ofono_sim *sim;
+       int pending;
+       int failed;
+       DBusMessage *msg;
+};
+
+struct service_number {
+       char *id;
+       struct ofono_phone_number ph;
+};
+
+static const char *const passwd_name[] = {
+       [OFONO_SIM_PASSWORD_NONE] = "none",
+       [OFONO_SIM_PASSWORD_SIM_PIN] = "pin",
+       [OFONO_SIM_PASSWORD_SIM_PUK] = "puk",
+       [OFONO_SIM_PASSWORD_PHSIM_PIN] = "phone",
+       [OFONO_SIM_PASSWORD_PHFSIM_PIN] = "firstphone",
+       [OFONO_SIM_PASSWORD_PHFSIM_PUK] = "firstphonepuk",
+       [OFONO_SIM_PASSWORD_SIM_PIN2] = "pin2",
+       [OFONO_SIM_PASSWORD_SIM_PUK2] = "puk2",
+       [OFONO_SIM_PASSWORD_PHNET_PIN] = "network",
+       [OFONO_SIM_PASSWORD_PHNET_PUK] = "networkpuk",
+       [OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "netsub",
+       [OFONO_SIM_PASSWORD_PHNETSUB_PUK] = "netsubpuk",
+       [OFONO_SIM_PASSWORD_PHSP_PIN] = "service",
+       [OFONO_SIM_PASSWORD_PHSP_PUK] = "servicepuk",
+       [OFONO_SIM_PASSWORD_PHCORP_PIN] = "corp",
+       [OFONO_SIM_PASSWORD_PHCORP_PUK] = "corppuk",
+};
+
+static void sim_own_numbers_update(struct ofono_sim *sim);
+
+static GSList *g_drivers = NULL;
+
+static const char *sim_passwd_name(enum ofono_sim_password_type type)
+{
+       return passwd_name[type];
+}
+
+static enum ofono_sim_password_type sim_string_to_passwd(const char *name)
+{
+       int len = sizeof(passwd_name) / sizeof(*passwd_name);
+       int i;
+
+       for (i = 0; i < len; i++)
+               if (!strcmp(passwd_name[i], name))
+                       return i;
+
+       return OFONO_SIM_PASSWORD_INVALID;
+}
+
+static gboolean password_is_pin(enum ofono_sim_password_type type)
+{
+       switch (type) {
+       case OFONO_SIM_PASSWORD_SIM_PIN:
+       case OFONO_SIM_PASSWORD_PHSIM_PIN:
+       case OFONO_SIM_PASSWORD_PHFSIM_PIN:
+       case OFONO_SIM_PASSWORD_SIM_PIN2:
+       case OFONO_SIM_PASSWORD_PHNET_PIN:
+       case OFONO_SIM_PASSWORD_PHNETSUB_PIN:
+       case OFONO_SIM_PASSWORD_PHSP_PIN:
+       case OFONO_SIM_PASSWORD_PHCORP_PIN:
+               return TRUE;
+       case OFONO_SIM_PASSWORD_SIM_PUK:
+       case OFONO_SIM_PASSWORD_PHFSIM_PUK:
+       case OFONO_SIM_PASSWORD_SIM_PUK2:
+       case OFONO_SIM_PASSWORD_PHNET_PUK:
+       case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
+       case OFONO_SIM_PASSWORD_PHSP_PUK:
+       case OFONO_SIM_PASSWORD_PHCORP_PUK:
+       case OFONO_SIM_PASSWORD_INVALID:
+       case OFONO_SIM_PASSWORD_NONE:
+               return FALSE;
+       }
+
+       return FALSE;
+}
+
+static enum ofono_sim_password_type puk2pin(enum ofono_sim_password_type type)
+{
+       switch (type) {
+       case OFONO_SIM_PASSWORD_SIM_PUK:
+               return OFONO_SIM_PASSWORD_SIM_PIN;
+       case OFONO_SIM_PASSWORD_PHFSIM_PUK:
+               return OFONO_SIM_PASSWORD_PHFSIM_PIN;
+       case OFONO_SIM_PASSWORD_SIM_PUK2:
+               return OFONO_SIM_PASSWORD_SIM_PIN2;
+       case OFONO_SIM_PASSWORD_PHNET_PUK:
+               return OFONO_SIM_PASSWORD_PHNET_PIN;
+       case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
+               return OFONO_SIM_PASSWORD_PHNETSUB_PIN;
+       case OFONO_SIM_PASSWORD_PHSP_PUK:
+               return OFONO_SIM_PASSWORD_PHSP_PIN;
+       case OFONO_SIM_PASSWORD_PHCORP_PUK:
+               return OFONO_SIM_PASSWORD_PHCORP_PIN;
+       default:
+               return OFONO_SIM_PASSWORD_INVALID;
+       }
+}
+
+static char **get_own_numbers(GSList *own_numbers)
+{
+       int nelem = 0;
+       GSList *l;
+       struct ofono_phone_number *num;
+       char **ret;
+
+       if (own_numbers)
+               nelem = g_slist_length(own_numbers);
+
+       ret = g_new0(char *, nelem + 1);
+
+       nelem = 0;
+       for (l = own_numbers; l; l = l->next) {
+               num = l->data;
+
+               ret[nelem++] = g_strdup(phone_number_to_string(num));
+       }
+
+       return ret;
+}
+
+static char **get_locked_pins(struct ofono_sim *sim)
+{
+       int i;
+       int nelem = 0;
+       char **ret;
+
+       for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) {
+               if (sim->locked_pins[i] == FALSE)
+                       continue;
+
+               nelem += 1;
+       }
+
+       ret = g_new0(char *, nelem + 1);
+
+       nelem = 0;
+
+       for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) {
+               if (sim->locked_pins[i] == FALSE)
+                       continue;
+
+               ret[nelem] = g_strdup(sim_passwd_name(i));
+               nelem += 1;
+       }
+
+       return ret;
+}
+
+static void **get_pin_retries(struct ofono_sim *sim)
+{
+       int i, nelem;
+       void **ret;
+
+       for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) {
+               if (sim->pin_retries[i] == -1)
+                       continue;
+
+               nelem += 1;
+       }
+
+       ret = g_new0(void *, nelem * 2 + 1);
+
+       for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) {
+               if (sim->pin_retries[i] == -1)
+                       continue;
+
+               ret[nelem++] = (void *) sim_passwd_name(i);
+               ret[nelem++] = &sim->pin_retries[i];
+       }
+
+       return ret;
+}
+
+static char **get_service_numbers(GSList *service_numbers)
+{
+       int nelem;
+       GSList *l;
+       struct service_number *num;
+       char **ret;
+
+       nelem = g_slist_length(service_numbers) * 2;
+
+       ret = g_new0(char *, nelem + 1);
+
+       nelem = 0;
+       for (l = service_numbers; l; l = l->next) {
+               num = l->data;
+
+               ret[nelem++] = g_strdup(num->id);
+               ret[nelem++] = g_strdup(phone_number_to_string(&num->ph));
+       }
+
+       return ret;
+}
+
+static void service_number_free(struct service_number *num)
+{
+       g_free(num->id);
+       g_free(num);
+}
+
+static void call_state_watches(struct ofono_sim *sim)
+{
+       GSList *l;
+       ofono_sim_state_event_cb_t notify;
+
+       for (l = sim->state_watches->items; l; l = l->next) {
+               struct ofono_watchlist_item *item = l->data;
+               notify = item->notify;
+
+               notify(sim->state, item->notify_data);
+       }
+}
+
+static DBusMessage *sim_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       char **own_numbers;
+       char **service_numbers;
+       char **locked_pins;
+       const char *pin_name;
+       void **pin_retries;
+       dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;
+       dbus_bool_t fdn;
+       dbus_bool_t bdn;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       ofono_dbus_dict_append(&dict, "Present", DBUS_TYPE_BOOLEAN, &present);
+
+       if (!present)
+               goto done;
+
+       if (sim->iccid)
+               ofono_dbus_dict_append(&dict, "CardIdentifier",
+                                       DBUS_TYPE_STRING, &sim->iccid);
+
+       if (sim->imsi)
+               ofono_dbus_dict_append(&dict, "SubscriberIdentity",
+                                       DBUS_TYPE_STRING, &sim->imsi);
+
+       fdn = sim->fixed_dialing;
+       ofono_dbus_dict_append(&dict, "FixedDialing", DBUS_TYPE_BOOLEAN, &fdn);
+
+       bdn = sim->barred_dialing;
+       ofono_dbus_dict_append(&dict, "BarredDialing", DBUS_TYPE_BOOLEAN, &bdn);
+
+       if (sim->mcc[0] != '\0' && sim->mnc[0] != '\0') {
+               const char *str;
+               str = sim->mcc;
+               ofono_dbus_dict_append(&dict, "MobileCountryCode",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = sim->mnc;
+               ofono_dbus_dict_append(&dict, "MobileNetworkCode",
+                                       DBUS_TYPE_STRING, &str);
+       }
+
+       own_numbers = get_own_numbers(sim->own_numbers);
+
+       ofono_dbus_dict_append_array(&dict, "SubscriberNumbers",
+                                       DBUS_TYPE_STRING, &own_numbers);
+       g_strfreev(own_numbers);
+
+       locked_pins = get_locked_pins(sim);
+       ofono_dbus_dict_append_array(&dict, "LockedPins",
+                                       DBUS_TYPE_STRING, &locked_pins);
+       g_strfreev(locked_pins);
+
+       if (sim->service_numbers && sim->sdn_ready) {
+               service_numbers = get_service_numbers(sim->service_numbers);
+
+               ofono_dbus_dict_append_dict(&dict, "ServiceNumbers",
+                                               DBUS_TYPE_STRING,
+                                               &service_numbers);
+               g_strfreev(service_numbers);
+       }
+
+       if (sim->language_prefs)
+               ofono_dbus_dict_append_array(&dict, "PreferredLanguages",
+                                               DBUS_TYPE_STRING,
+                                               &sim->language_prefs);
+
+       pin_name = sim_passwd_name(sim->pin_type);
+       ofono_dbus_dict_append(&dict, "PinRequired",
+                               DBUS_TYPE_STRING,
+                               (void *) &pin_name);
+
+       pin_retries = get_pin_retries(sim);
+       ofono_dbus_dict_append_dict(&dict, "Retries", DBUS_TYPE_BYTE,
+                                                               &pin_retries);
+       g_free(pin_retries);
+
+done:
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void sim_pin_retries_query_cb(const struct ofono_error *error,
+                                       int retries[OFONO_SIM_PASSWORD_INVALID],
+                                       void *data)
+{
+       struct ofono_sim *sim = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+       void **pin_retries;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Querying remaining pin retries failed");
+
+               return;
+       }
+
+       if (!memcmp(retries, sim->pin_retries, sizeof(sim->pin_retries)))
+               return;
+
+       memcpy(sim->pin_retries, retries, sizeof(sim->pin_retries));
+
+       pin_retries = get_pin_retries(sim);
+       ofono_dbus_signal_dict_property_changed(conn, path,
+                                       OFONO_SIM_MANAGER_INTERFACE, "Retries",
+                                       DBUS_TYPE_BYTE, &pin_retries);
+       g_free(pin_retries);
+}
+
+static void sim_pin_retries_check(struct ofono_sim *sim)
+{
+       if (sim->driver->query_pin_retries == NULL)
+               return;
+
+       sim->driver->query_pin_retries(sim, sim_pin_retries_query_cb, sim);
+}
+
+static void msisdn_set_done(struct msisdn_set_request *req)
+{
+       DBusMessage *reply;
+
+       if (req->failed)
+               reply = __ofono_error_failed(req->msg);
+       else
+               reply = dbus_message_new_method_return(req->msg);
+
+       __ofono_dbus_pending_reply(&req->msg, reply);
+
+       /* Re-read the numbers and emit signal if needed */
+       sim_own_numbers_update(req->sim);
+
+       g_free(req);
+}
+
+static void msisdn_set_cb(int ok, void *data)
+{
+       struct msisdn_set_request *req = data;
+
+       if (!ok)
+               req->failed++;
+
+       req->pending--;
+
+       if (!req->pending)
+               msisdn_set_done(req);
+}
+
+static gboolean set_own_numbers(struct ofono_sim *sim,
+                               GSList *new_numbers, DBusMessage *msg)
+{
+       struct msisdn_set_request *req;
+       int record;
+       unsigned char efmsisdn[255];
+       struct ofono_phone_number *number;
+
+       if (new_numbers && g_slist_length(new_numbers) > sim->efmsisdn_records)
+               return FALSE;
+
+       req = g_new0(struct msisdn_set_request, 1);
+
+       req->sim = sim;
+       req->msg = dbus_message_ref(msg);
+
+       for (record = 1; record <= sim->efmsisdn_records; record++) {
+               if (new_numbers) {
+                       number = new_numbers->data;
+                       sim_adn_build(efmsisdn, sim->efmsisdn_length,
+                                       number, NULL);
+                       new_numbers = new_numbers->next;
+               } else {
+                       memset(efmsisdn, 0xff, sim->efmsisdn_length);
+                       /* Set number length */
+                       efmsisdn[sim->efmsisdn_length - 14] = 1;
+               }
+
+               if (ofono_sim_write(req->sim->context, SIM_EFMSISDN_FILEID,
+                               msisdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               record, efmsisdn,
+                               sim->efmsisdn_length, req) == 0)
+                       req->pending++;
+               else
+                       req->failed++;
+       }
+
+       if (!req->pending)
+               msisdn_set_done(req);
+
+       return TRUE;
+}
+
+static DBusMessage *sim_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sim *sim = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       DBusMessageIter var_elem;
+       const char *name, *value;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+
+       if (!strcmp(name, "SubscriberNumbers")) {
+               gboolean set_ok = FALSE;
+               struct ofono_phone_number *own;
+               GSList *own_numbers = NULL;
+
+               if (sim->efmsisdn_length == 0)
+                       return __ofono_error_busy(msg);
+
+               dbus_message_iter_next(&iter);
+
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_recurse(&iter, &var);
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_ARRAY ||
+                               dbus_message_iter_get_element_type(&var) !=
+                               DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_recurse(&var, &var_elem);
+
+               /* Empty lists are supported */
+               while (dbus_message_iter_get_arg_type(&var_elem) !=
+                               DBUS_TYPE_INVALID) {
+                       if (dbus_message_iter_get_arg_type(&var_elem) !=
+                                       DBUS_TYPE_STRING)
+                               goto error;
+
+                       dbus_message_iter_get_basic(&var_elem, &value);
+
+                       if (!valid_phone_number_format(value))
+                               goto error;
+
+                       own = g_new0(struct ofono_phone_number, 1);
+                       string_to_phone_number(value, own);
+
+                       own_numbers = g_slist_prepend(own_numbers, own);
+
+                       dbus_message_iter_next(&var_elem);
+               }
+
+               own_numbers = g_slist_reverse(own_numbers);
+               set_ok = set_own_numbers(sim, own_numbers, msg);
+
+error:
+               g_slist_foreach(own_numbers, (GFunc) g_free, 0);
+               g_slist_free(own_numbers);
+
+               if (set_ok)
+                       return NULL;
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static void sim_locked_cb(struct ofono_sim *sim, gboolean locked)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+       const char *typestr;
+       const char *pin;
+       char **locked_pins;
+       enum ofono_sim_password_type type;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(sim->pending);
+
+       dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_STRING, &typestr,
+                                       DBUS_TYPE_STRING, &pin,
+                                       DBUS_TYPE_INVALID);
+
+       type = sim_string_to_passwd(typestr);
+
+       /* This is used by lock/unlock pin, no puks allowed */
+       sim->locked_pins[type] = locked;
+       __ofono_dbus_pending_reply(&sim->pending, reply);
+
+       locked_pins = get_locked_pins(sim);
+       ofono_dbus_signal_array_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "LockedPins", DBUS_TYPE_STRING,
+                                               &locked_pins);
+       g_strfreev(locked_pins);
+
+       sim_pin_retries_check(sim);
+}
+
+static void sim_unlock_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_sim *sim = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBusMessage *reply = __ofono_error_failed(sim->pending);
+
+               __ofono_dbus_pending_reply(&sim->pending, reply);
+               __ofono_sim_recheck_pin(sim);
+
+               return;
+       }
+
+       sim_locked_cb(sim, FALSE);
+}
+
+static void sim_lock_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_sim *sim = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBusMessage *reply = __ofono_error_failed(sim->pending);
+
+               __ofono_dbus_pending_reply(&sim->pending, reply);
+               __ofono_sim_recheck_pin(sim);
+
+               return;
+       }
+
+       sim_locked_cb(sim, TRUE);
+}
+
+static DBusMessage *sim_lock_or_unlock(struct ofono_sim *sim, int lock,
+                                       DBusConnection *conn, DBusMessage *msg)
+{
+       enum ofono_sim_password_type type;
+       const char *typestr;
+       const char *pin;
+
+       if (sim->driver->lock == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (sim->pending)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
+                                       DBUS_TYPE_STRING, &pin,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       type = sim_string_to_passwd(typestr);
+
+       /*
+        * SIM PIN2 cannot be locked / unlocked according to 27.007,
+        * however the PIN combination can be changed
+        */
+       if (password_is_pin(type) == FALSE ||
+                       type == OFONO_SIM_PASSWORD_SIM_PIN2)
+               return __ofono_error_invalid_format(msg);
+
+       if (!__ofono_is_valid_sim_pin(pin, type))
+               return __ofono_error_invalid_format(msg);
+
+       sim->pending = dbus_message_ref(msg);
+
+       sim->driver->lock(sim, type, lock, pin,
+                               lock ? sim_lock_cb : sim_unlock_cb, sim);
+
+       return NULL;
+}
+
+static DBusMessage *sim_lock_pin(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sim *sim = data;
+
+       return sim_lock_or_unlock(sim, 1, conn, msg);
+}
+
+static DBusMessage *sim_unlock_pin(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sim *sim = data;
+
+       return sim_lock_or_unlock(sim, 0, conn, msg);
+}
+
+static void sim_change_pin_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_sim *sim = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               __ofono_dbus_pending_reply(&sim->pending,
+                               __ofono_error_failed(sim->pending));
+
+               __ofono_sim_recheck_pin(sim);
+
+               return;
+       }
+
+       __ofono_dbus_pending_reply(&sim->pending,
+                               dbus_message_new_method_return(sim->pending));
+
+       sim_pin_retries_check(sim);
+}
+
+static DBusMessage *sim_change_pin(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sim *sim = data;
+       enum ofono_sim_password_type type;
+       const char *typestr;
+       const char *old;
+       const char *new;
+
+       if (sim->driver->change_passwd == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (sim->pending)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
+                                       DBUS_TYPE_STRING, &old,
+                                       DBUS_TYPE_STRING, &new,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       type = sim_string_to_passwd(typestr);
+
+       if (password_is_pin(type) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       if (!__ofono_is_valid_sim_pin(old, type))
+               return __ofono_error_invalid_format(msg);
+
+       if (!__ofono_is_valid_sim_pin(new, type))
+               return __ofono_error_invalid_format(msg);
+
+       if (!strcmp(new, old))
+               return dbus_message_new_method_return(msg);
+
+       sim->pending = dbus_message_ref(msg);
+       sim->driver->change_passwd(sim, type, old, new,
+                                       sim_change_pin_cb, sim);
+
+       return NULL;
+}
+
+static void sim_enter_pin_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_sim *sim = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               reply = __ofono_error_failed(sim->pending);
+       else
+               reply = dbus_message_new_method_return(sim->pending);
+
+       __ofono_dbus_pending_reply(&sim->pending, reply);
+
+       __ofono_sim_recheck_pin(sim);
+}
+
+static DBusMessage *sim_enter_pin(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sim *sim = data;
+       const char *typestr;
+       enum ofono_sim_password_type type;
+       const char *pin;
+
+       if (sim->driver->send_passwd == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (sim->pending)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
+                                       DBUS_TYPE_STRING, &pin,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       type = sim_string_to_passwd(typestr);
+
+       if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type)
+               return __ofono_error_invalid_format(msg);
+
+       if (password_is_pin(type) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       if (!__ofono_is_valid_sim_pin(pin, type))
+               return __ofono_error_invalid_format(msg);
+
+       sim->pending = dbus_message_ref(msg);
+       sim->driver->send_passwd(sim, pin, sim_enter_pin_cb, sim);
+
+       return NULL;
+}
+
+static void sim_get_image_cb(struct ofono_sim *sim,
+                               unsigned char id, char *xpm, gboolean cache)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, array;
+       int xpm_len;
+
+       if (xpm == NULL) {
+               reply = __ofono_error_failed(sim->pending);
+               __ofono_dbus_pending_reply(&sim->pending, reply);
+               return;
+       }
+
+       xpm_len = strlen(xpm);
+
+       reply = dbus_message_new_method_return(sim->pending);
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_BYTE_AS_STRING, &array);
+
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                               &xpm, xpm_len);
+       dbus_message_iter_close_container(&iter, &array);
+
+       __ofono_dbus_pending_reply(&sim->pending, reply);
+
+       if (cache)
+               sim_fs_cache_image(sim->simfs, (const char *) xpm, id);
+
+       g_free(xpm);
+}
+
+static void sim_iidf_read_clut_cb(int ok, int length, int record,
+                                       const unsigned char *data,
+                                       int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       unsigned char id;
+       unsigned char *efimg;
+       unsigned short iidf_len;
+       unsigned short clut_len;
+       char *xpm;
+
+       DBG("ok: %d", ok);
+
+       dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
+                                       DBUS_TYPE_INVALID);
+       id -= 1;
+       efimg = &sim->efimg[id * 9];
+
+       if (!ok) {
+               sim_get_image_cb(sim, id, NULL, FALSE);
+               goto done;
+       }
+
+       iidf_len = efimg[7] << 8 | efimg[8];
+
+       if (sim->iidf_image[3] == 0)
+               clut_len = 256 * 3;
+       else
+               clut_len = sim->iidf_image[3] * 3;
+
+       xpm = stk_image_to_xpm(sim->iidf_image, iidf_len, efimg[2],
+                                       data, clut_len);
+       sim_get_image_cb(sim, id, xpm, TRUE);
+
+done:
+       g_free(sim->iidf_image);
+       sim->iidf_image = NULL;
+}
+
+static void sim_iidf_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       unsigned char id;
+       unsigned char *efimg;
+       unsigned short iidf_id;
+       unsigned short offset;
+       unsigned short clut_len;
+
+       DBG("ok: %d", ok);
+
+       dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
+                                       DBUS_TYPE_INVALID);
+       id -= 1;
+       efimg = &sim->efimg[id * 9];
+
+       if (!ok) {
+               sim_get_image_cb(sim, id, NULL, FALSE);
+               return;
+       }
+
+       if (efimg[2] == STK_IMG_SCHEME_BASIC) {
+               char *xpm = stk_image_to_xpm(data, length, efimg[2], NULL, 0);
+               sim_get_image_cb(sim, id, xpm, TRUE);
+               return;
+       }
+
+       offset = data[4] << 8 | data[5];
+
+       if (data[3] == 0)
+               clut_len = 256 * 3;
+       else
+               clut_len = data[3] * 3;
+
+       iidf_id = efimg[3] << 8 | efimg[4];
+       sim->iidf_image = g_memdup(data, length);
+
+       /* read the clut data */
+       ofono_sim_read_bytes(sim->context, iidf_id, offset, clut_len,
+                                       sim_iidf_read_clut_cb, sim);
+}
+
+static void sim_image_data_changed(int id, void *userdata)
+{
+       /* TODO: notify D-bus clients */
+}
+
+static void sim_get_image(struct ofono_sim *sim, unsigned char id,
+                               gpointer user_data)
+{
+       unsigned char *efimg;
+       char *image;
+       unsigned short iidf_id;
+       unsigned short iidf_offset;
+       unsigned short iidf_len;
+
+       if (sim->efimg_length <= id * 9) {
+               sim_get_image_cb(sim, id, NULL, FALSE);
+               return;
+       }
+
+       image = sim_fs_get_cached_image(sim->simfs, id);
+       if (image != NULL)
+               sim_get_image_cb(sim, id, image, FALSE);
+
+       efimg = &sim->efimg[id * 9];
+
+       iidf_id = efimg[3] << 8 | efimg[4];
+       iidf_offset = efimg[5] << 8 | efimg[6];
+       iidf_len = efimg[7] << 8 | efimg[8];
+
+       /* read the image data */
+       if (image == NULL)
+               ofono_sim_read_bytes(sim->context, iidf_id, iidf_offset,
+                                       iidf_len, sim_iidf_read_cb, sim);
+
+       if (sim->iidf_watch_ids[id] > 0)
+               return;
+
+       sim->iidf_watch_ids[id] = ofono_sim_add_file_watch(sim->context,
+                                       iidf_id, sim_image_data_changed,
+                                       sim, NULL);
+}
+
+static DBusMessage *sim_get_icon(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_sim *sim = data;
+       unsigned char id;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &id,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       /* zero means no icon */
+       if (id == 0)
+               return __ofono_error_invalid_args(msg);
+
+       if (sim->pending)
+               return __ofono_error_busy(msg);
+
+       if (sim->efimg == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       sim->pending = dbus_message_ref(msg);
+
+       sim_get_image(sim, id - 1, sim);
+
+       return NULL;
+}
+
+static DBusMessage *sim_reset_pin(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sim *sim = data;
+       const char *typestr;
+       enum ofono_sim_password_type type;
+       const char *puk;
+       const char *pin;
+
+       if (sim->driver->reset_passwd == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (sim->pending)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
+                                       DBUS_TYPE_STRING, &puk,
+                                       DBUS_TYPE_STRING, &pin,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       type = sim_string_to_passwd(typestr);
+
+       if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type)
+               return __ofono_error_invalid_format(msg);
+
+       if (!__ofono_is_valid_sim_pin(puk, type))
+               return __ofono_error_invalid_format(msg);
+
+       type = puk2pin(type);
+
+       if (!__ofono_is_valid_sim_pin(pin, type))
+               return __ofono_error_invalid_format(msg);
+
+       sim->pending = dbus_message_ref(msg);
+       sim->driver->reset_passwd(sim, puk, pin, sim_enter_pin_cb, sim);
+
+       return NULL;
+}
+
+static GDBusMethodTable sim_methods[] = {
+       { "GetProperties",      "",     "a{sv}",        sim_get_properties },
+       { "SetProperty",        "sv",   "",             sim_set_property,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "ChangePin",          "sss",  "",             sim_change_pin,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "EnterPin",           "ss",   "",             sim_enter_pin,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "ResetPin",           "sss",  "",             sim_reset_pin,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "LockPin",            "ss",   "",             sim_lock_pin,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "UnlockPin",          "ss",   "",             sim_unlock_pin,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "GetIcon",            "y",    "ay",           sim_get_icon,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable sim_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { }
+};
+
+static gboolean numbers_list_equal(GSList *a, GSList *b)
+{
+       struct ofono_phone_number *num_a, *num_b;
+
+       while (a || b) {
+               if (a == NULL || b == NULL)
+                       return FALSE;
+
+               num_a = a->data;
+               num_b = b->data;
+
+               if (!g_str_equal(num_a->number, num_b->number) ||
+                               num_a->type != num_b->type)
+                       return FALSE;
+
+               a = a->next;
+               b = b->next;
+       }
+
+       return TRUE;
+}
+
+static void sim_msisdn_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       int total;
+       struct ofono_phone_number ph;
+
+       if (!ok)
+               goto check;
+
+       if (record_length < 14 || length < record_length)
+               return;
+
+       total = length / record_length;
+
+       sim->efmsisdn_length = record_length;
+       sim->efmsisdn_records = total;
+
+       if (sim_adn_parse(data, record_length, &ph, NULL) == TRUE) {
+               struct ofono_phone_number *own;
+
+               own = g_new(struct ofono_phone_number, 1);
+               memcpy(own, &ph, sizeof(struct ofono_phone_number));
+               sim->new_numbers = g_slist_prepend(sim->new_numbers, own);
+       }
+
+       if (record != total)
+               return;
+
+check:
+       /* All records retrieved */
+       if (sim->new_numbers)
+               sim->new_numbers = g_slist_reverse(sim->new_numbers);
+
+       if (!numbers_list_equal(sim->new_numbers, sim->own_numbers)) {
+               const char *path = __ofono_atom_get_path(sim->atom);
+               char **own_numbers;
+               DBusConnection *conn = ofono_dbus_get_connection();
+
+               g_slist_foreach(sim->own_numbers, (GFunc) g_free, NULL);
+               g_slist_free(sim->own_numbers);
+               sim->own_numbers = sim->new_numbers;
+
+               own_numbers = get_own_numbers(sim->own_numbers);
+
+               ofono_dbus_signal_array_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "SubscriberNumbers",
+                                               DBUS_TYPE_STRING, &own_numbers);
+
+               g_strfreev(own_numbers);
+       } else {
+               g_slist_foreach(sim->new_numbers, (GFunc) g_free, NULL);
+               g_slist_free(sim->new_numbers);
+       }
+
+       sim->new_numbers = NULL;
+}
+
+static gint service_number_compare(gconstpointer a, gconstpointer b)
+{
+       const struct service_number *sdn = a;
+       const char *id = b;
+
+       return strcmp(sdn->id, id);
+}
+
+static void sim_sdn_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+       int total;
+       struct ofono_phone_number ph;
+       char *alpha;
+       struct service_number *sdn;
+
+       if (!ok)
+               goto check;
+
+       if (record_length < 14 || length < record_length)
+               return;
+
+       total = length / record_length;
+
+       if (sim_adn_parse(data, record_length, &ph, &alpha) == FALSE)
+               goto out;
+
+
+       /* Use phone number if Id is unavailable */
+       if (alpha && alpha[0] == '\0') {
+               g_free(alpha);
+               alpha = NULL;
+       }
+
+       if (alpha == NULL)
+               alpha = g_strdup(phone_number_to_string(&ph));
+
+       if (sim->service_numbers &&
+                       g_slist_find_custom(sim->service_numbers,
+                               alpha, service_number_compare)) {
+               ofono_error("Duplicate EFsdn entries for `%s'",
+                               alpha);
+               g_free(alpha);
+
+               goto out;
+       }
+
+       sdn = g_new(struct service_number, 1);
+       sdn->id = alpha;
+       memcpy(&sdn->ph, &ph, sizeof(struct ofono_phone_number));
+
+       sim->service_numbers = g_slist_prepend(sim->service_numbers, sdn);
+
+out:
+       if (record != total)
+               return;
+
+check:
+       /* All records retrieved */
+       if (sim->service_numbers) {
+               sim->service_numbers = g_slist_reverse(sim->service_numbers);
+               sim->sdn_ready = TRUE;
+       }
+
+       if (sim->sdn_ready) {
+               char **service_numbers;
+
+               service_numbers = get_service_numbers(sim->service_numbers);
+
+               ofono_dbus_signal_dict_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "ServiceNumbers",
+                                               DBUS_TYPE_STRING,
+                                               &service_numbers);
+               g_strfreev(service_numbers);
+       }
+}
+
+static void sim_service_numbers_changed(int id, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (sim->service_numbers) {
+               g_slist_foreach(sim->service_numbers,
+                               (GFunc)service_number_free, NULL);
+               g_slist_free(sim->service_numbers);
+               sim->service_numbers = NULL;
+       }
+
+       ofono_sim_read(sim->context, SIM_EFSDN_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim);
+}
+
+static void sim_own_numbers_update(struct ofono_sim *sim)
+{
+       ofono_sim_read(sim->context, SIM_EFMSISDN_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED, sim_msisdn_read_cb,
+                       sim);
+}
+
+static void sim_own_numbers_changed(int id, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       sim_own_numbers_update(sim);
+}
+
+static void sim_efimg_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       unsigned char *efimg;
+       int num_records;
+
+       if (!ok)
+               return;
+
+       num_records = length / record_length;
+
+       /*
+        * EFimg descriptors are 9 bytes long.
+        * Byte 1 of the record is the number of descriptors per record.
+        */
+       if ((record_length < 10) ||
+                       ((record_length % 9 != 2) && (record_length % 9 != 1)))
+               return;
+
+       if (sim->efimg == NULL) {
+               sim->efimg = g_try_malloc0(num_records * 9);
+               if (sim->efimg == NULL)
+                       return;
+
+               sim->iidf_watch_ids = g_try_new0(unsigned int, num_records);
+               if (sim->iidf_watch_ids == NULL) {
+                       g_free(sim->efimg);
+                       sim->efimg = NULL;
+                       return;
+               }
+
+               sim->efimg_length = num_records * 9;
+       }
+
+       /*
+        * TBD - if we have more than one descriptor per record,
+        * pick the nicest one.  For now we use the first one.
+        */
+
+       /* copy descriptor into slot for this record */
+       efimg = &sim->efimg[(record - 1) * 9];
+
+       memcpy(efimg, &data[1], 9);
+}
+
+static void sim_efimg_changed(int id, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       int i, watch;
+
+       if (sim->efimg != NULL) {
+               for (i = sim->efimg_length / 9 - 1; i >= 0; i--) {
+                       watch = sim->iidf_watch_ids[i];
+                       if (watch == 0)
+                               continue;
+
+                       ofono_sim_remove_file_watch(sim->context, watch);
+               }
+
+               g_free(sim->efimg);
+               sim->efimg = NULL;
+               sim->efimg_length = 0;
+               g_free(sim->iidf_watch_ids);
+               sim->iidf_watch_ids = NULL;
+       }
+
+       ofono_sim_read(sim->context, SIM_EFIMG_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim);
+
+       /* TODO: notify D-bus clients */
+}
+
+static void sim_ready(enum ofono_sim_state new_state, void *user)
+{
+       struct ofono_sim *sim = user;
+
+       if (new_state != OFONO_SIM_STATE_READY)
+               return;
+
+       sim_own_numbers_update(sim);
+       ofono_sim_add_file_watch(sim->context, SIM_EFMSISDN_FILEID,
+                                       sim_own_numbers_changed, sim, NULL);
+
+       ofono_sim_read(sim->context, SIM_EFSDN_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim);
+       ofono_sim_add_file_watch(sim->context, SIM_EFSDN_FILEID,
+                                       sim_service_numbers_changed, sim, NULL);
+
+       ofono_sim_read(sim->context, SIM_EFIMG_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim);
+       ofono_sim_add_file_watch(sim->context, SIM_EFIMG_FILEID,
+                                       sim_efimg_changed, sim, NULL);
+}
+
+static void sim_set_ready(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return;
+
+       if (sim->state != OFONO_SIM_STATE_INSERTED &&
+                       sim->state != OFONO_SIM_STATE_LOCKED_OUT)
+               return;
+
+       sim->state = OFONO_SIM_STATE_READY;
+
+       sim_fs_check_version(sim->simfs);
+
+       call_state_watches(sim);
+}
+
+static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,
+               void *data)
+{
+       struct ofono_sim *sim = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Unable to read IMSI, emergency calls only");
+               return;
+       }
+
+       sim->imsi = g_strdup(imsi);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "SubscriberIdentity",
+                                               DBUS_TYPE_STRING, &sim->imsi);
+
+       if (sim->mnc_length) {
+               const char *str;
+
+               strncpy(sim->mcc, sim->imsi, OFONO_MAX_MCC_LENGTH);
+               sim->mcc[OFONO_MAX_MCC_LENGTH] = '\0';
+               strncpy(sim->mnc, sim->imsi + OFONO_MAX_MCC_LENGTH,
+                       sim->mnc_length);
+               sim->mnc[sim->mnc_length] = '\0';
+
+               str = sim->mcc;
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "MobileCountryCode",
+                                               DBUS_TYPE_STRING, &str);
+
+               str = sim->mnc;
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "MobileNetworkCode",
+                                               DBUS_TYPE_STRING, &str);
+       }
+
+       sim_set_ready(sim);
+}
+
+static void sim_retrieve_imsi(struct ofono_sim *sim)
+{
+       if (sim->driver->read_imsi == NULL) {
+               ofono_error("IMSI retrieval not implemented,"
+                               " only emergency calls will be available");
+               return;
+       }
+
+       sim->driver->read_imsi(sim, sim_imsi_cb, sim);
+}
+
+static void sim_fdn_enabled(struct ofono_sim *sim)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+       dbus_bool_t val;
+
+       sim->fixed_dialing = TRUE;
+
+       val = sim->fixed_dialing;
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "FixedDialing",
+                                               DBUS_TYPE_BOOLEAN, &val);
+}
+
+static void sim_bdn_enabled(struct ofono_sim *sim)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+       dbus_bool_t val;
+
+       sim->barred_dialing = TRUE;
+
+       val = sim->barred_dialing;
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "BarredDialing",
+                                               DBUS_TYPE_BOOLEAN, &val);
+}
+
+static void sim_efbdn_info_read_cb(int ok, unsigned char file_status,
+                                       int total_length, int record_length,
+                                       void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (!ok)
+               goto out;
+
+       if (file_status & SIM_FILE_STATUS_VALID)
+               sim_bdn_enabled(sim);
+
+out:
+       if (sim->fixed_dialing != TRUE &&
+                       sim->barred_dialing != TRUE)
+               sim_retrieve_imsi(sim);
+}
+
+static gboolean check_bdn_status(struct ofono_sim *sim)
+{
+       /*
+        * Check the status of Barred Dialing in the SIM-card
+        * (TS 11.11/TS 51.011, Section 11.5.1: BDN capability request).
+        * If BDN is allocated, activated in EFsst and EFbdn is validated,
+        * halt the SIM initialization.
+        */
+       if (sim_sst_is_active(sim->efsst, sim->efsst_length,
+                       SIM_SST_SERVICE_BDN)) {
+               sim_fs_read_info(sim->context, SIM_EFBDN_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_FIXED,
+                               sim_efbdn_info_read_cb, sim);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void sim_efadn_info_read_cb(int ok, unsigned char file_status,
+                                       int total_length, int record_length,
+                                       void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (!ok)
+               goto out;
+
+       if (!(file_status & SIM_FILE_STATUS_VALID))
+               sim_fdn_enabled(sim);
+
+out:
+       if (check_bdn_status(sim) != TRUE) {
+               if (sim->fixed_dialing != TRUE &&
+                               sim->barred_dialing != TRUE)
+                       sim_retrieve_imsi(sim);
+       }
+}
+
+static void sim_efsst_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (!ok)
+               goto out;
+
+       if (length < 2) {
+               ofono_error("EFsst shall contain at least two bytes");
+               goto out;
+       }
+
+       sim->efsst = g_memdup(data, length);
+       sim->efsst_length = length;
+
+       /*
+        * Check if Fixed Dialing is enabled in the SIM-card
+        * (TS 11.11/TS 51.011, Section 11.5.1: FDN capability request).
+        * If FDN is activated and ADN is invalidated,
+        * don't continue initialization routine.
+        */
+       if (sim_sst_is_active(sim->efsst, sim->efsst_length,
+                               SIM_SST_SERVICE_FDN)) {
+               sim_fs_read_info(sim->context, SIM_EFADN_FILEID,
+                                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                                       sim_efadn_info_read_cb, sim);
+               return;
+       }
+
+       if (check_bdn_status(sim) == TRUE)
+               return;
+
+out:
+       sim_retrieve_imsi(sim);
+}
+
+static void sim_efest_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       gboolean available;
+
+       if (!ok)
+               goto out;
+
+       if (length < 1) {
+               ofono_error("EFest shall contain at least one byte");
+               goto out;
+       }
+
+       sim->efest = g_memdup(data, length);
+       sim->efest_length = length;
+
+       /*
+        * Check if Fixed Dialing is enabled in the USIM-card
+        * (TS 31.102, Section 5.3.2: FDN capability request).
+        * If FDN is activated, don't continue initialization routine.
+        */
+       available = sim_ust_is_available(sim->efust, sim->efust_length,
+                                               SIM_UST_SERVICE_FDN);
+       if (available && sim_est_is_active(sim->efest, sim->efest_length,
+                                               SIM_EST_SERVICE_FDN))
+               sim_fdn_enabled(sim);
+
+       /*
+        * Check the status of Barred Dialing in the USIM-card
+        * (TS 31.102, Section 5.3.2: BDN capability request).
+        * If BDN service is enabled, halt the USIM initialization.
+        */
+       available = sim_ust_is_available(sim->efust, sim->efust_length,
+                                               SIM_UST_SERVICE_BDN);
+       if (available && sim_est_is_active(sim->efest, sim->efest_length,
+                                               SIM_EST_SERVICE_BDN))
+               sim_bdn_enabled(sim);
+
+out:
+       if (sim->fixed_dialing != TRUE &&
+                       sim->barred_dialing != TRUE)
+               sim_retrieve_imsi(sim);
+}
+
+static void sim_efust_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (!ok)
+               goto out;
+
+       if (length < 1) {
+               ofono_error("EFust shall contain at least one byte");
+               goto out;
+       }
+
+       sim->efust = g_memdup(data, length);
+       sim->efust_length = length;
+
+       /*
+        * Check whether the SIM provides EFest file
+        * According to 3GPP TS 31.102 section 4.2.47, EFest file
+        * shall be present if FDN or BDN or EST is available
+        * Lets be paranoid and check for the special cases as well
+        * where EST is not available(FDN or BDN available), but EFest
+        * is present
+        */
+       if (sim_ust_is_available(sim->efust, sim->efust_length,
+                               SIM_UST_SERVICE_ENABLED_SERVICE_TABLE) ||
+                       sim_ust_is_available(sim->efust, sim->efust_length,
+                               SIM_UST_SERVICE_FDN) ||
+                       sim_ust_is_available(sim->efust, sim->efust_length,
+                               SIM_UST_SERVICE_BDN)) {
+               ofono_sim_read(sim->context, SIM_EFEST_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               sim_efest_read_cb, sim);
+
+               return;
+       }
+
+out:
+       sim_retrieve_imsi(sim);
+}
+
+static void sim_cphs_information_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE;
+
+       if (!ok || length < 3)
+               return;
+
+       if (data[0] == 0x01)
+               sim->cphs_phase = OFONO_SIM_CPHS_PHASE_1G;
+       else if (data[0] >= 0x02)
+               sim->cphs_phase = OFONO_SIM_CPHS_PHASE_2G;
+
+       memcpy(sim->cphs_service_table, data + 1, 2);
+}
+
+static void sim_ad_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       int new_mnc_length;
+
+       if (!ok)
+               return;
+
+       if (length < 4)
+               return;
+
+       new_mnc_length = data[3] & 0xf;
+
+       /* sanity check for potential invalid values */
+       if (new_mnc_length < 2 || new_mnc_length > 3)
+               return;
+
+       sim->mnc_length = new_mnc_length;
+}
+
+static void sim_efphase_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (!ok || length != 1) {
+               sim->phase = OFONO_SIM_PHASE_3G;
+
+               ofono_sim_read(sim->context, SIM_EFUST_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               sim_efust_read_cb, sim);
+
+               return;
+       }
+
+       switch (data[0]) {
+       case 0:
+               sim->phase = OFONO_SIM_PHASE_1G;
+               break;
+       case 2:
+               sim->phase = OFONO_SIM_PHASE_2G;
+               break;
+       case 3:
+               sim->phase = OFONO_SIM_PHASE_2G_PLUS;
+               break;
+       default:
+               ofono_error("Unknown phase");
+               return;
+       }
+
+       ofono_sim_read(sim->context, SIM_EFSST_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_efsst_read_cb, sim);
+}
+
+static void sim_initialize_after_pin(struct ofono_sim *sim)
+{
+       sim->context = ofono_sim_context_create(sim);
+
+       ofono_sim_read(sim->context, SIM_EFPHASE_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_efphase_read_cb, sim);
+
+       ofono_sim_read(sim->context, SIM_EFAD_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_ad_read_cb, sim);
+
+       /*
+        * Read CPHS-support bits, this is still part of the SIM
+        * initialisation but no order is specified for it.
+        */
+       ofono_sim_read(sim->context, SIM_EF_CPHS_INFORMATION_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_cphs_information_read_cb, sim);
+}
+
+static void sim_efli_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (!ok)
+               return;
+
+       sim->efli = g_memdup(data, length);
+       sim->efli_length = length;
+}
+
+/* Detect whether the file is in EFli format, as opposed to 51.011 EFlp */
+static gboolean sim_efli_format(const unsigned char *ef, int length)
+{
+       int i;
+
+       if (length & 1)
+               return FALSE;
+
+       for (i = 0; i < length; i += 2) {
+               if (ef[i] == 0xff && ef[i+1] == 0xff)
+                       continue;
+
+               /*
+                * ISO 639 country codes are each two lower-case SMS 7-bit
+                * characters while CB DCS language codes are in ranges
+                * (0 - 15) or (32 - 47), so the ranges don't overlap
+                */
+               if (g_ascii_isalpha(ef[i]) == 0)
+                       return FALSE;
+
+               if (g_ascii_isalpha(ef[i+1]) == 0)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static GSList *parse_language_list(const unsigned char *ef, int length)
+{
+       int i;
+       GSList *ret = NULL;
+
+       for (i = 0; i < length; i += 2) {
+               if (ef[i] > 0x7f || ef[i+1] > 0x7f)
+                       continue;
+
+               /*
+                * ISO 639 codes contain only characters that are coded
+                * identically in SMS 7 bit charset, ASCII or UTF8 so
+                * no conversion.
+                */
+               ret = g_slist_prepend(ret, g_ascii_strdown((char *)ef + i, 2));
+       }
+
+       if (ret)
+               ret = g_slist_reverse(ret);
+
+       return ret;
+}
+
+static GSList *parse_eflp(const unsigned char *eflp, int length)
+{
+       int i;
+       char code[3];
+       GSList *ret = NULL;
+
+       for (i = 0; i < length; i++) {
+               if (iso639_2_from_language(eflp[i], code) == FALSE)
+                       continue;
+
+               ret = g_slist_prepend(ret, g_strdup(code));
+       }
+
+       if (ret)
+               ret = g_slist_reverse(ret);
+
+       return ret;
+}
+
+static char **concat_lang_prefs(GSList *a, GSList *b)
+{
+       GSList *l, *k;
+       char **ret;
+       int i = 0;
+       int total = g_slist_length(a) + g_slist_length(b);
+
+       if (total == 0)
+               return NULL;
+
+       ret = g_new0(char *, total + 1);
+
+       for (l = a; l; l = l->next)
+               ret[i++] = g_strdup(l->data);
+
+       for (l = b; l; l = l->next) {
+               gboolean duplicate = FALSE;
+
+               for (k = a; k; k = k->next)
+                       if (!strcmp(k->data, l->data))
+                               duplicate = TRUE;
+
+               if (duplicate)
+                       continue;
+
+               ret[i++] = g_strdup(l->data);
+       }
+
+       return ret;
+}
+
+static void sim_efpl_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       const char *path = __ofono_atom_get_path(sim->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       gboolean efli_format = TRUE;
+       GSList *efli = NULL;
+       GSList *efpl = NULL;
+
+       if (!ok || length < 2)
+               goto skip_efpl;
+
+       efpl = parse_language_list(data, length);
+
+skip_efpl:
+       if (sim->efli && sim->efli_length > 0) {
+               efli_format = sim_efli_format(sim->efli, sim->efli_length);
+
+               if (efli_format)
+                       efli = parse_language_list(sim->efli, sim->efli_length);
+               else
+                       efli = parse_eflp(sim->efli, sim->efli_length);
+       }
+
+       /*
+        * If efli_format is TRUE, make a list of languages in both files in
+        * order of preference following TS 31.102.
+        * Quoting 31.102 Section 5.1.1.2:
+        * The preferred language selection shall always use the EFLI in
+        * preference to the EFPL at the MF unless:
+        * - if the EFLI has the value 'FFFF' in its highest priority position,
+        *   then the preferred language selection shall be the language
+        *   preference in the EFPL at the MF level
+        * Otherwise in order of preference according to TS 51.011
+        */
+       if (efli_format) {
+               if (sim->efli_length >= 2 && sim->efli[0] == 0xff &&
+                               sim->efli[1] == 0xff)
+                       sim->language_prefs = concat_lang_prefs(NULL, efpl);
+               else
+                       sim->language_prefs = concat_lang_prefs(efli, efpl);
+       } else {
+               sim->language_prefs = concat_lang_prefs(efpl, efli);
+       }
+
+       if (sim->efli) {
+               g_free(sim->efli);
+               sim->efli = NULL;
+               sim->efli_length = 0;
+       }
+
+       if (efli) {
+               g_slist_foreach(efli, (GFunc)g_free, NULL);
+               g_slist_free(efli);
+       }
+
+       if (efpl) {
+               g_slist_foreach(efpl, (GFunc)g_free, NULL);
+               g_slist_free(efpl);
+       }
+
+       if (sim->language_prefs != NULL)
+               ofono_dbus_signal_array_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "PreferredLanguages",
+                                               DBUS_TYPE_STRING,
+                                               &sim->language_prefs);
+
+       /* Proceed with sim initialization if we're not merely updating */
+       if (!sim->language_prefs_update)
+               __ofono_sim_recheck_pin(sim);
+
+       sim->language_prefs_update = FALSE;
+}
+
+static void sim_iccid_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       const char *path = __ofono_atom_get_path(sim->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       char iccid[21]; /* ICCID max length is 20 + 1 for NULL */
+
+       if (!ok || length < 10)
+               return;
+
+       extract_bcd_number(data, length, iccid);
+       iccid[20] = '\0';
+       sim->iccid = g_strdup(iccid);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "CardIdentifier",
+                                               DBUS_TYPE_STRING,
+                                               &sim->iccid);
+}
+
+static void sim_iccid_changed(int id, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (sim->iccid) {
+               g_free(sim->iccid);
+               sim->iccid = NULL;
+       }
+
+       ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_iccid_read_cb, sim);
+}
+
+static void sim_efli_efpl_changed(int id, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (sim->efli != NULL) /* This shouldn't happen */
+               return;
+
+       if (sim->language_prefs) {
+               g_strfreev(sim->language_prefs);
+               sim->language_prefs = NULL;
+       }
+
+       sim->language_prefs_update = TRUE;
+
+       ofono_sim_read(sim->early_context, SIM_EFLI_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_efli_read_cb, sim);
+
+       ofono_sim_read(sim->early_context, SIM_EFPL_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_efpl_read_cb, sim);
+}
+
+static void sim_initialize(struct ofono_sim *sim)
+{
+       /*
+        * Perform SIM initialization according to 3GPP 31.102 Section 5.1.1.2
+        * The assumption here is that if sim manager is being initialized,
+        * then sim commands are implemented, and the sim manager is then
+        * responsible for checking the PIN, reading the IMSI and signaling
+        * SIM ready condition.
+        *
+        * The procedure according to 31.102, 51.011, 11.11 and CPHS 4.2 is
+        * roughly:
+        *
+        * Read EFecc
+        * Read EFli and EFpl
+        * SIM Pin check
+        * Request SIM phase (only in 51.011)
+        * Administrative information request (read EFad)
+        * Request CPHS Information (only in CPHS 4.2)
+        * Read EFsst (only in 11.11 & 51.011)
+        * Read EFust (only in 31.102)
+        * Read EFest (only in 31.102)
+        * Read IMSI
+        *
+        * At this point we signal the SIM ready condition and allow
+        * arbitrary files to be written or read, assuming their presence
+        * in the EFust
+        */
+
+       if (sim->early_context == NULL)
+               sim->early_context = ofono_sim_context_create(sim);
+
+       /* Grab the EFiccid which is always available */
+       ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_iccid_read_cb, sim);
+       ofono_sim_add_file_watch(sim->early_context, SIM_EF_ICCID_FILEID,
+                                       sim_iccid_changed, sim, NULL);
+
+       /* EFecc is read by the voicecall atom */
+
+       /*
+        * According to 31.102 the EFli is read first and EFpl is then
+        * only read if none of the EFli languages are supported by user
+        * interface.  51.011 mandates the exact opposite, making EFpl/EFelp
+        * preferred over EFlp (same EFid as EFli, different format).
+        * However we don't depend on the user interface and so
+        * need to read both files now.
+        */
+       ofono_sim_read(sim->early_context, SIM_EFLI_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_efli_read_cb, sim);
+       ofono_sim_add_file_watch(sim->early_context, SIM_EFLI_FILEID,
+                                       sim_efli_efpl_changed, sim, NULL);
+
+       ofono_sim_read(sim->early_context, SIM_EFPL_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_efpl_read_cb, sim);
+       ofono_sim_add_file_watch(sim->early_context, SIM_EFPL_FILEID,
+                                       sim_efli_efpl_changed, sim, NULL);
+}
+
+struct ofono_sim_context *ofono_sim_context_create(struct ofono_sim *sim)
+{
+       if (sim == NULL || sim->simfs == NULL)
+               return NULL;
+
+       return sim_fs_context_new(sim->simfs);
+}
+
+void ofono_sim_context_free(struct ofono_sim_context *context)
+{
+       return sim_fs_context_free(context);
+}
+
+int ofono_sim_read_bytes(struct ofono_sim_context *context, int id,
+                       unsigned short offset, unsigned short num_bytes,
+                       ofono_sim_file_read_cb_t cb, void *data)
+{
+       if (num_bytes == 0)
+               return -1;
+
+       return sim_fs_read(context, id, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               offset, num_bytes, cb, data);
+}
+
+int ofono_sim_read(struct ofono_sim_context *context, int id,
+                       enum ofono_sim_file_structure expected_type,
+                       ofono_sim_file_read_cb_t cb, void *data)
+{
+       return sim_fs_read(context, id, expected_type, 0, 0, cb, data);
+}
+
+int ofono_sim_write(struct ofono_sim_context *context, int id,
+                       ofono_sim_file_write_cb_t cb,
+                       enum ofono_sim_file_structure structure, int record,
+                       const unsigned char *data, int length, void *userdata)
+{
+       return sim_fs_write(context, id, cb, structure, record, data, length,
+                               userdata);
+}
+
+unsigned int ofono_sim_add_file_watch(struct ofono_sim_context *context,
+                                       int id, ofono_sim_file_changed_cb_t cb,
+                                       void *userdata,
+                                       ofono_destroy_func destroy)
+{
+       return sim_fs_file_watch_add(context, id, cb, userdata, destroy);
+}
+
+void ofono_sim_remove_file_watch(struct ofono_sim_context *context,
+                                       unsigned int id)
+{
+       sim_fs_file_watch_remove(context, id);
+}
+
+const char *ofono_sim_get_imsi(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return NULL;
+
+       return sim->imsi;
+}
+
+const char *ofono_sim_get_mcc(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return NULL;
+
+       return sim->mcc;
+}
+
+const char *ofono_sim_get_mnc(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return NULL;
+
+       return sim->mnc;
+}
+
+const char *ofono_sim_get_spn(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return NULL;
+
+       return sim->spn;
+}
+
+enum ofono_sim_phase ofono_sim_get_phase(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return OFONO_SIM_PHASE_UNKNOWN;
+
+       return sim->phase;
+}
+
+enum ofono_sim_cphs_phase ofono_sim_get_cphs_phase(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return OFONO_SIM_CPHS_PHASE_NONE;
+
+       return sim->cphs_phase;
+}
+
+const unsigned char *ofono_sim_get_cphs_service_table(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return NULL;
+
+       return sim->cphs_service_table;
+}
+
+ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim,
+                                               int ust_service,
+                                               int sst_service)
+{
+       if (sim->efust)
+               return sim_ust_is_available(sim->efust, sim->efust_length,
+                                               ust_service);
+
+       if (sim->efsst)
+               return sim_sst_is_active(sim->efsst, sim->efsst_length,
+                                               sst_service);
+
+       return FALSE;
+}
+
+ofono_bool_t __ofono_sim_cphs_service_available(struct ofono_sim *sim,
+                                               int cphs_service)
+{
+       return sim_cphs_is_active(sim->cphs_service_table, cphs_service);
+}
+
+static void sim_inserted_update(struct ofono_sim *sim)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+       dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "Present",
+                                               DBUS_TYPE_BOOLEAN, &present);
+}
+
+static void sim_free_early_state(struct ofono_sim *sim)
+{
+       if (sim->iccid) {
+               g_free(sim->iccid);
+               sim->iccid = NULL;
+       }
+
+       if (sim->efli) {
+               g_free(sim->efli);
+               sim->efli = NULL;
+               sim->efli_length = 0;
+       }
+
+       if (sim->language_prefs) {
+               g_strfreev(sim->language_prefs);
+               sim->language_prefs = NULL;
+       }
+
+       if (sim->early_context) {
+               ofono_sim_context_free(sim->early_context);
+               sim->early_context = NULL;
+       }
+}
+
+static void sim_free_main_state(struct ofono_sim *sim)
+{
+       int i;
+
+       for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
+               sim->pin_retries[i] = -1;
+
+       memset(sim->locked_pins, 0, sizeof(sim->locked_pins));
+
+       if (sim->imsi) {
+               g_free(sim->imsi);
+               sim->imsi = NULL;
+       }
+
+       sim->mcc[0] = '\0';
+       sim->mnc[0] = '\0';
+
+       if (sim->own_numbers) {
+               g_slist_foreach(sim->own_numbers, (GFunc)g_free, NULL);
+               g_slist_free(sim->own_numbers);
+               sim->own_numbers = NULL;
+       }
+
+       if (sim->service_numbers) {
+               g_slist_foreach(sim->service_numbers,
+                               (GFunc)service_number_free, NULL);
+               g_slist_free(sim->service_numbers);
+               sim->service_numbers = NULL;
+               sim->sdn_ready = FALSE;
+       }
+
+       if (sim->efust) {
+               g_free(sim->efust);
+               sim->efust = NULL;
+               sim->efust_length = 0;
+       }
+
+       if (sim->efest) {
+               g_free(sim->efest);
+               sim->efest = NULL;
+               sim->efest_length = 0;
+       }
+
+       if (sim->efsst) {
+               g_free(sim->efsst);
+               sim->efsst = NULL;
+               sim->efsst_length = 0;
+       }
+
+       sim->phase = OFONO_SIM_PHASE_UNKNOWN;
+       sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE;
+       sim->mnc_length = 0;
+       memset(sim->cphs_service_table, 0, 2);
+
+       if (sim->efimg) {
+               g_free(sim->efimg);
+               sim->efimg = NULL;
+               sim->efimg_length = 0;
+               g_free(sim->iidf_watch_ids);
+               sim->iidf_watch_ids = NULL;
+       }
+
+       g_free(sim->iidf_image);
+       sim->iidf_image = NULL;
+
+       sim->fixed_dialing = FALSE;
+       sim->barred_dialing = FALSE;
+
+       if (sim->context) {
+               ofono_sim_context_free(sim->context);
+               sim->context = NULL;
+       }
+}
+
+static void sim_free_state(struct ofono_sim *sim)
+{
+       sim_free_early_state(sim);
+       sim_free_main_state(sim);
+}
+
+void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted)
+{
+       if (inserted == TRUE && sim->state == OFONO_SIM_STATE_NOT_PRESENT)
+               sim->state = OFONO_SIM_STATE_INSERTED;
+       else if (inserted == FALSE && sim->state != OFONO_SIM_STATE_NOT_PRESENT)
+               sim->state = OFONO_SIM_STATE_NOT_PRESENT;
+       else
+               return;
+
+       if (!__ofono_atom_get_registered(sim->atom))
+               return;
+
+       sim_inserted_update(sim);
+       call_state_watches(sim);
+
+       if (inserted)
+               sim_initialize(sim);
+       else
+               sim_free_state(sim);
+}
+
+unsigned int ofono_sim_add_state_watch(struct ofono_sim *sim,
+                                       ofono_sim_state_event_cb_t notify,
+                                       void *data, ofono_destroy_func destroy)
+{
+       struct ofono_watchlist_item *item;
+
+       DBG("%p", sim);
+
+       if (sim == NULL)
+               return 0;
+
+       if (notify == NULL)
+               return 0;
+
+       item = g_new0(struct ofono_watchlist_item, 1);
+
+       item->notify = notify;
+       item->destroy = destroy;
+       item->notify_data = data;
+
+       return __ofono_watchlist_add_item(sim->state_watches, item);
+}
+
+void ofono_sim_remove_state_watch(struct ofono_sim *sim, unsigned int id)
+{
+       __ofono_watchlist_remove_item(sim->state_watches, id);
+}
+
+enum ofono_sim_state ofono_sim_get_state(struct ofono_sim *sim)
+{
+       if (sim == NULL)
+               return OFONO_SIM_STATE_NOT_PRESENT;
+
+       return sim->state;
+}
+
+static void spn_watch_cb(gpointer data, gpointer user_data)
+{
+       struct ofono_watchlist_item *item = data;
+       struct ofono_sim *sim = user_data;
+
+       if (item->notify)
+               ((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc,
+                                                       item->notify_data);
+}
+
+static inline void spn_watches_notify(struct ofono_sim *sim)
+{
+       if (sim->spn_watches->items)
+               g_slist_foreach(sim->spn_watches->items, spn_watch_cb, sim);
+
+       sim->flags &= ~SIM_FLAG_READING_SPN;
+}
+
+static void sim_spn_set(struct ofono_sim *sim, const void *data, int length,
+                                               const unsigned char *dc)
+{
+       g_free(sim->spn);
+       sim->spn = NULL;
+
+       g_free(sim->spn_dc);
+       sim->spn_dc = NULL;
+
+       if (data == NULL)
+               goto notify;
+
+       /*
+        * TS 31.102 says:
+        *
+        * the string shall use:
+        *
+        * - either the SMS default 7-bit coded alphabet as defined in
+        *   TS 23.038 [5] with bit 8 set to 0. The string shall be left
+        *   justified. Unused bytes shall be set to 'FF'.
+        *
+        * - or one of the UCS2 code options defined in the annex of TS
+        *   31.101 [11].
+        *
+        * 31.101 has no such annex though.  51.101 refers to Annex B of
+        * itself which is not there either.  11.11 contains the same
+        * paragraph as 51.101 and has an Annex B which we implement.
+        */
+       sim->spn = sim_string_to_utf8(data, length);
+       if (sim->spn == NULL) {
+               ofono_error("EFspn read successfully, but couldn't parse");
+               goto notify;
+       }
+
+       if (strlen(sim->spn) == 0) {
+               g_free(sim->spn);
+               sim->spn = NULL;
+               goto notify;
+       }
+
+       if (dc)
+               sim->spn_dc = g_memdup(dc, 1);
+
+notify:
+       spn_watches_notify(sim);
+}
+
+static void sim_cphs_spn_short_read_cb(int ok, int length, int record,
+                                       const unsigned char *data,
+                                       int record_length, void *user_data)
+{
+       struct ofono_sim *sim = user_data;
+
+       if (!ok) {
+               sim_spn_set(sim, NULL, 0, NULL);
+               return;
+       }
+
+       sim_spn_set(sim, data, length, NULL);
+}
+
+static void sim_cphs_spn_read_cb(int ok, int length, int record,
+                                       const unsigned char *data,
+                                       int record_length, void *user_data)
+{
+       struct ofono_sim *sim = user_data;
+
+       if (!ok) {
+               if (__ofono_sim_cphs_service_available(sim,
+                                               SIM_CPHS_SERVICE_SHORT_SPN))
+                       ofono_sim_read(sim->context,
+                                       SIM_EF_CPHS_SPN_SHORT_FILEID,
+                                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                                       sim_cphs_spn_short_read_cb, sim);
+               else
+                       sim_spn_set(sim, NULL, 0, NULL);
+
+               return;
+       }
+
+       sim_spn_set(sim, data, length, NULL);
+}
+
+static void sim_spn_read_cb(int ok, int length, int record,
+                               const unsigned char *data,
+                               int record_length, void *user_data)
+{
+       struct ofono_sim *sim = user_data;
+
+       if (!ok) {
+               ofono_sim_read(sim->context, SIM_EF_CPHS_SPN_FILEID,
+                               OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                               sim_cphs_spn_read_cb, sim);
+
+               return;
+       }
+
+       sim_spn_set(sim, data + 1, length - 1, data);
+}
+
+static void sim_spn_changed(int id, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+
+       if (sim->flags & SIM_FLAG_READING_SPN)
+               return;
+
+       sim->flags |= SIM_FLAG_READING_SPN;
+       ofono_sim_read(sim->context, SIM_EFSPN_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       sim_spn_read_cb, sim);
+}
+
+static void sim_spn_init(struct ofono_sim *sim)
+{
+       sim->ef_spn_watch = ofono_sim_add_file_watch(sim->context,
+                                       SIM_EFSPN_FILEID, sim_spn_changed, sim,
+                                       NULL);
+
+       sim->cphs_spn_watch = ofono_sim_add_file_watch(sim->context,
+                                       SIM_EF_CPHS_SPN_FILEID,
+                                       sim_spn_changed, sim, NULL);
+
+       if (__ofono_sim_cphs_service_available(sim,
+                                               SIM_CPHS_SERVICE_SHORT_SPN))
+               sim->cphs_spn_short_watch = ofono_sim_add_file_watch(
+                               sim->context, SIM_EF_CPHS_SPN_SHORT_FILEID,
+                               sim_spn_changed, sim, NULL);
+}
+
+static void sim_spn_close(struct ofono_sim *sim)
+{
+       __ofono_watchlist_free(sim->spn_watches);
+       sim->spn_watches = NULL;
+
+       /*
+        * We have not initialized SPN logic at all yet, either because
+        * no netreg / gprs atom has been needed or we have not reached the
+        * post_sim state
+        */
+       if (sim->ef_spn_watch == 0)
+               return;
+
+       ofono_sim_remove_file_watch(sim->context, sim->ef_spn_watch);
+       sim->ef_spn_watch = 0;
+
+       ofono_sim_remove_file_watch(sim->context, sim->cphs_spn_watch);
+       sim->cphs_spn_watch = 0;
+
+       if (sim->cphs_spn_short_watch) {
+               ofono_sim_remove_file_watch(sim->context,
+                                               sim->cphs_spn_short_watch);
+               sim->cphs_spn_short_watch = 0;
+       }
+
+       sim->flags &= ~SIM_FLAG_READING_SPN;
+
+       g_free(sim->spn);
+       sim->spn = NULL;
+
+       g_free(sim->spn_dc);
+       sim->spn_dc = NULL;
+}
+
+gboolean ofono_sim_add_spn_watch(struct ofono_sim *sim, unsigned int *id,
+                                       ofono_sim_spn_cb_t cb, void *data,
+                                       ofono_destroy_func destroy)
+{
+       struct ofono_watchlist_item *item;
+       unsigned int watch_id;
+
+       DBG("%p", sim);
+
+       if (sim == NULL)
+               return 0;
+
+       item = g_new0(struct ofono_watchlist_item, 1);
+
+       item->notify = cb;
+       item->destroy = destroy;
+       item->notify_data = data;
+
+       watch_id = __ofono_watchlist_add_item(sim->spn_watches, item);
+       if (watch_id == 0)
+               return FALSE;
+
+       *id = watch_id;
+
+       if (sim->ef_spn_watch == 0) {
+               sim_spn_init(sim);
+               sim_spn_changed(0, sim);
+               return TRUE;
+       }
+
+       if (sim->flags & SIM_FLAG_READING_SPN)
+               return TRUE;
+
+       ((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc,
+                                                       item->notify_data);
+       return TRUE;
+}
+
+gboolean ofono_sim_remove_spn_watch(struct ofono_sim *sim, unsigned int *id)
+{
+       gboolean ret;
+
+       DBG("%p", sim);
+
+       if (sim == NULL)
+               return FALSE;
+
+       ret = __ofono_watchlist_remove_item(sim->spn_watches, *id);
+       if (ret == TRUE)
+               *id = 0;
+
+       return ret;
+}
+
+static void sim_pin_query_cb(const struct ofono_error *error,
+                               enum ofono_sim_password_type pin_type,
+                               void *data)
+{
+       struct ofono_sim *sim = data;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sim->atom);
+       const char *pin_name;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Querying PIN authentication state failed");
+
+               goto checkdone;
+       }
+
+       if (sim->pin_type != pin_type) {
+               sim->pin_type = pin_type;
+               pin_name = sim_passwd_name(pin_type);
+
+               if (pin_type != OFONO_SIM_PASSWORD_NONE &&
+                               password_is_pin(pin_type) == FALSE)
+                       pin_type = puk2pin(pin_type);
+
+               if (pin_type != OFONO_SIM_PASSWORD_INVALID)
+                       sim->locked_pins[pin_type] = TRUE;
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_SIM_MANAGER_INTERFACE,
+                                               "PinRequired", DBUS_TYPE_STRING,
+                                               &pin_name);
+       }
+
+       switch (pin_type) {
+       case OFONO_SIM_PASSWORD_NONE:
+       case OFONO_SIM_PASSWORD_SIM_PIN2:
+       case OFONO_SIM_PASSWORD_SIM_PUK2:
+               break;
+       default:
+               if (sim->state == OFONO_SIM_STATE_READY) {
+                       /* Force the sim state out of READY */
+                       sim_free_main_state(sim);
+
+                       sim->state = OFONO_SIM_STATE_LOCKED_OUT;
+                       call_state_watches(sim);
+               }
+               break;
+       }
+
+       sim_pin_retries_check(sim);
+
+checkdone:
+       switch (pin_type) {
+       case OFONO_SIM_PASSWORD_SIM_PIN2:
+       case OFONO_SIM_PASSWORD_SIM_PUK2:
+               if (sim->state == OFONO_SIM_STATE_READY)
+                       break;
+
+               /* Fall through */
+       case OFONO_SIM_PASSWORD_NONE:
+               sim_initialize_after_pin(sim);
+               break;
+       default:
+               break;
+       }
+}
+
+void __ofono_sim_recheck_pin(struct ofono_sim *sim)
+{
+       if (sim->driver->query_passwd_state == NULL) {
+               sim_initialize_after_pin(sim);
+               return;
+       }
+
+       sim->driver->query_passwd_state(sim, sim_pin_query_cb, sim);
+}
+
+int ofono_sim_driver_register(const struct ofono_sim_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_sim_driver_unregister(const struct ofono_sim_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void emulator_remove_handler(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       ofono_emulator_remove_handler(em, data);
+}
+
+static void sim_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+       struct ofono_sim *sim = __ofono_atom_get_data(atom);
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "+CNUM");
+
+       __ofono_modem_remove_atom_watch(modem, sim->hfp_watch);
+
+       __ofono_watchlist_free(sim->state_watches);
+       sim->state_watches = NULL;
+
+       sim_spn_close(sim);
+
+       g_dbus_unregister_interface(conn, path, OFONO_SIM_MANAGER_INTERFACE);
+       ofono_modem_remove_interface(modem, OFONO_SIM_MANAGER_INTERFACE);
+}
+
+static void sim_remove(struct ofono_atom *atom)
+{
+       struct ofono_sim *sim = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (sim == NULL)
+               return;
+
+       if (sim->driver != NULL && sim->driver->remove != NULL)
+               sim->driver->remove(sim);
+
+       sim_free_state(sim);
+
+       sim_fs_free(sim->simfs);
+       sim->simfs = NULL;
+
+       g_free(sim);
+}
+
+struct ofono_sim *ofono_sim_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_sim *sim;
+       GSList *l;
+       int i;
+
+       if (driver == NULL)
+               return NULL;
+
+       sim = g_try_new0(struct ofono_sim, 1);
+
+       if (sim == NULL)
+               return NULL;
+
+       sim->phase = OFONO_SIM_PHASE_UNKNOWN;
+       sim->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SIM,
+                                               sim_remove, sim);
+
+       for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
+               sim->pin_retries[i] = -1;
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_sim_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(sim, vendor, data) < 0)
+                       continue;
+
+               sim->driver = drv;
+               break;
+       }
+
+       return sim;
+}
+
+static void emulator_cnum_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_sim *sim = userdata;
+       struct ofono_error result;
+       GSList *l;
+       const char *phone;
+       /*
+        * '+CNUM: ,"+",,,4' + phone number + phone type on 3 digits max
+        * + terminating null
+        */
+       char buf[OFONO_MAX_PHONE_NUMBER_LENGTH + 18 + 1];
+
+       result.error = 0;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
+               for (l = sim->own_numbers; l; l = l->next) {
+                       struct ofono_phone_number *ph = l->data;
+
+                       phone = phone_number_to_string(ph);
+                       sprintf(buf, "+CNUM: ,\"%s\",%d,,4", phone, ph->type);
+                       ofono_emulator_send_info(em, buf, l->next == NULL ?
+                                                       TRUE : FALSE);
+               }
+
+               result.type = OFONO_ERROR_TYPE_NO_ERROR;
+               ofono_emulator_send_final(em, &result);
+               break;
+
+       default:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               ofono_emulator_send_final(em, &result);
+       };
+}
+
+static void emulator_hfp_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED)
+               ofono_emulator_add_handler(em, "+CNUM", emulator_cnum_cb, data,
+                                               NULL);
+}
+
+void ofono_sim_register(struct ofono_sim *sim)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(sim->atom);
+       const char *path = __ofono_atom_get_path(sim->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_SIM_MANAGER_INTERFACE,
+                                       sim_methods, sim_signals, NULL,
+                                       sim, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_SIM_MANAGER_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_SIM_MANAGER_INTERFACE);
+       sim->state_watches = __ofono_watchlist_new(g_free);
+       sim->spn_watches = __ofono_watchlist_new(g_free);
+       sim->simfs = sim_fs_new(sim, sim->driver);
+
+       __ofono_atom_register(sim->atom, sim_unregister);
+
+       ofono_sim_add_state_watch(sim, sim_ready, sim, NULL);
+
+       if (sim->state > OFONO_SIM_STATE_NOT_PRESENT)
+               sim_initialize(sim);
+
+       sim->hfp_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                       emulator_hfp_watch, sim, NULL);
+}
+
+void ofono_sim_remove(struct ofono_sim *sim)
+{
+       __ofono_atom_free(sim->atom);
+}
+
+void ofono_sim_set_data(struct ofono_sim *sim, void *data)
+{
+       sim->driver_data = data;
+}
+
+void *ofono_sim_get_data(struct ofono_sim *sim)
+{
+       return sim->driver_data;
+}
+
+static ofono_bool_t is_valid_pin(const char *pin, unsigned int min,
+                                       unsigned int max)
+{
+       unsigned int i;
+
+       /* Pin must not be empty */
+       if (pin == NULL || pin[0] == '\0')
+               return FALSE;
+
+       i = strlen(pin);
+       if (i != strspn(pin, "0123456789"))
+               return FALSE;
+
+       if (min <= i && i <= max)
+               return TRUE;
+
+       return FALSE;
+}
+
+ofono_bool_t __ofono_is_valid_sim_pin(const char *pin,
+                                       enum ofono_sim_password_type type)
+{
+       switch (type) {
+       case OFONO_SIM_PASSWORD_SIM_PIN:
+       case OFONO_SIM_PASSWORD_SIM_PIN2:
+               /* 11.11 Section 9.3 ("CHV"): 4..8 IA-5 digits */
+               return is_valid_pin(pin, 4, 8);
+               break;
+       case OFONO_SIM_PASSWORD_PHSIM_PIN:
+       case OFONO_SIM_PASSWORD_PHFSIM_PIN:
+       case OFONO_SIM_PASSWORD_PHNET_PIN:
+       case OFONO_SIM_PASSWORD_PHNETSUB_PIN:
+       case OFONO_SIM_PASSWORD_PHSP_PIN:
+       case OFONO_SIM_PASSWORD_PHCORP_PIN:
+               /* 22.022 Section 14 4..16 IA-5 digits */
+               return is_valid_pin(pin, 4, 16);
+               break;
+       case OFONO_SIM_PASSWORD_SIM_PUK:
+       case OFONO_SIM_PASSWORD_SIM_PUK2:
+       case OFONO_SIM_PASSWORD_PHFSIM_PUK:
+       case OFONO_SIM_PASSWORD_PHNET_PUK:
+       case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
+       case OFONO_SIM_PASSWORD_PHSP_PUK:
+       case OFONO_SIM_PASSWORD_PHCORP_PUK:
+               /* 11.11 Section 9.3 ("UNBLOCK CHV"), 8 IA-5 digits */
+               return is_valid_pin(pin, 8, 8);
+               break;
+       case OFONO_SIM_PASSWORD_NONE:
+               return is_valid_pin(pin, 0, 8);
+               break;
+       case OFONO_SIM_PASSWORD_INVALID:
+               break;
+       }
+
+       return FALSE;
+}
+
+ofono_bool_t __ofono_is_valid_net_pin(const char *pin)
+{
+       return is_valid_pin(pin, 4, 4);
+}
+
+static void sim_file_changed_flush(struct ofono_sim *sim, int id)
+{
+       int i, imgid;
+
+       if (id == SIM_EFIMG_FILEID)
+               /* All cached images become invalid */
+               sim_fs_image_cache_flush(sim->simfs);
+       else if (sim->efimg) {
+               /*
+                * Data and CLUT for image instances stored in the changed
+                * file need to be re-read.
+                */
+               for (i = sim->efimg_length / 9 - 1; i >= 0; i--) {
+                       imgid = (sim->efimg[i * 9 + 3] << 8) |
+                               sim->efimg[i * 9 + 4];
+
+                       if (imgid == id)
+                               sim_fs_image_cache_flush_file(sim->simfs, i);
+               }
+       }
+
+       sim_fs_cache_flush_file(sim->simfs, id);
+}
+
+void __ofono_sim_refresh(struct ofono_sim *sim, GSList *file_list,
+                       ofono_bool_t full_file_change, ofono_bool_t naa_init)
+{
+       GSList *l;
+       gboolean reinit_naa = naa_init || full_file_change;
+
+       /*
+        * Check if any files used in SIM initialisation procedure
+        * are affected, except EFiccid, EFpl, EFli.
+        */
+       for (l = file_list; l; l = l->next) {
+               struct stk_file *file = l->data;
+               uint32_t mf, df, ef;
+
+               if (file->len != 6)
+                       continue;
+
+               mf = (file->file[0] << 8) | (file->file[1] << 0);
+               df = (file->file[2] << 8) | (file->file[3] << 0);
+               ef = (file->file[4] << 8) | (file->file[5] << 0);
+
+               if (mf != 0x3f00)
+                       continue;
+
+               /*
+                * 8.18: "the path '3F007FFF' indicates the relevant
+                * NAA Application dedicated file;".
+                */
+               if (df == 0x7fff)
+                       df = 0x7f20;
+
+#define DFGSM (0x7f20 << 16)
+#define DFTEL (0x7f10 << 16)
+
+               switch ((df << 16) | ef) {
+               case DFGSM | SIM_EFEST_FILEID:
+               case DFGSM | SIM_EFUST_FILEID: /* aka. EFSST */
+               case DFGSM | SIM_EFPHASE_FILEID:
+               case DFGSM | SIM_EFAD_FILEID:
+               case DFTEL | SIM_EFBDN_FILEID:
+               case DFTEL | SIM_EFADN_FILEID:
+               case DFGSM | SIM_EF_CPHS_INFORMATION_FILEID:
+                       reinit_naa = TRUE;
+                       break;
+               }
+       }
+
+       /* Flush cached content for affected files */
+       if (full_file_change)
+               sim_fs_cache_flush(sim->simfs);
+       else {
+               for (l = file_list; l; l = l->next) {
+                       struct stk_file *file = l->data;
+                       int id = (file->file[file->len - 2] << 8) |
+                               (file->file[file->len - 1] << 0);
+
+                       sim_file_changed_flush(sim, id);
+               }
+       }
+
+       if (reinit_naa) {
+               /* Force the sim state out of READY */
+               sim_free_main_state(sim);
+
+               sim->state = OFONO_SIM_STATE_INSERTED;
+               __ofono_modem_sim_reset(__ofono_atom_get_modem(sim->atom));
+       }
+
+       /*
+        * Notify the subscribers of files that have changed and who
+        * haven't unsubsribed during the SIM state change.
+        */
+       if (full_file_change)
+               sim_fs_notify_file_watches(sim->simfs, -1);
+       else {
+               for (l = file_list; l; l = l->next) {
+                       struct stk_file *file = l->data;
+                       int id = (file->file[file->len - 2] << 8) |
+                               (file->file[file->len - 1] << 0);
+
+                       sim_fs_notify_file_watches(sim->simfs, id);
+               }
+       }
+
+       if (reinit_naa) {
+               /*
+                * REVISIT: There's some concern that on re-insertion the
+                * atoms will start to talk to the SIM before it becomes
+                * ready, on certain SIMs.
+                */
+               /*
+                * Start initialization procedure from after EFiccid,
+                * EFli and EFpl are retrieved.
+                */
+               __ofono_sim_recheck_pin(sim);
+       }
+}
diff --git a/src/simfs.c b/src/simfs.c
new file mode 100644 (file)
index 0000000..58dc41d
--- /dev/null
@@ -0,0 +1,1178 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "ofono.h"
+
+#include "simfs.h"
+#include "simutil.h"
+#include "storage.h"
+
+#define SIM_CACHE_MODE 0600
+#define SIM_CACHE_BASEPATH STORAGEDIR "/%s-%i"
+#define SIM_CACHE_VERSION SIM_CACHE_BASEPATH "/version"
+#define SIM_CACHE_PATH SIM_CACHE_BASEPATH "/%04x"
+#define SIM_CACHE_HEADER_SIZE 39
+#define SIM_FILE_INFO_SIZE 7
+#define SIM_IMAGE_CACHE_BASEPATH STORAGEDIR "/%s-%i/images"
+#define SIM_IMAGE_CACHE_PATH SIM_IMAGE_CACHE_BASEPATH "/%d.xpm"
+
+#define SIM_FS_VERSION 2
+
+static gboolean sim_fs_op_next(gpointer user_data);
+static gboolean sim_fs_op_read_record(gpointer user);
+static gboolean sim_fs_op_read_block(gpointer user_data);
+
+struct sim_fs_op {
+       int id;
+       unsigned char *buffer;
+       enum ofono_sim_file_structure structure;
+       unsigned short offset;
+       gboolean info_only;
+       int num_bytes;
+       int length;
+       int record_length;
+       int current;
+       gconstpointer cb;
+       gboolean is_read;
+       void *userdata;
+       struct ofono_sim_context *context;
+};
+
+static void sim_fs_op_free(struct sim_fs_op *node)
+{
+       g_free(node->buffer);
+       g_free(node);
+}
+
+struct sim_fs {
+       GQueue *op_q;
+       gint op_source;
+       unsigned char bitmap[32];
+       int fd;
+       struct ofono_sim *sim;
+       const struct ofono_sim_driver *driver;
+       GSList *contexts;
+};
+
+void sim_fs_free(struct sim_fs *fs)
+{
+       if (fs == NULL)
+               return;
+
+       if (fs->op_source) {
+               g_source_remove(fs->op_source);
+               fs->op_source = 0;
+       }
+
+       /*
+        * Note: users of sim_fs must not assume that the callback happens
+        * for operations still in progress
+        */
+       if (fs->op_q) {
+               g_queue_foreach(fs->op_q, (GFunc) sim_fs_op_free, NULL);
+               g_queue_free(fs->op_q);
+               fs->op_q = NULL;
+       }
+
+       while (fs->contexts)
+               sim_fs_context_free(fs->contexts->data);
+
+       g_free(fs);
+}
+
+struct file_watch {
+       struct ofono_watchlist_item item;
+       int ef;
+};
+
+struct ofono_sim_context {
+       struct sim_fs *fs;
+       struct ofono_watchlist *file_watches;
+};
+
+struct sim_fs *sim_fs_new(struct ofono_sim *sim,
+                               const struct ofono_sim_driver *driver)
+{
+       struct sim_fs *fs;
+
+       fs = g_try_new0(struct sim_fs, 1);
+       if (fs == NULL)
+               return NULL;
+
+       fs->sim = sim;
+       fs->driver = driver;
+       fs->fd = -1;
+
+       return fs;
+}
+
+struct ofono_sim_context *sim_fs_context_new(struct sim_fs *fs)
+{
+       struct ofono_sim_context *context =
+               g_try_new0(struct ofono_sim_context, 1);
+
+       if (context == NULL)
+               return NULL;
+
+       context->fs = fs;
+       fs->contexts = g_slist_prepend(fs->contexts, context);
+
+       return context;
+}
+
+void sim_fs_context_free(struct ofono_sim_context *context)
+{
+       struct sim_fs *fs = context->fs;
+       int n = 0;
+       struct sim_fs_op *op;
+
+       if (fs->op_q) {
+               while ((op = g_queue_peek_nth(fs->op_q, n)) != NULL) {
+                       if (op->context != context) {
+                               n += 1;
+                               continue;
+                       }
+
+                       if (n == 0) {
+                               op->cb = NULL;
+
+                               n += 1;
+                               continue;
+                       }
+
+                       sim_fs_op_free(op);
+                       g_queue_remove(fs->op_q, op);
+               }
+       }
+
+       if (context->file_watches)
+               __ofono_watchlist_free(context->file_watches);
+
+       fs->contexts = g_slist_remove(fs->contexts, context);
+       g_free(context);
+}
+
+unsigned int sim_fs_file_watch_add(struct ofono_sim_context *context, int id,
+                                       ofono_sim_file_changed_cb_t cb,
+                                       void *userdata,
+                                       ofono_destroy_func destroy)
+{
+       struct file_watch *watch;
+
+       if (cb == NULL)
+               return 0;
+
+       if (context->file_watches == NULL)
+               context->file_watches = __ofono_watchlist_new(g_free);
+
+       watch = g_new0(struct file_watch, 1);
+
+       watch->ef = id;
+       watch->item.notify = cb;
+       watch->item.notify_data = userdata;
+       watch->item.destroy = destroy;
+
+       return __ofono_watchlist_add_item(context->file_watches,
+                                       (struct ofono_watchlist_item *) watch);
+}
+
+void sim_fs_file_watch_remove(struct ofono_sim_context *context,
+                               unsigned int id)
+{
+       __ofono_watchlist_remove_item(context->file_watches, id);
+}
+
+void sim_fs_notify_file_watches(struct sim_fs *fs, int id)
+{
+       GSList *l;
+
+       for (l = fs->contexts; l; l = l->next) {
+               struct ofono_sim_context *context = l->data;
+               GSList *k;
+
+               for (k = context->file_watches->items; k; k = k->next) {
+                       struct file_watch *w = k->data;
+                       ofono_sim_file_changed_cb_t notify = w->item.notify;
+
+                       if (id == -1 || w->ef == id)
+                               notify(w->ef, w->item.notify_data);
+               }
+       }
+
+}
+
+static void sim_fs_end_current(struct sim_fs *fs)
+{
+       struct sim_fs_op *op = g_queue_pop_head(fs->op_q);
+
+       if (g_queue_get_length(fs->op_q) > 0)
+               fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+       if (fs->fd != -1) {
+               TFR(close(fs->fd));
+               fs->fd = -1;
+       }
+
+       memset(fs->bitmap, 0, sizeof(fs->bitmap));
+
+       sim_fs_op_free(op);
+}
+
+static void sim_fs_op_error(struct sim_fs *fs)
+{
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+
+       if (op->cb == NULL) {
+               sim_fs_end_current(fs);
+               return;
+       }
+
+       if (op->info_only == TRUE)
+               ((sim_fs_read_info_cb_t) op->cb)
+                       (0, 0, 0, 0, op->userdata);
+       else if (op->is_read == TRUE)
+               ((ofono_sim_file_read_cb_t) op->cb)
+                       (0, 0, 0, 0, 0, op->userdata);
+       else
+               ((ofono_sim_file_write_cb_t) op->cb)
+                       (0, op->userdata);
+
+       sim_fs_end_current(fs);
+}
+
+static gboolean cache_block(struct sim_fs *fs, int block, int block_len,
+                               const unsigned char *data, int num_bytes)
+{
+       int offset;
+       int bit;
+       ssize_t r;
+       unsigned char b;
+
+       if (fs->fd == -1)
+               return FALSE;
+
+       if (lseek(fs->fd, block * block_len +
+                               SIM_CACHE_HEADER_SIZE, SEEK_SET) == (off_t) -1)
+               return FALSE;
+
+       r = TFR(write(fs->fd, data, num_bytes));
+
+       if (r != num_bytes)
+               return FALSE;
+
+       /* update present bit for this block */
+       offset = block / 8;
+       bit = block % 8;
+
+       /* lseek to correct byte (skip file info) */
+       lseek(fs->fd, offset + SIM_FILE_INFO_SIZE, SEEK_SET);
+
+       b = fs->bitmap[offset];
+       b |= 1 << bit;
+
+       r = TFR(write(fs->fd, &b, sizeof(b)));
+
+       if (r != sizeof(b))
+               return FALSE;
+
+       fs->bitmap[offset] = b;
+
+       return TRUE;
+}
+
+static void sim_fs_op_write_cb(const struct ofono_error *error, void *data)
+{
+       struct sim_fs *fs = data;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       ofono_sim_file_write_cb_t cb = op->cb;
+
+       if (cb == NULL) {
+               sim_fs_end_current(fs);
+               return;
+       }
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               cb(1, op->userdata);
+       else
+               cb(0, op->userdata);
+
+       sim_fs_end_current(fs);
+}
+
+static void sim_fs_op_read_block_cb(const struct ofono_error *error,
+                                       const unsigned char *data, int len,
+                                       void *user)
+{
+       struct sim_fs *fs = user;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       int start_block;
+       int end_block;
+       int bufoff;
+       int dataoff;
+       int tocopy;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       start_block = op->offset / 256;
+       end_block = (op->offset + (op->num_bytes - 1)) / 256;
+
+       if (op->current == start_block) {
+               bufoff = 0;
+               dataoff = op->offset % 256;
+               tocopy = MIN(256 - op->offset % 256,
+                               op->num_bytes - op->current * 256);
+       } else {
+               bufoff = (op->current - start_block - 1) * 256 +
+                               op->offset % 256;
+               dataoff = 0;
+               tocopy = MIN(256, op->num_bytes - op->current * 256);
+       }
+
+       DBG("bufoff: %d, dataoff: %d, tocopy: %d",
+                               bufoff, dataoff, tocopy);
+
+       memcpy(op->buffer + bufoff, data + dataoff, tocopy);
+       cache_block(fs, op->current, 256, data, len);
+
+       if (op->cb == NULL) {
+               sim_fs_end_current(fs);
+               return;
+       }
+
+       op->current++;
+
+       if (op->current > end_block) {
+               ofono_sim_file_read_cb_t cb = op->cb;
+
+               cb(1, op->num_bytes, 0, op->buffer,
+                               op->record_length, op->userdata);
+
+               sim_fs_end_current(fs);
+       } else {
+               fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
+       }
+}
+
+static gboolean sim_fs_op_read_block(gpointer user_data)
+{
+       struct sim_fs *fs = user_data;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       int start_block;
+       int end_block;
+       unsigned short read_bytes;
+
+       fs->op_source = 0;
+
+       if (op->cb == NULL) {
+               sim_fs_end_current(fs);
+               return FALSE;
+       }
+
+       start_block = op->offset / 256;
+       end_block = (op->offset + (op->num_bytes - 1)) / 256;
+
+       if (op->current == start_block) {
+               op->buffer = g_try_new0(unsigned char, op->num_bytes);
+
+               if (op->buffer == NULL) {
+                       sim_fs_op_error(fs);
+                       return FALSE;
+               }
+       }
+
+       while (fs->fd != -1 && op->current <= end_block) {
+               int offset = op->current / 8;
+               int bit = 1 << op->current % 8;
+               int bufoff;
+               int seekoff;
+               int toread;
+
+               if ((fs->bitmap[offset] & bit) == 0)
+                       break;
+
+               if (op->current == start_block) {
+                       bufoff = 0;
+                       seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256 +
+                               op->offset % 256;
+                       toread = MIN(256 - op->offset % 256,
+                                       op->num_bytes - op->current * 256);
+               } else {
+                       bufoff = (op->current - start_block - 1) * 256 +
+                                       op->offset % 256;
+                       seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256;
+                       toread = MIN(256, op->num_bytes - op->current * 256);
+               }
+
+               DBG("bufoff: %d, seekoff: %d, toread: %d",
+                               bufoff, seekoff, toread);
+
+               if (lseek(fs->fd, seekoff, SEEK_SET) == (off_t) -1)
+                       break;
+
+               if (TFR(read(fs->fd, op->buffer + bufoff, toread)) != toread)
+                       break;
+
+               op->current += 1;
+       }
+
+       if (op->current > end_block) {
+               ofono_sim_file_read_cb_t cb = op->cb;
+
+               cb(1, op->num_bytes, 0, op->buffer,
+                               op->record_length, op->userdata);
+
+               sim_fs_end_current(fs);
+
+               return FALSE;
+       }
+
+       if (fs->driver->read_file_transparent == NULL) {
+               sim_fs_op_error(fs);
+               return FALSE;
+       }
+
+       read_bytes = MIN(op->length - op->current * 256, 256);
+       fs->driver->read_file_transparent(fs->sim, op->id,
+                                               op->current * 256,
+                                               read_bytes,
+                                               sim_fs_op_read_block_cb, fs);
+
+       return FALSE;
+}
+
+static void sim_fs_op_retrieve_cb(const struct ofono_error *error,
+                                       const unsigned char *data, int len,
+                                       void *user)
+{
+       struct sim_fs *fs = user;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       int total = op->length / op->record_length;
+       ofono_sim_file_read_cb_t cb = op->cb;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       cache_block(fs, op->current - 1, op->record_length,
+                       data, op->record_length);
+
+       if (cb == NULL) {
+               sim_fs_end_current(fs);
+               return;
+       }
+
+       cb(1, op->length, op->current, data, op->record_length, op->userdata);
+
+       if (op->current < total) {
+               op->current += 1;
+               fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+       } else {
+               sim_fs_end_current(fs);
+       }
+}
+
+static gboolean sim_fs_op_read_record(gpointer user)
+{
+       struct sim_fs *fs = user;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       const struct ofono_sim_driver *driver = fs->driver;
+       int total = op->length / op->record_length;
+       unsigned char buf[256];
+
+       fs->op_source = 0;
+
+       if (op->cb == NULL) {
+               sim_fs_end_current(fs);
+               return FALSE;
+       }
+
+       while (fs->fd != -1 && op->current <= total) {
+               int offset = (op->current - 1) / 8;
+               int bit = 1 << ((op->current - 1) % 8);
+               ofono_sim_file_read_cb_t cb = op->cb;
+
+               if ((fs->bitmap[offset] & bit) == 0)
+                       break;
+
+               if (lseek(fs->fd, (op->current - 1) * op->record_length +
+                               SIM_CACHE_HEADER_SIZE, SEEK_SET) == (off_t) -1)
+                       break;
+
+               if (TFR(read(fs->fd, buf, op->record_length)) !=
+                               op->record_length)
+                       break;
+
+               cb(1, op->length, op->current,
+                               buf, op->record_length, op->userdata);
+
+               op->current += 1;
+       }
+
+       if (op->current > total) {
+               sim_fs_end_current(fs);
+
+               return FALSE;
+       }
+
+       switch (op->structure) {
+       case OFONO_SIM_FILE_STRUCTURE_FIXED:
+               if (driver->read_file_linear == NULL) {
+                       sim_fs_op_error(fs);
+                       return FALSE;
+               }
+
+               driver->read_file_linear(fs->sim, op->id, op->current,
+                                               op->record_length,
+                                               sim_fs_op_retrieve_cb, fs);
+               break;
+       case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
+               if (driver->read_file_cyclic == NULL) {
+                       sim_fs_op_error(fs);
+                       return FALSE;
+               }
+
+               driver->read_file_cyclic(fs->sim, op->id, op->current,
+                                               op->record_length,
+                                               sim_fs_op_retrieve_cb, fs);
+               break;
+       default:
+               ofono_error("Unrecognized file structure, this can't happen");
+       }
+
+       return FALSE;
+}
+
+static void sim_fs_op_cache_fileinfo(struct sim_fs *fs,
+                                       const struct ofono_error *error,
+                                       int length,
+                                       enum ofono_sim_file_structure structure,
+                                       int record_length,
+                                       const unsigned char access[3],
+                                       unsigned char file_status)
+{
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       enum sim_file_access update;
+       enum sim_file_access invalidate;
+       enum sim_file_access rehabilitate;
+       unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
+       gboolean cache;
+       char *path;
+
+       /* TS 11.11, Section 9.3 */
+       update = file_access_condition_decode(access[0] & 0xf);
+       rehabilitate = file_access_condition_decode((access[2] >> 4) & 0xf);
+       invalidate = file_access_condition_decode(access[2] & 0xf);
+
+       /* Never cache card holder writable files */
+       cache = (update == SIM_FILE_ACCESS_ADM ||
+                       update == SIM_FILE_ACCESS_NEVER) &&
+                       (invalidate == SIM_FILE_ACCESS_ADM ||
+                               invalidate == SIM_FILE_ACCESS_NEVER) &&
+                       (rehabilitate == SIM_FILE_ACCESS_ADM ||
+                               rehabilitate == SIM_FILE_ACCESS_NEVER);
+
+       if (imsi == NULL || phase == OFONO_SIM_PHASE_UNKNOWN || cache == FALSE)
+               return;
+
+       memset(fileinfo, 0, SIM_CACHE_HEADER_SIZE);
+
+       fileinfo[0] = error->type;
+       fileinfo[1] = length >> 8;
+       fileinfo[2] = length & 0xff;
+       fileinfo[3] = structure;
+       fileinfo[4] = record_length >> 8;
+       fileinfo[5] = record_length & 0xff;
+       fileinfo[6] = file_status;
+
+       path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, op->id);
+       fs->fd = TFR(open(path, O_WRONLY | O_CREAT | O_TRUNC, SIM_CACHE_MODE));
+       g_free(path);
+
+       if (fs->fd == -1)
+               return;
+
+       if (TFR(write(fs->fd, fileinfo, SIM_CACHE_HEADER_SIZE)) ==
+                       SIM_CACHE_HEADER_SIZE)
+               return;
+
+       TFR(close(fs->fd));
+       fs->fd = -1;
+}
+
+static void sim_fs_op_info_cb(const struct ofono_error *error, int length,
+                               enum ofono_sim_file_structure structure,
+                               int record_length,
+                               const unsigned char access[3],
+                               unsigned char file_status,
+                               void *data)
+{
+       struct sim_fs *fs = data;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       sim_fs_op_cache_fileinfo(fs, error, length, structure, record_length,
+                                       access, file_status);
+
+       if (structure != op->structure) {
+               ofono_error("Requested file structure differs from SIM: %x",
+                               op->id);
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       if (op->cb == NULL) {
+               sim_fs_end_current(fs);
+               return;
+       }
+
+       op->structure = structure;
+       op->length = length;
+
+       if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) {
+               if (op->num_bytes == 0)
+                       op->num_bytes = op->length;
+
+               op->record_length = length;
+               op->current = op->offset / 256;
+
+               if (op->info_only == FALSE)
+                       fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
+       } else {
+               op->record_length = record_length;
+               op->current = 1;
+
+               if (op->info_only == FALSE)
+                       fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+       }
+
+       if (op->info_only == TRUE) {
+               /*
+                * It's an info-only request, so there is no need to request
+                * actual contents of the EF. Just return the EF-info.
+                */
+               sim_fs_read_info_cb_t cb = op->cb;
+
+               cb(1, file_status, op->length,
+                       op->record_length, op->userdata);
+
+               sim_fs_end_current(fs);
+       }
+}
+
+static gboolean sim_fs_op_check_cached(struct sim_fs *fs)
+{
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       char *path;
+       int fd;
+       ssize_t len;
+       unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
+       int error_type;
+       int file_length;
+       enum ofono_sim_file_structure structure;
+       int record_length;
+       unsigned char file_status;
+
+       if (imsi == NULL || phase == OFONO_SIM_PHASE_UNKNOWN)
+               return FALSE;
+
+       path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, op->id);
+
+       if (path == NULL)
+               return FALSE;
+
+       fd = TFR(open(path, O_RDWR));
+       g_free(path);
+
+       if (fd == -1) {
+               if (errno != ENOENT)
+                       DBG("Error %i opening cache file for "
+                                       "fileid %04x, IMSI %s",
+                                       errno, op->id, imsi);
+
+               return FALSE;
+       }
+
+       len = TFR(read(fd, fileinfo, SIM_CACHE_HEADER_SIZE));
+
+       if (len != SIM_CACHE_HEADER_SIZE)
+               goto error;
+
+       error_type = fileinfo[0];
+       file_length = (fileinfo[1] << 8) | fileinfo[2];
+       structure = fileinfo[3];
+       record_length = (fileinfo[4] << 8) | fileinfo[5];
+       file_status = fileinfo[6];
+
+       if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
+               record_length = file_length;
+
+       if (record_length == 0 || file_length < record_length)
+               goto error;
+
+       op->length = file_length;
+       op->record_length = record_length;
+       memcpy(fs->bitmap, fileinfo + SIM_FILE_INFO_SIZE,
+                       SIM_CACHE_HEADER_SIZE - SIM_FILE_INFO_SIZE);
+       fs->fd = fd;
+
+       if (error_type != OFONO_ERROR_TYPE_NO_ERROR ||
+                       structure != op->structure) {
+               sim_fs_op_error(fs);
+               return TRUE;
+       }
+
+       if (op->info_only == TRUE) {
+               /*
+                * It's an info-only request, so there is no need to request
+                * actual contents of the EF. Just return the EF-info.
+                */
+               sim_fs_read_info_cb_t cb = op->cb;
+
+               cb(1, file_status, op->length,
+                       op->record_length, op->userdata);
+
+               sim_fs_end_current(fs);
+       } else if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) {
+               if (op->num_bytes == 0)
+                       op->num_bytes = op->length;
+
+               op->current = op->offset / 256;
+               fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
+       } else {
+               op->current = 1;
+               fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+       }
+
+       return TRUE;
+
+error:
+       TFR(close(fd));
+       return FALSE;
+}
+
+static gboolean sim_fs_op_next(gpointer user_data)
+{
+       struct sim_fs *fs = user_data;
+       const struct ofono_sim_driver *driver = fs->driver;
+       struct sim_fs_op *op;
+
+       fs->op_source = 0;
+
+       if (fs->op_q == NULL)
+               return FALSE;
+
+       op = g_queue_peek_head(fs->op_q);
+
+       if (op->cb == NULL) {
+               sim_fs_end_current(fs);
+               return FALSE;
+       }
+
+       if (op->is_read == TRUE) {
+               if (sim_fs_op_check_cached(fs))
+                       return FALSE;
+
+               driver->read_file_info(fs->sim, op->id, sim_fs_op_info_cb, fs);
+       } else {
+               switch (op->structure) {
+               case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
+                       driver->write_file_transparent(fs->sim, op->id, 0,
+                                       op->length, op->buffer,
+                                       sim_fs_op_write_cb, fs);
+                       break;
+               case OFONO_SIM_FILE_STRUCTURE_FIXED:
+                       driver->write_file_linear(fs->sim, op->id, op->current,
+                                       op->length, op->buffer,
+                                       sim_fs_op_write_cb, fs);
+                       break;
+               case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
+                       driver->write_file_cyclic(fs->sim, op->id,
+                                       op->length, op->buffer,
+                                       sim_fs_op_write_cb, fs);
+                       break;
+               default:
+                       ofono_error("Unrecognized file structure, "
+                                       "this can't happen");
+               }
+
+               g_free(op->buffer);
+               op->buffer = NULL;
+       }
+
+       return FALSE;
+}
+
+int sim_fs_read_info(struct ofono_sim_context *context, int id,
+                       enum ofono_sim_file_structure expected_type,
+                       sim_fs_read_info_cb_t cb, void *data)
+{
+       struct sim_fs *fs = context->fs;
+       struct sim_fs_op *op;
+
+       if (cb == NULL)
+               return -EINVAL;
+
+       if (fs->driver == NULL)
+               return -EINVAL;
+
+       if (fs->driver->read_file_info == NULL)
+               return -ENOSYS;
+
+       if (fs->op_q == NULL)
+               fs->op_q = g_queue_new();
+
+       op = g_try_new0(struct sim_fs_op, 1);
+       if (op == NULL)
+               return -ENOMEM;
+
+       op->id = id;
+       op->structure = expected_type;
+       op->cb = cb;
+       op->userdata = data;
+       op->is_read = TRUE;
+       op->info_only = TRUE;
+       op->context = context;
+
+       g_queue_push_tail(fs->op_q, op);
+
+       if (g_queue_get_length(fs->op_q) == 1)
+               fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+       return 0;
+}
+
+int sim_fs_read(struct ofono_sim_context *context, int id,
+               enum ofono_sim_file_structure expected_type,
+               unsigned short offset, unsigned short num_bytes,
+               ofono_sim_file_read_cb_t cb, void *data)
+{
+       struct sim_fs *fs = context->fs;
+       struct sim_fs_op *op;
+
+       if (cb == NULL)
+               return -EINVAL;
+
+       if (fs->driver == NULL)
+               return -EINVAL;
+
+       if (fs->driver->read_file_info == NULL) {
+               cb(0, 0, 0, NULL, 0, data);
+               return -ENOSYS;
+       }
+
+       if (fs->op_q == NULL)
+               fs->op_q = g_queue_new();
+
+       op = g_try_new0(struct sim_fs_op, 1);
+       if (op == NULL)
+               return -ENOMEM;
+
+       op->id = id;
+       op->structure = expected_type;
+       op->cb = cb;
+       op->userdata = data;
+       op->is_read = TRUE;
+       op->offset = offset;
+       op->num_bytes = num_bytes;
+       op->info_only = FALSE;
+       op->context = context;
+
+       g_queue_push_tail(fs->op_q, op);
+
+       if (g_queue_get_length(fs->op_q) == 1)
+               fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+       return 0;
+}
+
+int sim_fs_write(struct ofono_sim_context *context, int id,
+                       ofono_sim_file_write_cb_t cb,
+                       enum ofono_sim_file_structure structure, int record,
+                       const unsigned char *data, int length, void *userdata)
+{
+       struct sim_fs *fs = context->fs;
+       struct sim_fs_op *op;
+       gconstpointer fn = NULL;
+
+       if (cb == NULL)
+               return -EINVAL;
+
+       if (fs->driver == NULL)
+               return -EINVAL;
+
+       switch (structure) {
+       case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
+               fn = fs->driver->write_file_transparent;
+               break;
+       case OFONO_SIM_FILE_STRUCTURE_FIXED:
+               fn = fs->driver->write_file_linear;
+               break;
+       case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
+               fn = fs->driver->write_file_cyclic;
+               break;
+       default:
+               ofono_error("Unrecognized file structure, this can't happen");
+       }
+
+       if (fn == NULL)
+               return -ENOSYS;
+
+       if (fs->op_q == NULL)
+               fs->op_q = g_queue_new();
+
+       op = g_try_new0(struct sim_fs_op, 1);
+       if (op == NULL)
+               return -ENOMEM;
+
+       op->id = id;
+       op->cb = cb;
+       op->userdata = userdata;
+       op->is_read = FALSE;
+       op->buffer = g_memdup(data, length);
+       op->structure = structure;
+       op->length = length;
+       op->current = record;
+       op->context = context;
+
+       g_queue_push_tail(fs->op_q, op);
+
+       if (g_queue_get_length(fs->op_q) == 1)
+               fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+       return 0;
+}
+
+void sim_fs_cache_image(struct sim_fs *fs, const char *image, int id)
+{
+       const char *imsi;
+       enum ofono_sim_phase phase;
+
+       if (fs == NULL || image == NULL)
+               return;
+
+       imsi = ofono_sim_get_imsi(fs->sim);
+       if (imsi == NULL)
+               return;
+
+       phase = ofono_sim_get_phase(fs->sim);
+       if (phase == OFONO_SIM_PHASE_UNKNOWN)
+               return;
+
+       write_file((const unsigned char *) image, strlen(image),
+                       SIM_CACHE_MODE, SIM_IMAGE_CACHE_PATH, imsi,
+                       phase, id);
+}
+
+char *sim_fs_get_cached_image(struct sim_fs *fs, int id)
+{
+       const char *imsi;
+       enum ofono_sim_phase phase;
+       unsigned short image_length;
+       int fd;
+       char *buffer;
+       char *path;
+       int len;
+       struct stat st_buf;
+
+       if (fs == NULL)
+               return NULL;
+
+       imsi = ofono_sim_get_imsi(fs->sim);
+       if (imsi == NULL)
+               return NULL;
+
+       phase = ofono_sim_get_phase(fs->sim);
+       if (phase == OFONO_SIM_PHASE_UNKNOWN)
+               return NULL;
+
+       path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id);
+
+       TFR(stat(path, &st_buf));
+       fd = TFR(open(path, O_RDONLY));
+       g_free(path);
+
+       if (fd < 0)
+               return NULL;
+
+       image_length = st_buf.st_size;
+       buffer = g_try_new0(char, image_length + 1);
+
+       if (buffer == NULL) {
+               TFR(close(fd));
+               return NULL;
+       }
+
+       len = TFR(read(fd, buffer, image_length));
+       TFR(close(fd));
+
+       if (len != image_length) {
+               g_free(buffer);
+               return NULL;
+       }
+
+       return buffer;
+}
+
+static void remove_cachefile(const char *imsi, enum ofono_sim_phase phase,
+                               const struct dirent *file)
+{
+       int id;
+       char *path;
+
+       if (file->d_type != DT_REG)
+               return;
+
+       if (sscanf(file->d_name, "%4x", &id) != 1)
+               return;
+
+       path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, id);
+       remove(path);
+       g_free(path);
+}
+
+static void remove_imagefile(const char *imsi, enum ofono_sim_phase phase,
+                               const struct dirent *file)
+{
+       int id;
+       char *path;
+
+       if (file->d_type != DT_REG)
+               return;
+
+       if (sscanf(file->d_name, "%d", &id) != 1)
+               return;
+
+       path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id);
+       remove(path);
+       g_free(path);
+}
+
+void sim_fs_check_version(struct sim_fs *fs)
+{
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       unsigned char version;
+
+       if (imsi == NULL || phase == OFONO_SIM_PHASE_UNKNOWN)
+               return;
+
+       if (read_file(&version, 1, SIM_CACHE_VERSION, imsi, phase) == 1)
+               if (version == SIM_FS_VERSION)
+                       return;
+
+       sim_fs_cache_flush(fs);
+
+       version = SIM_FS_VERSION;
+       write_file(&version, 1, SIM_CACHE_MODE, SIM_CACHE_VERSION, imsi, phase);
+}
+
+void sim_fs_cache_flush(struct sim_fs *fs)
+{
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       char *path = g_strdup_printf(SIM_CACHE_BASEPATH, imsi, phase);
+       struct dirent **entries;
+       int len = scandir(path, &entries, NULL, alphasort);
+
+       g_free(path);
+
+       if (len > 0) {
+               /* Remove all file ids */
+               while (len--) {
+                       remove_cachefile(imsi, phase, entries[len]);
+                       g_free(entries[len]);
+               }
+
+               g_free(entries);
+       }
+
+       sim_fs_image_cache_flush(fs);
+}
+
+void sim_fs_cache_flush_file(struct sim_fs *fs, int id)
+{
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       char *path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, id);
+
+       remove(path);
+       g_free(path);
+}
+
+void sim_fs_image_cache_flush(struct sim_fs *fs)
+{
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       char *path = g_strdup_printf(SIM_IMAGE_CACHE_BASEPATH, imsi, phase);
+       struct dirent **entries;
+       int len = scandir(path, &entries, NULL, alphasort);
+
+       g_free(path);
+
+       if (len <= 0)
+               return;
+
+       /* Remove everything */
+       while (len--) {
+               remove_imagefile(imsi, phase, entries[len]);
+               g_free(entries[len]);
+       }
+
+       g_free(entries);
+}
+
+void sim_fs_image_cache_flush_file(struct sim_fs *fs, int id)
+{
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       char *path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id);
+
+       remove(path);
+       g_free(path);
+}
diff --git a/src/simfs.h b/src/simfs.h
new file mode 100644 (file)
index 0000000..92e8fdb
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct sim_fs;
+
+typedef void (*sim_fs_read_info_cb_t)(int ok, unsigned char file_status,
+                                       int total_length, int record_length,
+                                       void *userdata);
+
+struct sim_fs *sim_fs_new(struct ofono_sim *sim,
+                               const struct ofono_sim_driver *driver);
+struct ofono_sim_context *sim_fs_context_new(struct sim_fs *fs);
+
+unsigned int sim_fs_file_watch_add(struct ofono_sim_context *context,
+                                       int id, ofono_sim_file_changed_cb_t cb,
+                                       void *userdata,
+                                       ofono_destroy_func destroy);
+void sim_fs_file_watch_remove(struct ofono_sim_context *context,
+                                       unsigned int id);
+
+/* Id of -1 notifies all watches, serving as a wildcard */
+void sim_fs_notify_file_watches(struct sim_fs *fs, int id);
+
+int sim_fs_read(struct ofono_sim_context *context, int id,
+               enum ofono_sim_file_structure expected_type,
+               unsigned short offset, unsigned short num_bytes,
+               ofono_sim_file_read_cb_t cb, void *data);
+
+int sim_fs_read_info(struct ofono_sim_context *context, int id,
+               enum ofono_sim_file_structure expected_type,
+               sim_fs_read_info_cb_t cb, void *data);
+
+void sim_fs_check_version(struct sim_fs *fs);
+
+int sim_fs_write(struct ofono_sim_context *context, int id,
+                       ofono_sim_file_write_cb_t cb,
+                       enum ofono_sim_file_structure structure, int record,
+                       const unsigned char *data, int length, void *userdata);
+
+char *sim_fs_get_cached_image(struct sim_fs *fs, int id);
+
+void sim_fs_cache_image(struct sim_fs *fs, const char *image, int id);
+
+void sim_fs_cache_flush(struct sim_fs *fs);
+void sim_fs_cache_flush_file(struct sim_fs *fs, int id);
+void sim_fs_image_cache_flush(struct sim_fs *fs);
+void sim_fs_image_cache_flush_file(struct sim_fs *fs, int id);
+
+void sim_fs_free(struct sim_fs *fs);
+void sim_fs_context_free(struct ofono_sim_context *context);
diff --git a/src/simutil.c b/src/simutil.c
new file mode 100644 (file)
index 0000000..1b6f3a8
--- /dev/null
@@ -0,0 +1,1545 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include <ofono/types.h>
+#include "simutil.h"
+#include "util.h"
+#include "smsutil.h"
+
+struct sim_eons {
+       struct sim_eons_operator_info *pnn_list;
+       GSList *opl_list;
+       gboolean pnn_valid;
+       int pnn_max;
+};
+
+struct spdi_operator {
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+};
+
+struct opl_operator {
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       guint16 lac_tac_low;
+       guint16 lac_tac_high;
+       guint8 id;
+};
+
+#define BINARY 0
+#define RECORD 1
+#define CYCLIC 3
+
+#define ALW    0
+#define PIN    1
+#define PIN2   2
+#define ADM    4
+#define NEV    15
+
+#define ROOTMF 0x3F00
+
+static struct sim_ef_info ef_db[] = {
+{      0x2F05, ROOTMF, BINARY, 0,      ALW,    PIN     },
+{      0x2F06, ROOTMF, RECORD, 0,      ALW,    PIN     },
+{      0x2FE2, ROOTMF, BINARY, 10,     ALW,    NEV     },
+{      0x4F20, 0x5F50, BINARY, 0,      PIN,    ADM     },
+{      0x6F05, 0x7F20, BINARY, 0,      ALW,    PIN     },
+{      0x6F06, 0x0000, RECORD, 0,      ALW,    ADM     },
+{      0x6F14, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F15, 0x7F20, BINARY, 0,      PIN,    PIN     },
+{      0x6F18, 0x7F20, BINARY, 10,     PIN,    ADM     },
+{      0x6F2C, 0x7F20, BINARY, 16,     PIN,    PIN     },
+{      0x6F30, 0x7F20, BINARY, 0,      PIN,    PIN     },
+{      0x6F32, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F37, 0x7F20, BINARY, 3,      PIN,    PIN2    },
+{      0x6F38, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F39, 0x7F20, CYCLIC, 3,      PIN,    PIN2    },
+{      0x6F3B, 0x7F10, RECORD, 0,      PIN,    PIN2    },
+{      0x6F3E, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F3F, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F40, 0x7F10, RECORD, 0,      PIN,    PIN     },
+{      0x6F41, 0x7F20, BINARY, 5,      PIN,    PIN2    },
+{      0x6F42, 0x7F10, RECORD, 0,      PIN,    PIN     },
+{      0x6F44, 0x7F10, CYCLIC, 0,      PIN,    PIN     },
+{      0x6F45, 0x7F20, BINARY, 0,      PIN,    PIN     },
+{      0x6F46, 0x7F20, BINARY, 17,     ALW,    ADM     },
+{      0x6F48, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F49, 0x7F10, RECORD, 0,      PIN,    ADM     },
+{      0x6F4D, 0x7F20, RECORD, 0,      PIN,    PIN2    },
+{      0x6F50, 0x7F20, BINARY, 0,      PIN,    PIN     },
+{      0x6F51, 0x7F20, RECORD, 0,      PIN,    ADM     },
+{      0x6F53, 0x7F20, BINARY, 14,     PIN,    PIN     },
+{      0x6F56, 0x0000, BINARY, 0,      PIN,    PIN2    },
+{      0x6F60, 0x7F20, BINARY, 0,      PIN,    PIN     },
+{      0x6F61, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F62, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6F73, 0x0000, BINARY, 14,     PIN,    PIN     },
+{      0x6F7B, 0x7F20, BINARY, 0,      PIN,    PIN     },
+{      0x6F7E, 0x7F20, BINARY, 11,     PIN,    PIN     },
+{      0x6FAD, 0x7F20, BINARY, 0,      ALW,    ADM     },
+{      0x6FAE, 0x7F20, BINARY, 1,      ALW,    ADM     },
+{      0x6FB7, 0x7F20, BINARY, 0,      ALW,    ADM     },
+{      0x6FC5, 0x7F20, RECORD, 0,      ALW,    ADM     },
+{      0x6FC6, 0x7F20, RECORD, 0,      ALW,    ADM     },
+{      0x6FC7, 0x7F20, RECORD, 0,      PIN,    PIN     },
+{      0x6FC9, 0x7F20, RECORD, 0,      PIN,    PIN     },
+{      0x6FCA, 0x7F20, RECORD, 0,      PIN,    PIN     },
+{      0x6FCB, 0x7F20, RECORD, 16,     PIN,    PIN     },
+{      0x6FCD, 0x7F20, BINARY, 0,      PIN,    ADM     },
+{      0x6FD9, 0x0000, BINARY, 0,      PIN,    ADM     },
+{      0x6FDB, 0x0000, BINARY, 1,      PIN,    ADM     },
+{      0x6FDC, 0x0000, BINARY, 1,      PIN,    ADM     },
+{      0x6FDE, 0x0000, BINARY, 0,      ALW,    ADM     },
+{      0x6FDF, 0x0000, RECORD, 0,      ALW,    ADM     },
+{      0x6FE3, 0x0000, BINARY, 18,     PIN,    PIN     },
+};
+
+void simple_tlv_iter_init(struct simple_tlv_iter *iter,
+                               const unsigned char *pdu, unsigned int len)
+{
+       iter->pdu = pdu;
+       iter->max = len;
+       iter->pos = 0;
+       iter->tag = 0;
+       iter->len = 0;
+       iter->data = NULL;
+}
+
+gboolean simple_tlv_iter_next(struct simple_tlv_iter *iter)
+{
+       const unsigned char *pdu = iter->pdu + iter->pos;
+       const unsigned char *end = iter->pdu + iter->max;
+       unsigned char tag;
+       unsigned short len;
+
+       if (pdu == end)
+               return FALSE;
+
+       tag = *pdu;
+       pdu++;
+
+       /*
+        * ISO 7816-4, Section 5.2.1:
+        *
+        * The tag field consists of a single byte encoding a tag number from
+        * 1 to 254.  The values 00 and FF are invalid for tag fields.
+        *
+        * The length field consists of one or three consecutive bytes.
+        *      - If the first byte is not set to FF, then the length field
+        *        consists of a single byte encoding a number from zero to
+        *        254 and denoted N.
+        *      - If the first byte is set to FF, then the length field
+        *        continues on the subsequent two bytes with any value
+        *        encoding a number from zero to 65535 and denoted N
+        *
+        * If N is zero, there is no value field, i.e. data object is empty.
+        */
+       if (pdu == end)
+               return FALSE;
+
+       len = *pdu++;
+
+       if (len == 0xFF) {
+               if ((pdu + 2) > end)
+                       return FALSE;
+
+               len = (pdu[0] << 8) | pdu[1];
+
+               pdu += 2;
+       }
+
+       if (pdu + len > end)
+               return FALSE;
+
+       iter->tag = tag;
+       iter->len = len;
+       iter->data = pdu;
+
+       iter->pos = pdu + len - iter->pdu;
+
+       return TRUE;
+}
+
+unsigned char simple_tlv_iter_get_tag(struct simple_tlv_iter *iter)
+{
+       return iter->tag;
+}
+
+unsigned short simple_tlv_iter_get_length(struct simple_tlv_iter *iter)
+{
+       return iter->len;
+}
+
+const unsigned char *simple_tlv_iter_get_data(struct simple_tlv_iter *iter)
+{
+       return iter->data;
+}
+
+void comprehension_tlv_iter_init(struct comprehension_tlv_iter *iter,
+                                       const unsigned char *pdu,
+                                       unsigned int len)
+{
+       iter->pdu = pdu;
+       iter->max = len;
+       iter->pos = 0;
+       iter->tag = 0;
+       iter->cr = FALSE;
+       iter->data = 0;
+}
+
+/* Comprehension TLVs defined in Section 7 of ETSI TS 101.220 */
+gboolean comprehension_tlv_iter_next(struct comprehension_tlv_iter *iter)
+{
+       const unsigned char *pdu = iter->pdu + iter->pos;
+       const unsigned char *end = iter->pdu + iter->max;
+       unsigned short tag;
+       unsigned short len;
+       gboolean cr;
+
+       if (pdu == end)
+               return FALSE;
+
+       if (*pdu == 0x00 || *pdu == 0xFF || *pdu == 0x80)
+               return FALSE;
+
+       cr = bit_field(*pdu, 7, 1);
+       tag = bit_field(*pdu, 0, 7);
+       pdu++;
+
+       /*
+        * ETSI TS 101.220, Section 7.1.1.2
+        *
+        * If byte 1 of the tag is equal to 0x7F, then the tag is encoded
+        * on the following two bytes, with bit 8 of the 2nd byte of the tag
+        * being the CR flag.
+        */
+       if (tag == 0x7F) {
+               if ((pdu + 2) > end)
+                       return FALSE;
+
+               cr = bit_field(pdu[0], 7, 1);
+               tag = ((pdu[0] & 0x7f) << 8) | pdu[1];
+
+               if (tag < 0x0001 || tag > 0x7fff)
+                       return FALSE;
+
+               pdu += 2;
+       }
+
+       if (pdu == end)
+               return FALSE;
+
+       len = *pdu++;
+
+       if (len >= 0x80) {
+               unsigned int extended_bytes = len - 0x80;
+               unsigned int i;
+
+               if (extended_bytes == 0 || extended_bytes > 3)
+                       return FALSE;
+
+               if ((pdu + extended_bytes) > end)
+                       return FALSE;
+
+               if (pdu[0] == 0)
+                       return FALSE;
+
+               for (len = 0, i = 0; i < extended_bytes; i++)
+                       len = (len << 8) | *pdu++;
+       }
+
+       if (pdu + len > end)
+               return FALSE;
+
+       iter->tag = tag;
+       iter->cr = cr;
+       iter->len = len;
+       iter->data = pdu;
+
+       iter->pos = pdu + len - iter->pdu;
+
+       return TRUE;
+}
+
+unsigned short comprehension_tlv_iter_get_tag(
+                                       struct comprehension_tlv_iter *iter)
+{
+       return iter->tag;
+}
+
+gboolean comprehension_tlv_get_cr(struct comprehension_tlv_iter *iter)
+{
+       return iter->cr;
+}
+
+unsigned int comprehension_tlv_iter_get_length(
+                                       struct comprehension_tlv_iter *iter)
+{
+       return iter->len;
+}
+
+const unsigned char *comprehension_tlv_iter_get_data(
+                                       struct comprehension_tlv_iter *iter)
+{
+       return iter->data;
+}
+
+void comprehension_tlv_iter_copy(struct comprehension_tlv_iter *from,
+                                       struct comprehension_tlv_iter *to)
+{
+       to->max = from->max;
+       to->pos = from->pos;
+       to->pdu = from->pdu;
+       to->tag = from->tag;
+       to->cr = from->cr;
+       to->len = from->len;
+       to->data = from->data;
+}
+
+void ber_tlv_iter_init(struct ber_tlv_iter *iter, const unsigned char *pdu,
+                       unsigned int len)
+{
+       iter->pdu = pdu;
+       iter->max = len;
+       iter->pos = 0;
+}
+
+unsigned int ber_tlv_iter_get_tag(struct ber_tlv_iter *iter)
+{
+       return iter->tag;
+}
+
+enum ber_tlv_data_type ber_tlv_iter_get_class(struct ber_tlv_iter *iter)
+{
+       return iter->class;
+}
+
+enum ber_tlv_data_encoding_type
+       ber_tlv_iter_get_encoding(struct ber_tlv_iter *iter)
+{
+       return iter->encoding;
+}
+
+unsigned char ber_tlv_iter_get_short_tag(struct ber_tlv_iter *iter)
+{
+       if (iter->tag > 30)
+               return 0;
+
+       return iter->tag | (iter->encoding << 5) | (iter->class << 6);
+}
+
+unsigned int ber_tlv_iter_get_length(struct ber_tlv_iter *iter)
+{
+       return iter->len;
+}
+
+const unsigned char *ber_tlv_iter_get_data(struct ber_tlv_iter *iter)
+{
+       return iter->data;
+}
+
+/* BER TLV structure is defined in ISO/IEC 7816-4 */
+gboolean ber_tlv_iter_next(struct ber_tlv_iter *iter)
+{
+       const unsigned char *pdu = iter->pdu + iter->pos;
+       const unsigned char *end = iter->pdu + iter->max;
+       unsigned int tag;
+       unsigned int len;
+       enum ber_tlv_data_type class;
+       enum ber_tlv_data_encoding_type encoding;
+
+       while ((pdu < end) && (*pdu == 0x00 || *pdu == 0xff))
+               pdu++;
+
+       if (pdu == end)
+               return FALSE;
+
+       class = bit_field(*pdu, 6, 2);
+       encoding = bit_field(*pdu, 5, 1);
+       tag = bit_field(*pdu, 0, 5);
+
+       pdu++;
+
+       /*
+        * ISO 7816-4, Section 5.2.2.1:
+        * "If bits 5 to 1 of the first byte of the tag are not
+        * all set to 1, then they encode a tag number from zero
+        * to thirty and the tag field consists of a single byte.
+        *
+        * Otherwise, the tag field continues on one or more
+        * subsequent bytes
+        *      - Bit 8 of each subsequent byte shall be set to 1,
+        *        unless it is the last subsequent byte
+        *      - Bits 7 to 1 of the first subsequent byte shall not be
+        *        all set to 0
+        *      - Bits 7 to 1 of the first subsequent byte, followed by
+        *        bits 7 to 1 of each further subsequent byte, up to
+        *        and including bits 7 to 1 of the last subsequent
+        *        byte encode a tag number.
+        */
+       if (tag == 0x1f) {
+               if (pdu == end)
+                       return FALSE;
+
+               /* First byte of the extended tag cannot contain 0 */
+               if ((*pdu & 0x7f) == 0)
+                       return FALSE;
+
+               tag = 0;
+
+               while ((pdu < end) && (*pdu & 0x80)) {
+                       tag = (tag << 7) | (*pdu & 0x7f);
+                       pdu++;
+               }
+
+               if (pdu == end)
+                       return FALSE;
+
+               tag = (tag << 7) | *pdu;
+               pdu++;
+       }
+
+       if (pdu == end)
+               return FALSE;
+
+       len = *pdu++;
+
+       if (len >= 0x80) {
+               unsigned int extended_bytes = len - 0x80;
+               unsigned int i;
+
+               if (extended_bytes == 0 || extended_bytes > 4)
+                       return FALSE;
+
+               if ((pdu + extended_bytes) > end)
+                       return FALSE;
+
+               if (pdu[0] == 0)
+                       return FALSE;
+
+               for (len = 0, i = 0; i < extended_bytes; i++)
+                       len = (len << 8) | *pdu++;
+       }
+
+       if (pdu + len > end)
+               return FALSE;
+
+       iter->tag = tag;
+       iter->class = class;
+       iter->encoding = encoding;
+       iter->len = len;
+       iter->data = pdu;
+
+       iter->pos = pdu + len - iter->pdu;
+
+       return TRUE;
+}
+
+void ber_tlv_iter_recurse(struct ber_tlv_iter *iter,
+                               struct ber_tlv_iter *recurse)
+{
+       recurse->pdu = iter->data;
+       recurse->max = iter->len;
+       recurse->pos = 0;
+}
+
+void ber_tlv_iter_recurse_simple(struct ber_tlv_iter *iter,
+                                       struct simple_tlv_iter *container)
+{
+       simple_tlv_iter_init(container, iter->data, iter->len);
+}
+
+void ber_tlv_iter_recurse_comprehension(struct ber_tlv_iter *iter,
+                                       struct comprehension_tlv_iter *recurse)
+{
+       comprehension_tlv_iter_init(recurse, iter->data, iter->len);
+}
+
+static const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag,
+                                               int in_len, int *out_len)
+{
+       struct ber_tlv_iter iter;
+
+       ber_tlv_iter_init(&iter, pdu, in_len);
+
+       while (ber_tlv_iter_next(&iter)) {
+               if (ber_tlv_iter_get_short_tag(&iter) != in_tag)
+                       continue;
+
+               if (out_len)
+                       *out_len = ber_tlv_iter_get_length(&iter);
+
+               return ber_tlv_iter_get_data(&iter);
+       }
+
+       return NULL;
+}
+
+#define MAX_BER_TLV_HEADER 8
+
+gboolean ber_tlv_builder_init(struct ber_tlv_builder *builder,
+                               unsigned char *pdu, unsigned int size)
+{
+       if (size < MAX_BER_TLV_HEADER)
+               return FALSE;
+
+       builder->pdu = pdu;
+       builder->pos = 0;
+       builder->max = size;
+       builder->parent = NULL;
+       builder->tag = 0xff;
+       builder->len = 0;
+
+       return TRUE;
+}
+
+#define BTLV_LEN_FIELD_SIZE_NEEDED(a)                          \
+       ((a) <= 0x7f ? 1 :                                      \
+               ((a) <= 0xff ? 2 :                              \
+                       ((a) <= 0xffff ? 3 :                    \
+                               ((a) <= 0xffffff ? 4 : 5))))
+
+#define BTLV_TAG_FIELD_SIZE_NEEDED(a)                          \
+       ((a) <= 0x1e ? 1 :                                      \
+               ((a) <= 0x7f ? 2 : 3))
+
+static void ber_tlv_builder_write_header(struct ber_tlv_builder *builder)
+{
+       int tag_size = BTLV_TAG_FIELD_SIZE_NEEDED(builder->tag);
+       int len_size = BTLV_LEN_FIELD_SIZE_NEEDED(builder->len);
+       int offset = MAX_BER_TLV_HEADER - tag_size - len_size;
+       unsigned char *pdu = builder->pdu + builder->pos;
+
+       /* Pad with stuff bytes */
+       memset(pdu, 0xff, offset);
+
+       /* Write the tag */
+       pdu[offset++] = (builder->class << 6) |
+                               (builder->encoding << 5) |
+                                       (tag_size == 1 ? builder->tag : 0x1f);
+
+       if (tag_size == 3)
+               pdu[offset++] = 0x80 | (builder->tag >> 7);
+
+       if (tag_size > 2)
+               pdu[offset++] = builder->tag & 0x7f;
+
+       /* Write the length */
+       if (len_size > 1) {
+               int i;
+
+               pdu[offset++] = 0x80 + len_size - 1;
+
+               for (i = len_size - 2; i >= 0; i--)
+                       pdu[offset++] = (builder->len >> (i * 8)) & 0xff;
+       } else
+               pdu[offset++] = builder->len;
+}
+
+gboolean ber_tlv_builder_next(struct ber_tlv_builder *builder,
+                               enum ber_tlv_data_type class,
+                               enum ber_tlv_data_encoding_type encoding,
+                               unsigned int new_tag)
+{
+       if (builder->tag != 0xff) {
+               ber_tlv_builder_write_header(builder);
+               builder->pos += MAX_BER_TLV_HEADER + builder->len;
+       }
+
+       if (ber_tlv_builder_set_length(builder, 0) == FALSE)
+               return FALSE;
+
+       builder->class = class;
+       builder->encoding = encoding;
+       builder->tag = new_tag;
+
+       return TRUE;
+}
+
+/*
+ * Resize the TLV because the content of Value field needs more space.
+ * If this TLV is part of another TLV, resize that one too.
+ */
+gboolean ber_tlv_builder_set_length(struct ber_tlv_builder *builder,
+                                       unsigned int new_len)
+{
+       unsigned int new_pos = builder->pos + MAX_BER_TLV_HEADER + new_len;
+
+       if (new_pos > builder->max)
+               return FALSE;
+
+       if (builder->parent)
+               ber_tlv_builder_set_length(builder->parent, new_pos);
+
+       builder->len = new_len;
+
+       return TRUE;
+}
+
+unsigned char *ber_tlv_builder_get_data(struct ber_tlv_builder *builder)
+{
+       return builder->pdu + builder->pos + MAX_BER_TLV_HEADER;
+}
+
+gboolean ber_tlv_builder_recurse(struct ber_tlv_builder *builder,
+                                       struct ber_tlv_builder *recurse)
+{
+       unsigned char *end = builder->pdu + builder->max;
+       unsigned char *data = ber_tlv_builder_get_data(builder);
+
+       if (ber_tlv_builder_init(recurse, data, end - data) == FALSE)
+               return FALSE;
+
+       recurse->parent = builder;
+
+       return TRUE;
+}
+
+gboolean ber_tlv_builder_recurse_comprehension(struct ber_tlv_builder *builder,
+                               struct comprehension_tlv_builder *recurse)
+{
+       unsigned char *end = builder->pdu + builder->max;
+       unsigned char *data = ber_tlv_builder_get_data(builder);
+
+       if (comprehension_tlv_builder_init(recurse, data, end - data) == FALSE)
+               return FALSE;
+
+       recurse->parent = builder;
+
+       return TRUE;
+}
+
+void ber_tlv_builder_optimize(struct ber_tlv_builder *builder,
+                               unsigned char **out_pdu, unsigned int *out_len)
+{
+       unsigned int len;
+       unsigned char *pdu;
+
+       ber_tlv_builder_write_header(builder);
+
+       len = builder->pos + MAX_BER_TLV_HEADER + builder->len;
+
+       for (pdu = builder->pdu; *pdu == 0xff; pdu++)
+               len--;
+
+       if (out_pdu)
+               *out_pdu = pdu;
+
+       if (out_len)
+               *out_len = len;
+}
+
+gboolean comprehension_tlv_builder_init(
+                               struct comprehension_tlv_builder *builder,
+                               unsigned char *pdu, unsigned int size)
+{
+       if (size < 2)
+               return FALSE;
+
+       builder->pdu = pdu;
+       builder->pos = 0;
+       builder->max = size;
+       builder->parent = NULL;
+       builder->len = 0;
+
+       builder->pdu[0] = 0;
+
+       return TRUE;
+}
+
+#define CTLV_TAG_FIELD_SIZE(a)                 \
+       bit_field((a), 0, 7) == 0x7f ? 3 : 1    \
+
+#define CTLV_LEN_FIELD_SIZE(a)                 \
+       (a) >= 0x80 ? (a) - 0x7f : 1            \
+
+gboolean comprehension_tlv_builder_next(
+                               struct comprehension_tlv_builder *builder,
+                               gboolean cr, unsigned short tag)
+{
+       unsigned char *tlv = builder->pdu + builder->pos;
+       unsigned int prev_size = 0;
+       unsigned int new_size;
+
+       /* Tag is invalid when we start, means we've just been inited */
+       if (tlv[0] != 0) {
+               unsigned int tag_size = CTLV_TAG_FIELD_SIZE(tlv[0]);
+               prev_size = builder->len + tag_size;
+               prev_size += CTLV_LEN_FIELD_SIZE(tlv[tag_size]);
+       }
+
+       new_size = (tag < 0x7f ? 1 : 3) + 1;
+
+       if (builder->pos + prev_size + new_size > builder->max)
+               return FALSE;
+
+       builder->pos += prev_size;
+
+       if (tag >= 0x7f) {
+               builder->pdu[builder->pos + 0] = 0x7f;
+               builder->pdu[builder->pos + 1] = (cr ? 0x80 : 0) | (tag >> 8);
+               builder->pdu[builder->pos + 2] = tag & 0xff;
+       } else
+               builder->pdu[builder->pos + 0] = (cr ? 0x80 : 0x00) | tag;
+
+       builder->len = 0;
+       builder->pdu[builder->pos + new_size - 1] = 0; /* Length */
+
+       return TRUE;
+}
+
+/*
+ * Resize the TLV because the content of Value field needs more space.
+ * If this TLV is part of another TLV, resize that one too.
+ */
+gboolean comprehension_tlv_builder_set_length(
+                               struct comprehension_tlv_builder *builder,
+                               unsigned int new_len)
+{
+       unsigned char *tlv = builder->pdu + builder->pos;
+       unsigned int tag_size = CTLV_TAG_FIELD_SIZE(tlv[0]);
+       unsigned int len_size, new_len_size;
+       unsigned int new_ctlv_len;
+       unsigned int len;
+
+       len_size = CTLV_LEN_FIELD_SIZE(tlv[tag_size]);
+       new_len_size = BTLV_LEN_FIELD_SIZE_NEEDED(new_len);
+       new_ctlv_len = tag_size + new_len_size + new_len;
+
+       /* Check there is enough space */
+       if (builder->pos + new_ctlv_len > builder->max)
+               return FALSE;
+
+       if (builder->parent)
+               ber_tlv_builder_set_length(builder->parent,
+                                               builder->pos + new_ctlv_len);
+
+       len = MIN(builder->len, new_len);
+       if (len > 0 && new_len_size != len_size)
+               memmove(tlv + tag_size + new_len_size,
+                               tlv + tag_size + len_size, len);
+
+       builder->len = new_len;
+
+       /* Write new length */
+       if (new_len_size > 1) {
+               int i;
+               unsigned int offset = tag_size;
+
+               tlv[offset++] = 0x80 + new_len_size - 1;
+
+               for (i = new_len_size - 2; i >= 0; i--)
+                       tlv[offset++] = (builder->len >> (i * 8)) & 0xff;
+       } else
+               tlv[tag_size] = builder->len;
+
+       return TRUE;
+}
+
+unsigned char *comprehension_tlv_builder_get_data(
+                               struct comprehension_tlv_builder *builder)
+{
+       unsigned char *tlv = builder->pdu + builder->pos;
+       unsigned int tag_size = CTLV_TAG_FIELD_SIZE(*tlv);
+       unsigned int len_size = CTLV_LEN_FIELD_SIZE(tlv[tag_size]);
+
+       return tlv + tag_size + len_size;
+}
+
+static char *sim_network_name_parse(const unsigned char *buffer, int length,
+                                       gboolean *add_ci)
+{
+       char *ret = NULL;
+       unsigned char *endp;
+       unsigned char dcs;
+       int i;
+       gboolean ci = FALSE;
+
+       if (length < 1)
+               return NULL;
+
+       dcs = *buffer++;
+       length--;
+
+       /*
+        * "The MS should add the letters for the Country's Initials and a
+        * separator (e.g. a space)"
+        */
+       if (is_bit_set(dcs, 4))
+               ci = TRUE;
+
+       switch (dcs & (7 << 4)) {
+       case 0x00:
+               endp = memchr(buffer, 0xff, length);
+               if (endp)
+                       length = endp - buffer;
+               ret = convert_gsm_to_utf8(buffer, length,
+                               NULL, NULL, 0xff);
+               break;
+       case 0x10:
+               if ((length % 2) == 1) {
+                       if (buffer[length - 1] != 0xff)
+                               return NULL;
+
+                       length = length - 1;
+               }
+
+               for (i = 0; i < length; i += 2)
+                       if (buffer[i] == 0xff && buffer[i + 1] == 0xff)
+                               break;
+
+               ret = g_convert((const char *) buffer, length,
+                                       "UTF-8//TRANSLIT", "UCS-2BE",
+                                       NULL, NULL, NULL);
+               break;
+       }
+
+       if (add_ci)
+               *add_ci = ci;
+
+       return ret;
+}
+
+void sim_parse_mcc_mnc(const guint8 *bcd, char *mcc, char *mnc)
+{
+       static const char digit_lut[] = "0123456789*#abd\0";
+       guint8 digit;
+
+       digit = (bcd[0] >> 0) & 0xf;
+       *mcc++ = digit_lut[digit];
+
+       digit = (bcd[0] >> 4) & 0xf;
+       *mcc++ = digit_lut[digit];
+
+       digit = (bcd[1] >> 0) & 0xf;
+       *mcc++ = digit_lut[digit];
+
+       digit = (bcd[2] >> 0) & 0xf;
+       *mnc++ = digit_lut[digit];
+
+       digit = (bcd[2] >> 4) & 0xf;
+       *mnc++ = digit_lut[digit];
+
+       digit = (bcd[1] >> 4) & 0xf;
+       *mnc++ = digit_lut[digit];
+}
+
+static inline int to_semi_oct(char in)
+{
+       int digit;
+
+       switch (in) {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+               digit = in - '0';
+               break;
+       case '*':
+               digit = 10;
+               break;
+       case '#':
+               digit = 11;
+               break;
+       case 'C':
+       case 'c':
+               digit = 12;
+               break;
+       case '?':
+               digit = 13;
+               break;
+       case 'E':
+       case 'e':
+               digit = 14;
+               break;
+       default:
+               digit = -1;
+               break;
+       }
+
+       return digit;
+}
+
+void sim_encode_mcc_mnc(guint8 *out, const char *mcc, const char *mnc)
+{
+       out[0] = to_semi_oct(mcc[0]);
+       out[0] |= to_semi_oct(mcc[1]) << 4;
+
+       out[1] = mcc[2] ? to_semi_oct(mcc[2]) : 0xf;
+       out[1] |= (mnc[2] ? to_semi_oct(mnc[2]) : 0xf) << 4;
+
+       out[2] = to_semi_oct(mnc[0]);
+       out[2] |= to_semi_oct(mnc[1]) << 4;
+}
+
+static gint spdi_operator_compare(gconstpointer a, gconstpointer b)
+{
+       const struct spdi_operator *opa = a;
+       const struct spdi_operator *opb = b;
+       gint r = strcmp(opa->mcc, opb->mcc);
+
+       if (r)
+               return r;
+
+       return strcmp(opa->mnc, opb->mnc);
+}
+
+struct sim_spdi {
+       GSList *operators;
+};
+
+struct sim_spdi *sim_spdi_new(const guint8 *tlv, int length)
+{
+       const guint8 *plmn_list_tlv;
+       const guint8 *plmn_list;
+       struct sim_spdi *spdi;
+       struct spdi_operator *oper;
+       int tlv_length;
+       int list_length;
+
+       if (length < 7)
+               return NULL;
+
+       plmn_list_tlv = ber_tlv_find_by_tag(tlv, 0xA3, length, &tlv_length);
+
+       if (plmn_list_tlv == NULL)
+               return NULL;
+
+       plmn_list = ber_tlv_find_by_tag(plmn_list_tlv, 0x80, tlv_length,
+                                               &list_length);
+
+       if (plmn_list == NULL)
+               return NULL;
+
+       spdi = g_new0(struct sim_spdi, 1);
+
+       for (list_length /= 3; list_length--; plmn_list += 3) {
+               if ((plmn_list[0] & plmn_list[1] & plmn_list[2]) == 0xff)
+                       continue;
+
+               oper = g_new0(struct spdi_operator, 1);
+
+               sim_parse_mcc_mnc(plmn_list, oper->mcc, oper->mnc);
+               spdi->operators = g_slist_insert_sorted(spdi->operators, oper,
+                                               spdi_operator_compare);
+       }
+
+       return spdi;
+}
+
+gboolean sim_spdi_lookup(struct sim_spdi *spdi,
+                               const char *mcc, const char *mnc)
+{
+       struct spdi_operator spdi_op;
+
+       if (spdi == NULL)
+               return FALSE;
+
+       g_strlcpy(spdi_op.mcc, mcc, sizeof(spdi_op.mcc));
+       g_strlcpy(spdi_op.mnc, mnc, sizeof(spdi_op.mnc));
+
+       return g_slist_find_custom(spdi->operators, &spdi_op,
+                                       spdi_operator_compare) != NULL;
+}
+
+void sim_spdi_free(struct sim_spdi *spdi)
+{
+       if (spdi == NULL)
+               return;
+
+       g_slist_foreach(spdi->operators, (GFunc)g_free, NULL);
+       g_slist_free(spdi->operators);
+       g_free(spdi);
+}
+
+static void pnn_operator_free(struct sim_eons_operator_info *oper)
+{
+       if (oper == NULL)
+               return;
+
+       g_free(oper->info);
+       g_free(oper->shortname);
+       g_free(oper->longname);
+}
+
+struct sim_eons *sim_eons_new(int pnn_records)
+{
+       struct sim_eons *eons = g_new0(struct sim_eons, 1);
+
+       eons->pnn_list = g_new0(struct sim_eons_operator_info, pnn_records);
+       eons->pnn_max = pnn_records;
+
+       return eons;
+}
+
+gboolean sim_eons_pnn_is_empty(struct sim_eons *eons)
+{
+       return !eons->pnn_valid;
+}
+
+void sim_eons_add_pnn_record(struct sim_eons *eons, int record,
+                               const guint8 *tlv, int length)
+{
+       const unsigned char *name;
+       int namelength;
+       struct sim_eons_operator_info *oper = &eons->pnn_list[record-1];
+
+       name = ber_tlv_find_by_tag(tlv, 0x43, length, &namelength);
+
+       if (name == NULL || !namelength)
+               return;
+
+       oper->longname = sim_network_name_parse(name, namelength,
+                                               &oper->long_ci);
+
+       name = ber_tlv_find_by_tag(tlv, 0x45, length, &namelength);
+
+       if (name && namelength)
+               oper->shortname = sim_network_name_parse(name, namelength,
+                                                       &oper->short_ci);
+
+       name = ber_tlv_find_by_tag(tlv, 0x80, length, &namelength);
+
+       if (name && namelength)
+               oper->info = sim_string_to_utf8(name, namelength);
+
+       eons->pnn_valid = TRUE;
+}
+
+static struct opl_operator *opl_operator_alloc(const guint8 *record)
+{
+       struct opl_operator *oper = g_new0(struct opl_operator, 1);
+
+       sim_parse_mcc_mnc(record, oper->mcc, oper->mnc);
+       record += 3;
+
+       oper->lac_tac_low = (record[0] << 8) | record[1];
+       record += 2;
+       oper->lac_tac_high = (record[0] << 8) | record[1];
+       record += 2;
+
+       oper->id = record[0];
+
+       return oper;
+}
+
+void sim_eons_add_opl_record(struct sim_eons *eons,
+                               const guint8 *contents, int length)
+{
+       struct opl_operator *oper;
+
+       oper = opl_operator_alloc(contents);
+
+       if (oper->id > eons->pnn_max) {
+               g_free(oper);
+               return;
+       }
+
+       eons->opl_list = g_slist_prepend(eons->opl_list, oper);
+}
+
+void sim_eons_optimize(struct sim_eons *eons)
+{
+       eons->opl_list = g_slist_reverse(eons->opl_list);
+}
+
+void sim_eons_free(struct sim_eons *eons)
+{
+       int i;
+
+       if (eons == NULL)
+               return;
+
+       for (i = 0; i < eons->pnn_max; i++)
+               pnn_operator_free(eons->pnn_list + i);
+
+       g_free(eons->pnn_list);
+
+       g_slist_foreach(eons->opl_list, (GFunc)g_free, NULL);
+       g_slist_free(eons->opl_list);
+
+       g_free(eons);
+}
+
+static const struct sim_eons_operator_info *
+       sim_eons_lookup_common(struct sim_eons *eons,
+                               const char *mcc, const char *mnc,
+                               gboolean have_lac, guint16 lac)
+{
+       GSList *l;
+       const struct opl_operator *opl;
+       int i;
+
+       for (l = eons->opl_list; l; l = l->next) {
+               opl = l->data;
+
+               for (i = 0; i < OFONO_MAX_MCC_LENGTH; i++)
+                       if (mcc[i] != opl->mcc[i] &&
+                                       !(opl->mcc[i] == 'b' && mcc[i]))
+                               break;
+               if (i < OFONO_MAX_MCC_LENGTH)
+                       continue;
+
+               for (i = 0; i < OFONO_MAX_MNC_LENGTH; i++)
+                       if (mnc[i] != opl->mnc[i] &&
+                                       !(opl->mnc[i] == 'b' && mnc[i]))
+                               break;
+               if (i < OFONO_MAX_MNC_LENGTH)
+                       continue;
+
+               if (opl->lac_tac_low == 0 && opl->lac_tac_high == 0xfffe)
+                       break;
+
+               if (have_lac == FALSE)
+                       continue;
+
+               if ((lac >= opl->lac_tac_low) && (lac <= opl->lac_tac_high))
+                       break;
+       }
+
+       if (l == NULL)
+               return NULL;
+
+       opl = l->data;
+
+       /* 0 is not a valid record id */
+       if (opl->id == 0)
+               return NULL;
+
+       return &eons->pnn_list[opl->id - 1];
+}
+
+const struct sim_eons_operator_info *sim_eons_lookup(struct sim_eons *eons,
+                                               const char *mcc,
+                                               const char *mnc)
+{
+       return sim_eons_lookup_common(eons, mcc, mnc, FALSE, 0);
+}
+
+const struct sim_eons_operator_info *sim_eons_lookup_with_lac(
+                                                       struct sim_eons *eons,
+                                                       const char *mcc,
+                                                       const char *mnc,
+                                                       guint16 lac)
+{
+       return sim_eons_lookup_common(eons, mcc, mnc, TRUE, lac);
+}
+
+/*
+ * Extract extended BCD format defined in 3GPP 11.11, 31.102.  The format
+ * is different from what is defined in 3GPP 24.008 and 23.040 (sms).
+ *
+ * Here the digits with values 'C', 'D' and 'E' are treated differently,
+ * for more details see 31.102 Table 4.4
+ *
+ * 'C' - DTMF Control Digit Separator, represented as 'c' by this function
+ * 'D' - Wild Value, represented as a '?' by this function
+ * 'E' - RFU, used to be used as a Shift Operator in 11.11
+ * 'F' - Endmark
+ *
+ * Note that a second or subsequent 'C' BCD value will be interpreted as a
+ * 3 second pause.
+ */
+void sim_extract_bcd_number(const unsigned char *buf, int len, char *out)
+{
+       static const char digit_lut[] = "0123456789*#c?e\0";
+       unsigned char oct;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               oct = buf[i];
+
+               out[i*2] = digit_lut[oct & 0x0f];
+               out[i*2+1] = digit_lut[(oct & 0xf0) >> 4];
+       }
+
+       out[i*2] = '\0';
+}
+
+void sim_encode_bcd_number(const char *number, unsigned char *out)
+{
+       while (number[0] != '\0' && number[1] != '\0') {
+               *out = to_semi_oct(*number++);
+               *out++ |= to_semi_oct(*number++) << 4;
+       }
+
+       if (*number)
+               *out = to_semi_oct(*number) | 0xf0;
+}
+
+gboolean sim_adn_parse(const unsigned char *data, int length,
+                       struct ofono_phone_number *ph, char **identifier)
+{
+       int number_len;
+       int ton_npi;
+       const unsigned char *alpha;
+       int alpha_length;
+
+       if (length < 14)
+               return FALSE;
+
+       alpha = data;
+       alpha_length = length - 14;
+
+       data += alpha_length;
+
+       number_len = *data++;
+       ton_npi = *data++;
+
+       if (number_len > 11 || ton_npi == 0xff)
+               return FALSE;
+
+       ph->type = ton_npi;
+
+       /* BCD coded, however the TON/NPI is given by the first byte */
+       number_len -= 1;
+       sim_extract_bcd_number(data, number_len, ph->number);
+
+       if (identifier == NULL)
+               return TRUE;
+
+       /* Alpha-Identifier field */
+       if (alpha_length > 0)
+               *identifier = sim_string_to_utf8(alpha, alpha_length);
+       else
+               *identifier = NULL;
+
+       return TRUE;
+}
+
+void sim_adn_build(unsigned char *data, int length,
+                       const struct ofono_phone_number *ph,
+                       const char *identifier)
+{
+       int number_len = strlen(ph->number);
+       unsigned char *alpha = NULL;
+       int alpha_written = 0;
+       int alpha_length;
+
+       alpha_length = length - 14;
+
+       /* Alpha-Identifier field */
+       if (alpha_length > 0) {
+               if (identifier)
+                       alpha = utf8_to_sim_string(identifier, alpha_length,
+                                                       &alpha_written);
+               if (alpha) {
+                       memcpy(data, alpha, alpha_written);
+                       g_free(alpha);
+               }
+
+               memset(data + alpha_written, 0xff,
+                               alpha_length - alpha_written);
+               data += alpha_length;
+       }
+
+       number_len = (number_len + 1) / 2;
+       *data++ = number_len + 1;
+       *data++ = ph->type;
+
+       sim_encode_bcd_number(ph->number, data);
+       memset(data + number_len, 0xff, 10 - number_len);
+       data += 10;
+
+       /* CCP1 unused */
+       *data++ = 0xff;
+       /* Ext1 unused */
+       *data++ = 0xff;
+}
+
+static int find_ef_by_id(const void *key, const void *value)
+{
+       unsigned short id = GPOINTER_TO_UINT(key);
+       const struct sim_ef_info *info = value;
+
+       return id - info->id;
+}
+
+struct sim_ef_info *sim_ef_db_lookup(unsigned short id)
+{
+       struct sim_ef_info *result;
+       unsigned int nelem = sizeof(ef_db) / sizeof(struct sim_ef_info);
+
+       result = bsearch(GUINT_TO_POINTER((unsigned int) id), ef_db, nelem,
+                               sizeof(struct sim_ef_info), find_ef_by_id);
+
+       return result;
+}
+
+gboolean sim_parse_3g_get_response(const unsigned char *data, int len,
+                                       int *file_len, int *record_len,
+                                       int *structure, unsigned char *access,
+                                       unsigned short *efid)
+{
+       const unsigned char *fcp;
+       int fcp_length;
+       const unsigned char *tlv;
+       int tlv_length;
+       int i;
+       int flen, rlen, str;
+       unsigned short id;
+       unsigned char acc[3];
+       struct sim_ef_info *info;
+
+       fcp = ber_tlv_find_by_tag(data, 0x62, len, &fcp_length);
+
+       if (fcp == NULL)
+               return FALSE;
+
+       /*
+        * Find the file size tag 0x80 according to
+        * ETSI 102.221 Section 11.1.1.3.2
+        */
+       tlv = ber_tlv_find_by_tag(fcp, 0x80, fcp_length, &tlv_length);
+
+       if (tlv == NULL || tlv_length < 2)
+               return FALSE;
+
+       flen = tlv[0];
+       for (i = 1; i < tlv_length; i++)
+               flen = (flen << 8) | tlv[i];
+
+       tlv = ber_tlv_find_by_tag(fcp, 0x83, fcp_length, &tlv_length);
+
+       if (tlv == NULL || tlv_length != 2)
+               return FALSE;
+
+       id = (tlv[0] << 8) | tlv[1];
+
+       tlv = ber_tlv_find_by_tag(fcp, 0x82, fcp_length, &tlv_length);
+
+       if (tlv == NULL || (tlv_length != 2 && tlv_length != 5))
+               return FALSE;
+
+       if (tlv[1] != 0x21)
+               return FALSE;
+
+       switch (tlv[0] & 0x3) {
+       case 1: /* Transparent */
+               str = 0x00;
+               break;
+       case 2: /* Linear Fixed */
+               str = 0x01;
+               break;
+       case 6: /* Cyclic */
+               str = 0x03;
+               break;
+       default:
+               return FALSE;
+       };
+
+       /* For cyclic or linear fixed we need record size & num records */
+       if (str != 0x00 && tlv_length != 5)
+               return FALSE;
+
+       /*
+        * strictly speaking the record length is 16 bit, but the valid
+        * range is 0x01 to 0xFF according to 102.221
+        */
+       if (str != 0x00)
+               rlen = tlv[3];
+       else
+               rlen = 0;
+
+       /*
+        * The 3G response data contains references to EFarr which actually
+        * contains the security attributes.  These are usually not carried
+        * along with the response data unlike in 2G.  Instead of querying
+        * this, we simply look it up in our database.  We fudge it somewhat
+        * and guess if the file isn't found.
+        */
+       info = sim_ef_db_lookup(id);
+
+       if (str == 0x03)
+               acc[1] = 0x1f;
+       else
+               acc[1] = 0xff;
+
+       acc[2] = 0x44;
+
+       if (info == NULL)
+               acc[0] = 0x11;
+       else
+               acc[0] = (info->perm_read << 4) | info->perm_update;
+
+       if (file_len)
+               *file_len = flen;
+
+       if (record_len)
+               *record_len = rlen;
+
+       if (efid)
+               *efid = id;
+
+       if (structure)
+               *structure = str;
+
+       if (access)
+               memcpy(access, acc, 3);
+
+       return TRUE;
+}
+
+gboolean sim_parse_2g_get_response(const unsigned char *response, int len,
+                                       int *file_len, int *record_len,
+                                       int *structure, unsigned char *access,
+                                       unsigned char *file_status)
+{
+       if (len < 14 || response[6] != 0x04)
+               return FALSE;
+
+       if ((response[13] == 0x01 || response[13] == 0x03) && len < 15)
+               return FALSE;
+
+       *file_len = (response[2] << 8) | response[3];
+       *structure = response[13];
+
+       access[0] = response[8];
+       access[1] = response[9];
+       access[2] = response[10];
+
+       *file_status = response[11];
+
+       if (response[13] == 0x01 || response[13] == 0x03)
+               *record_len = response[14];
+       else
+               *record_len = 0;
+
+       return TRUE;
+}
+
+gboolean sim_ust_is_available(unsigned char *efust, unsigned char len,
+                                               enum sim_ust_service index)
+{
+       if (index >= len * 8u)
+               return FALSE;
+
+       return (efust[index / 8] >> (index % 8)) & 1;
+}
+
+gboolean sim_est_is_active(unsigned char *efest, unsigned char len,
+                                               enum sim_est_service index)
+{
+       if (index >= len * 8u)
+               return FALSE;
+
+       return (efest[index / 8] >> (index % 8)) & 1;
+}
+
+gboolean sim_sst_is_available(unsigned char *efsst, unsigned char len,
+                                               enum sim_sst_service index)
+{
+       if (index >= len * 4u)
+               return FALSE;
+
+       return (efsst[index / 4] >> ((index % 4) * 2)) & 1;
+}
+
+gboolean sim_sst_is_active(unsigned char *efsst, unsigned char len,
+                                               enum sim_sst_service index)
+{
+       if (index >= len * 4u)
+               return FALSE;
+
+       return (efsst[index / 4] >> (((index % 4) * 2) + 1)) & 1;
+}
+
+gboolean sim_cphs_is_active(unsigned char *cphs, enum sim_cphs_service index)
+{
+       if (index >= 2 * 4u)
+               return FALSE;
+
+       return ((cphs[index / 4] >> ((index % 4) * 2)) & 3) == 3;
+}
+
+GSList *sim_parse_app_template_entries(const unsigned char *buffer, int len)
+{
+       GSList *ret = NULL;
+       const unsigned char *dataobj;
+       int dataobj_len;
+
+       /* Find all the application entries */
+       while ((dataobj = ber_tlv_find_by_tag(buffer, 0x61, len,
+                                               &dataobj_len)) != NULL) {
+               struct sim_app_record app;
+               const unsigned char *aid, *label;
+               int label_len;
+
+               /* Find the aid (mandatory) */
+               aid = ber_tlv_find_by_tag(dataobj, 0x4f, dataobj_len,
+                                               &app.aid_len);
+               if (!aid || app.aid_len < 0x01 || app.aid_len > 0x10)
+                       goto error;
+
+               memcpy(app.aid, aid, app.aid_len);
+
+               /* Find the label (optional) */
+               label = ber_tlv_find_by_tag(dataobj, 0x50, dataobj_len,
+                                               &label_len);
+               if (label) {
+                       /*
+                        * Label field uses the extra complicated
+                        * encoding in 102.221 Annex A
+                        */
+                       app.label = sim_string_to_utf8(label, label_len);
+
+                       if (app.label == NULL)
+                               goto error;
+               } else
+                       app.label = NULL;
+
+               ret = g_slist_prepend(ret, g_memdup(&app, sizeof(app)));
+
+               len -= (dataobj - buffer) + dataobj_len;
+               buffer = dataobj + dataobj_len;
+       }
+
+       return ret;
+
+error:
+       while (ret) {
+               GSList *t = ret;
+               struct sim_app_record *app = ret->data;
+
+               g_free(app->label);
+               g_free(app);
+
+               ret = ret->next;
+               g_slist_free_1(t);
+       }
+
+       return NULL;
+}
diff --git a/src/simutil.h b/src/simutil.h
new file mode 100644 (file)
index 0000000..2906ca0
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define SIM_EFSPN_DC_HOME_PLMN_BIT 0x1
+#define SIM_EFSPN_DC_ROAMING_SPN_BIT 0x2
+
+enum sim_fileid {
+       SIM_EFPL_FILEID =                       0x2F05,
+       SIM_EF_ICCID_FILEID =                   0x2FE2,
+       SIM_MF_FILEID =                         0x3F00,
+       SIM_EFIMG_FILEID =                      0x4F20,
+       SIM_EFPSC_FILEID =                      0x4F22,
+       SIM_EFCC_FILEID =                       0x4F23,
+       SIM_EFPUID_FILEID =                     0x4F24,
+       SIM_EFPBR_FILEID =                      0x4F30,
+       SIM_DFPHONEBOOK_FILEID =                0x5F3A,
+       SIM_DFMULTIMEDIA_FILEID =               0x5F3B,
+       SIM_EFLI_FILEID =                       0x6F05,
+       SIM_EFARR_FILEID =                      0x6F06,
+       SIM_EFIMSI_FILEID =                     0x6F07,
+       SIM_EF_CPHS_MWIS_FILEID =               0x6F11,
+       SIM_EF_CPHS_CFF_FILEID =                0x6F13,
+       SIM_EF_CPHS_SPN_FILEID =                0x6F14,
+       SIM_EF_CPHS_CSP_FILEID =                0x6F15,
+       SIM_EF_CPHS_INFORMATION_FILEID =        0x6F16,
+       SIM_EF_CPHS_MBDN_FILEID =               0x6F17,
+       SIM_EF_CPHS_SPN_SHORT_FILEID =          0x6F18,
+       SIM_EFUST_FILEID =                      0x6F38,
+       SIM_EFSST_FILEID =                      0x6F38, /* same as EFust */
+       SIM_EFADN_FILEID =                      0x6F3A,
+       SIM_EFMSISDN_FILEID =                   0x6F40,
+       SIM_EFSMSP_FILEID =                     0x6F42,
+       SIM_EFCBMI_FILEID =                     0x6F45,
+       SIM_EFSPN_FILEID =                      0x6F46,
+       SIM_EFCBMID_FILEID =                    0x6F48,
+       SIM_EFSDN_FILEID =                      0x6F49,
+       SIM_EFEXT1_FILEID =                     0x6F4A,
+       SIM_EFBDN_FILEID =                      0x6F4D,
+       SIM_EFCBMIR_FILEID =                    0x6F50,
+       SIM_EFEST_FILEID =                      0x6F56,
+       SIM_EFAD_FILEID =                       0x6FAD,
+       SIM_EFPHASE_FILEID =                    0x6FAE,
+       SIM_EFECC_FILEID =                      0x6FB7,
+       SIM_EFPNN_FILEID =                      0x6FC5,
+       SIM_EFOPL_FILEID =                      0x6FC6,
+       SIM_EFMBDN_FILEID =                     0x6FC7,
+       SIM_EFMBI_FILEID =                      0x6FC9,
+       SIM_EFMWIS_FILEID =                     0x6FCA,
+       SIM_EFCFIS_FILEID =                     0x6FCB,
+       SIM_EFSPDI_FILEID =                     0x6FCD,
+       SIM_DFTELECOM_FILEID =                  0x7F10,
+       SIM_DFGSM_FILEID =                      0x7F20,
+};
+
+/* 51.011 Section 9.3 */
+enum sim_file_access {
+       SIM_FILE_ACCESS_ALWAYS =        0,
+       SIM_FILE_ACCESS_CHV1 =          1,
+       SIM_FILE_ACCESS_CHV2 =          2,
+       SIM_FILE_ACCESS_RESERVED =      3,
+       SIM_FILE_ACCESS_ADM =           4,
+       SIM_FILE_ACCESS_NEVER =         15,
+};
+
+/* 51.011 Section 9.3 */
+enum sim_file_status {
+       SIM_FILE_STATUS_VALID =                 0x01,
+       SIM_FILE_STATUS_RW_WHEN_INVALID =       0x04,
+};
+
+/* 131.102 Section 4.2.8 */
+enum sim_ust_service {
+       SIM_UST_SERVICE_LOCAL_PHONE_BOOK =              0,
+       SIM_UST_SERVICE_FDN =                           1,
+       SIM_UST_SERVICE_EXT_2 =                         2,
+       SIM_UST_SERVICE_SDN =                           3,
+       SIM_UST_SERVICE_EXT_3 =                         4,
+       SIM_UST_SERVICE_BDN =                           5,
+       SIM_UST_SERVICE_EXT_4 =                         6,
+       SIM_UST_SERVICE_OCI_OCT =                       7,
+       SIM_UST_SERVICE_ICI_ICT =                       8,
+       SIM_UST_SERVICE_SMS =                           9,
+       SIM_UST_SERVICE_SMSR =                          10,
+       SIM_UST_SERVICE_SMSP =                          11,
+       SIM_UST_SERVICE_AOC =                           12,
+       SIM_UST_SERVICE_CCP2 =                          13,
+       SIM_UST_SERVICE_CBS_ID =                        14,
+       SIM_UST_SERVICE_CBS_ID_RANGE =                  15,
+       SIM_UST_SERVICE_GROUP_ID_LEVEL_1 =              16,
+       SIM_UST_SERVICE_GROUP_ID_LEVEL_2 =              17,
+       SIM_UST_SERVICE_PROVIDER_NAME =                 18,
+       SIM_UST_SERVICE_USER_PLMN =                     19,
+       SIM_UST_SERVICE_MSISDN =                        20,
+       SIM_UST_SERVICE_IMG =                           21,
+       SIM_UST_SERVICE_SOLSA =                         22,
+       SIM_UST_SERVICE_PRECEDENCE_PREEMPTION =         23,
+       SIM_UST_SERVICE_EMLPP =                         24,
+       SIM_UST_SERVICE_GSM_ACCESS =                    26,
+       SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_PP =          27,
+       SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_CB =          28,
+       SIM_UST_SERVICE_CALL_CONTROL_USIM =             29,
+       SIM_UST_SERVICE_MO_SMS_USIM =                   30,
+       SIM_UST_SERVICE_RUN_AT_COMMAND =                31,
+       SIM_UST_SERVICE_ENABLED_SERVICE_TABLE =         33,
+       SIM_UST_SERVICE_ACL =                           34,
+       SIM_UST_SERVICE_DEPERSONALISATION_CTRL_KEY =    35,
+       SIM_UST_SERVICE_NETWORK_LIST =                  36,
+       SIM_UST_SERVICE_GSM_SECURITY_CONTEXT =          37,
+       SIM_UST_SERVICE_CPBCCH =                        38,
+       SIM_UST_SERVICE_INVESTIGATION_SCAN =            39,
+       SIM_UST_SERVICE_MEXE =                          40,
+       SIM_UST_SERVICE_OPERATOR_PLMN =                 41,
+       SIM_UST_SERVICE_HPLMN =                         42,
+       SIM_UST_SERVICE_EXT_5 =                         43,
+       SIM_UST_SERVICE_PLMN_NETWORK_NAME =             44,
+       SIM_UST_SERVICE_OPERATOR_PLMN_LIST =            45,
+       SIM_UST_SERVICE_MAILBOX_DIALLING_NUMBERS =      46,
+       SIM_UST_SERVICE_MWIS =                          47,
+       SIM_UST_SERVICE_CFIS =                          48,
+       SIM_UST_SERVICE_PROVIDER_DISPLAY_INFO =         50,
+       SIM_UST_SERVICE_MMS =                           51,
+       SIM_UST_SERVICE_EXT_8 =                         52,
+       SIM_UST_SERVICE_CALL_CONTROL_GPRS_USIM =        53,
+       SIM_UST_SERVICE_MMS_USER_CONN_PARAM =           54,
+       SIM_UST_SERVICE_NIA =                           55,
+       SIM_UST_SERVICE_EFVGCS_EFVGCSS =                56,
+       SIM_UST_SERVICE_EFVBS_EFVBSS =                  57,
+       SIM_UST_SERVICE_PSEUDONYM =                     58,
+       SIM_UST_SERVICE_USER_PLMN_I_WLAN =              59,
+       SIM_UST_SERVICE_OPERATOR_PLMN_I_WLAN =          60,
+       SIM_UST_SERVICE_USER_WSID =                     61,
+       SIM_UST_SERVICE_OPERATOR_WSID =                 62,
+       SIM_UST_SERVICE_VGCS_SECURITY =                 63,
+       SIM_UST_SERVICE_VBS_SECURITY =                  64,
+       SIM_UST_SERVICE_WLAN_REAUTH_ID =                65,
+       SIM_UST_SERVICE_MMS_STORAGE =                   66,
+       SIM_UST_SERVICE_GBA =                           67,
+       SIM_UST_SERVICE_MBMS_SECURITY =                 68,
+       SIM_UST_SERVICE_USSD_APPLICATION_MODE =         69,
+       SIM_UST_SERVICE_EQUIVALENT_HPLMN =              70,
+       SIM_UST_SERVICE_ADDITIONAL_TERMINAL_PROFILE =   71,
+       SIM_UST_SERVICE_EQUIVALENT_HPLMN_IND =          72,
+       SIM_UST_SERVICE_LAST_RPLMN_IND =                73,
+       SIM_UST_SERVICE_OMA_BCAST_SC_PROFILE =          74,
+       SIM_UST_SERVICE_BGA_LOCAL_KEY =                 75,
+       SIM_UST_SERVICE_TERMINAL_APPLICATIONS =         76,
+       SIM_UST_SERVICE_PROVIDER_NAME_ICON =            77,
+       SIM_UST_SERVICE_PLMN_NETWORK_NAME_ICON =        78,
+       SIM_UST_SERVICE_CONN_PARAM_USIM_IP =            79,
+       SIM_UST_SERVICE_HOME_I_WLAN_ID_LIST =           80,
+       SIM_UST_SERVICE_I_WLAN_EQUIVALENT_HPLMN_IND =   81,
+       SIM_UST_SERVICE_I_WLAN_HPLMN_PRIORITY_IND =     82,
+       SIM_UST_SERVICE_I_WLAN_LAST_PLMN =              83,
+       SIM_UST_SERVICE_EPS_INFO =                      84,
+       SIM_UST_SERVICE_CSG_IND =                       85,
+       SIM_UST_SERVICE_CALL_CONTROL_EPS_PDN_USIM =     86,
+       SIM_UST_SERVICE_HPLMN_DIRECT_ACCESS =           87,
+       SIM_UST_SERVICE_ECALL_DATA =                    88,
+       SIM_UST_SERVICE_OPERATOR_CSG =                  89
+};
+
+/* 131.102 Section 4.2.47 */
+enum sim_est_service {
+       SIM_EST_SERVICE_FDN =           0,
+       SIM_EST_SERVICE_BDN =           1,
+       SIM_EST_SERVICE_ACL =           2
+};
+
+/* 51.011 Section 10.3.7 */
+enum sim_sst_service {
+       SIM_SST_SERVICE_CHV1_DISABLE =                  0,
+       SIM_SST_SERVICE_ADN =                           1,
+       SIM_SST_SERVICE_FDN =                           2,
+       SIM_SST_SERVICE_SMS =                           3,
+       SIM_SST_SERVICE_AOC =                           4,
+       SIM_SST_SERVICE_CCP =                           5,
+       SIM_SST_SERVICE_PLMN_SELECTOR =                 6,
+       SIM_SST_SERVICE_MSISDN =                        8,
+       SIM_SST_SERVICE_EXT_1 =                         9,
+       SIM_SST_SERVICE_EXT_2 =                         10,
+       SIM_SST_SERVICE_SMSP =                          11,
+       SIM_SST_SERVICE_LND =                           12,
+       SIM_SST_SERVICE_CBS_ID =                        13,
+       SIM_SST_SERVICE_GROUP_ID_LEVEL_1 =              14,
+       SIM_SST_SERVICE_GROUP_ID_LEVEL_2 =              15,
+       SIM_SST_SERVICE_PROVIDER_NAME =                 16,
+       SIM_SST_SERVICE_SDN =                           17,
+       SIM_SST_SERVICE_EXT_3 =                         18,
+       SIM_SST_SERVICE_EFVGCS_EFVGCSS =                20,
+       SIM_SST_SERVICE_EFVBS_EFVBSS =                  21,
+       SIM_SST_SERVICE_PRECEDENCE_PREEMPTION =         22,
+       SIM_SST_SERVICE_EMLPP =                         23,
+       SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_CB =          24,
+       SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_PP =          25,
+       SIM_SST_SERVICE_MENU_SELECTION =                26,
+       SIM_SST_SERVICE_CALL_CONTROL =                  27,
+       SIM_SST_SERVICE_PROACTIVE_SIM =                 28,
+       SIM_SST_SERVICE_CBS_ID_RANGE =                  29,
+       SIM_SST_SERVICE_BDN =                           30,
+       SIM_SST_SERVICE_EXT_4 =                         31,
+       SIM_SST_SERVICE_DEPERSONALISATION_CTRL_KEY =    32,
+       SIM_SST_SERVICE_NETWORK_LIST =                  33,
+       SIM_SST_SERVICE_SMSR =                          34,
+       SIM_SST_SERVICE_NIA =                           35,
+       SIM_SST_SERVICE_MO_SMS_SIM =                    36,
+       SIM_SST_SERVICE_GPRS =                          37,
+       SIM_SST_SERVICE_IMG =                           38,
+       SIM_SST_SERVICE_SOLSA =                         39,
+       SIM_SST_SERVICE_USSD_CALL_CONTROL =             40,
+       SIM_SST_SERVICE_RUN_AT_COMMAND =                41,
+       SIM_SST_SERVICE_USER_PLMN =                     42,
+       SIM_SST_SERVICE_OPERATOR_PLMN =                 43,
+       SIM_SST_SERVICE_HPLMN =                         44,
+       SIM_SST_SERVICE_CPBCCH =                        45,
+       SIM_SST_SERVICE_INVESTIGATION_SCAN =            46,
+       SIM_SST_SERVICE_EXT_CCP =                       47,
+       SIM_SST_SERVICE_MEXE =                          48,
+       SIM_SST_SERVICE_RPLMN =                         49,
+       SIM_SST_SERVICE_PLMN_NETWORK_NAME =             50,
+       SIM_SST_SERVICE_OPERATOR_PLMN_LIST =            51,
+       SIM_SST_SERVICE_MAILBOX_DIALLING_NUMBERS =      52,
+       SIM_SST_SERVICE_MWIS =                          53,
+       SIM_SST_SERVICE_CFIS =                          54,
+       SIM_SST_SERVICE_PROVIDER_DISPLAY_INFO =         55
+};
+
+/* CPHS 4.2, Section B.3.1.1 */
+enum sim_cphs_service {
+       SIM_CPHS_SERVICE_CSP =                  0x0,
+       SIM_CPHS_SERVICE_SST =                  0x1,
+       SIM_CPHS_SERVICE_MAILBOX_NUMBERS =      0x2,
+       SIM_CPHS_SERVICE_SHORT_SPN =            0x3,
+       SIM_CPHS_SERVICE_INFO_NUMBERS =         0x4,
+};
+
+/* CPHS 4.2, Section B4.7 CSP Service Group Codes */
+enum sim_csp_entry {
+       SIM_CSP_ENTRY_CALL_OFFERING =           0x01,
+       SIM_CSP_ENTRY_CALL_RESTRICTION =        0x02,
+       SIM_CSP_ENTRY_OTHER_SUPP_SERVICES =     0x03,
+       SIM_CSP_ENTRY_CALL_COMPLETION =         0x04,
+       SIM_CSP_ENTRY_TELESERVICES =            0x05,
+       SIM_CSP_ENTRY_CPHS_TELESERVICES =       0x06,
+       SIM_CSP_ENTRY_CPHS_FEATURES =           0x07,
+       SIM_CSP_ENTRY_NUMBER_IDENTIFICATION =   0x08,
+       SIM_CSP_ENTRY_PHASE_2GPLUS_SERVICES =   0x09,
+       SIM_CSP_ENTRY_VALUE_ADDED_SERVICES =    0xC0,
+       SIM_CSP_ENTRY_INFORMATION_NUMBERS =     0xD5,
+};
+
+enum ber_tlv_data_type {
+       BER_TLV_DATA_TYPE_UNIVERSAL =           0,
+       BER_TLV_DATA_TYPE_APPLICATION =         1,
+       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC =    2,
+       BER_TLV_DATA_TYPE_PRIVATE =             3,
+};
+
+enum ber_tlv_data_encoding_type {
+       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE =          0,
+       BER_TLV_DATA_ENCODING_TYPE_CONSTRUCTED =        1,
+};
+
+struct sim_eons_operator_info {
+       char *longname;
+       gboolean long_ci;
+       char *shortname;
+       gboolean short_ci;
+       char *info;
+};
+
+struct sim_ef_info {
+       unsigned short id;
+       unsigned short parent;
+       unsigned char file_type;
+       unsigned char size;
+       enum sim_file_access perm_read;
+       enum sim_file_access perm_update;
+};
+
+struct sim_app_record {
+       unsigned char aid[16];
+       int aid_len;
+       char *label;
+};
+
+struct simple_tlv_iter {
+       unsigned int max;
+       unsigned int pos;
+       const unsigned char *pdu;
+       unsigned char tag;
+       unsigned short len;
+       const unsigned char *data;
+};
+
+struct comprehension_tlv_iter {
+       unsigned int max;
+       unsigned int pos;
+       const unsigned char *pdu;
+       unsigned short tag;
+       gboolean cr;
+       unsigned int len;
+       const unsigned char *data;
+};
+
+struct ber_tlv_iter {
+       unsigned int max;
+       unsigned int pos;
+       const unsigned char *pdu;
+       unsigned int tag;
+       enum ber_tlv_data_type class;
+       enum ber_tlv_data_encoding_type encoding;
+       unsigned int len;
+       const unsigned char *data;
+};
+
+struct ber_tlv_builder {
+       unsigned int max;
+       unsigned int pos;
+       unsigned char *pdu;
+       struct ber_tlv_builder *parent;
+
+       unsigned int tag;
+       enum ber_tlv_data_type class;
+       enum ber_tlv_data_encoding_type encoding;
+       unsigned int len;
+};
+
+struct comprehension_tlv_builder {
+       unsigned int max;
+       unsigned int pos;
+       unsigned char *pdu;
+       unsigned int len;
+       struct ber_tlv_builder *parent;
+};
+
+void simple_tlv_iter_init(struct simple_tlv_iter *iter,
+                               const unsigned char *pdu, unsigned int len);
+gboolean simple_tlv_iter_next(struct simple_tlv_iter *iter);
+unsigned char simple_tlv_iter_get_tag(struct simple_tlv_iter *iter);
+unsigned short simple_tlv_iter_get_length(struct simple_tlv_iter *iter);
+const unsigned char *simple_tlv_iter_get_data(struct simple_tlv_iter *iter);
+
+void comprehension_tlv_iter_init(struct comprehension_tlv_iter *iter,
+                                       const unsigned char *pdu,
+                                       unsigned int len);
+gboolean comprehension_tlv_iter_next(struct comprehension_tlv_iter *iter);
+unsigned short comprehension_tlv_iter_get_tag(struct comprehension_tlv_iter *i);
+gboolean comprehension_tlv_get_cr(struct comprehension_tlv_iter *iter);
+unsigned int comprehension_tlv_iter_get_length(
+                                       struct comprehension_tlv_iter *iter);
+const unsigned char *comprehension_tlv_iter_get_data(
+                                       struct comprehension_tlv_iter *iter);
+
+void comprehension_tlv_iter_copy(struct comprehension_tlv_iter *from,
+                                       struct comprehension_tlv_iter *to);
+
+gboolean comprehension_tlv_builder_init(
+                               struct comprehension_tlv_builder *builder,
+                               unsigned char *pdu, unsigned int size);
+gboolean comprehension_tlv_builder_next(
+                               struct comprehension_tlv_builder *builder,
+                               gboolean cr, unsigned short tag);
+gboolean comprehension_tlv_builder_set_length(
+                               struct comprehension_tlv_builder *builder,
+                               unsigned int len);
+unsigned char *comprehension_tlv_builder_get_data(
+                               struct comprehension_tlv_builder *builder);
+
+void ber_tlv_iter_init(struct ber_tlv_iter *iter, const unsigned char *pdu,
+                       unsigned int len);
+/*
+ * Returns the tag value of the TLV.  Note that the tag value can be either
+ * short (0-30) or long
+ */
+unsigned int ber_tlv_iter_get_tag(struct ber_tlv_iter *iter);
+
+enum ber_tlv_data_type ber_tlv_iter_get_class(struct ber_tlv_iter *iter);
+enum ber_tlv_data_encoding_type ber_tlv_iter_get_encoding(
+                                               struct ber_tlv_iter *iter);
+
+/*
+ * This will return the short tag along with class and encoding information.
+ * This is more convenient to use for TLV contents of SIM Elementary Files
+ * and SIM toolkit since these elements only use short tags.  In case of an
+ * error (e.g. not a short tag) a zero is returned.  According to ISO 7816,
+ * a tag value of '00' is invalid.
+ */
+unsigned char ber_tlv_iter_get_short_tag(struct ber_tlv_iter *iter);
+unsigned int ber_tlv_iter_get_length(struct ber_tlv_iter *iter);
+
+const unsigned char *ber_tlv_iter_get_data(struct ber_tlv_iter *iter);
+
+gboolean ber_tlv_iter_next(struct ber_tlv_iter *iter);
+void ber_tlv_iter_recurse(struct ber_tlv_iter *iter,
+                               struct ber_tlv_iter *recurse);
+void ber_tlv_iter_recurse_simple(struct ber_tlv_iter *iter,
+                                       struct simple_tlv_iter *container);
+void ber_tlv_iter_recurse_comprehension(struct ber_tlv_iter *iter,
+                                       struct comprehension_tlv_iter *recurse);
+
+gboolean ber_tlv_builder_init(struct ber_tlv_builder *builder,
+                               unsigned char *pdu, unsigned int size);
+gboolean ber_tlv_builder_next(struct ber_tlv_builder *builder,
+                               enum ber_tlv_data_type class,
+                               enum ber_tlv_data_encoding_type encoding,
+                               unsigned int new_tag);
+gboolean ber_tlv_builder_set_length(struct ber_tlv_builder *builder,
+                                       unsigned int len);
+unsigned char *ber_tlv_builder_get_data(struct ber_tlv_builder *builder);
+gboolean ber_tlv_builder_recurse(struct ber_tlv_builder *builder,
+                                       struct ber_tlv_builder *recurse);
+gboolean ber_tlv_builder_recurse_comprehension(struct ber_tlv_builder *builder,
+                               struct comprehension_tlv_builder *recurse);
+void ber_tlv_builder_optimize(struct ber_tlv_builder *builder,
+                               unsigned char **pdu, unsigned int *len);
+
+struct sim_eons *sim_eons_new(int pnn_records);
+void sim_eons_add_pnn_record(struct sim_eons *eons, int record,
+                               const guint8 *tlv, int length);
+gboolean sim_eons_pnn_is_empty(struct sim_eons *eons);
+void sim_eons_add_opl_record(struct sim_eons *eons,
+                               const guint8 *contents, int length);
+void sim_eons_optimize(struct sim_eons *eons);
+const struct sim_eons_operator_info *sim_eons_lookup_with_lac(
+                                               struct sim_eons *eons,
+                                               const char *mcc,
+                                               const char *mnc,
+                                               guint16 lac);
+const struct sim_eons_operator_info *sim_eons_lookup(struct sim_eons *eons,
+                                               const char *mcc,
+                                               const char *mnc);
+void sim_eons_free(struct sim_eons *eons);
+
+void sim_parse_mcc_mnc(const guint8 *bcd, char *mcc, char *mnc);
+void sim_encode_mcc_mnc(guint8 *out, const char *mcc, const char *mnc);
+struct sim_spdi *sim_spdi_new(const guint8 *tlv, int length);
+gboolean sim_spdi_lookup(struct sim_spdi *spdi,
+                               const char *mcc, const char *mnc);
+void sim_spdi_free(struct sim_spdi *spdi);
+
+static inline enum sim_file_access file_access_condition_decode(int bcd)
+{
+       if (bcd >= 4 && bcd <= 14)
+               return SIM_FILE_ACCESS_ADM;
+       return bcd;
+}
+
+void sim_extract_bcd_number(const unsigned char *buf, int len, char *out);
+void sim_encode_bcd_number(const char *number, unsigned char *out);
+
+gboolean sim_adn_parse(const unsigned char *data, int length,
+                       struct ofono_phone_number *ph, char **identifier);
+void sim_adn_build(unsigned char *data, int length,
+                       const struct ofono_phone_number *ph,
+                       const char *identifier);
+
+struct sim_ef_info *sim_ef_db_lookup(unsigned short efid);
+
+gboolean sim_parse_3g_get_response(const unsigned char *data, int len,
+                                       int *file_len, int *record_len,
+                                       int *structure, unsigned char *access,
+                                       unsigned short *efid);
+
+gboolean sim_parse_2g_get_response(const unsigned char *response, int len,
+                                       int *file_len, int *record_len,
+                                       int *structure, unsigned char *access,
+                                       unsigned char *file_status);
+
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+                                               enum sim_ust_service index);
+gboolean sim_est_is_active(unsigned char *service_est, unsigned char len,
+                                               enum sim_est_service index);
+gboolean sim_sst_is_available(unsigned char *service_sst, unsigned char len,
+                                               enum sim_sst_service index);
+gboolean sim_sst_is_active(unsigned char *service_sst, unsigned char len,
+                                               enum sim_sst_service index);
+gboolean sim_cphs_is_active(unsigned char *service_cphs,
+                               enum sim_cphs_service index);
+
+GSList *sim_parse_app_template_entries(const unsigned char *buffer, int len);
diff --git a/src/sms.c b/src/sms.c
new file mode 100644 (file)
index 0000000..26dbd89
--- /dev/null
+++ b/src/sms.c
@@ -0,0 +1,2119 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+#include <sys/time.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "util.h"
+#include "smsutil.h"
+#include "storage.h"
+#include "simutil.h"
+#include "message.h"
+
+#define uninitialized_var(x) x = x
+
+#define MESSAGE_MANAGER_FLAG_CACHED 0x1
+#define MESSAGE_MANAGER_FLAG_TXQ_ACTIVE 0x2
+
+#define SETTINGS_STORE "sms"
+#define SETTINGS_GROUP "Settings"
+
+#define TXQ_MAX_RETRIES 4
+#define NETWORK_TIMEOUT 332
+
+static gboolean tx_next(gpointer user_data);
+
+static GSList *g_drivers = NULL;
+
+struct sms_handler {
+       struct ofono_watchlist_item item;
+       int dst;
+       int src;
+};
+
+struct ofono_sms {
+       int flags;
+       DBusMessage *pending;
+       struct ofono_phone_number sca;
+       struct sms_assembly *assembly;
+       guint ref;
+       GQueue *txq;
+       unsigned long tx_counter;
+       guint tx_source;
+       struct ofono_message_waiting *mw;
+       unsigned int mw_watch;
+       ofono_bool_t registered;
+       struct ofono_netreg *netreg;
+       unsigned int netreg_watch;
+       unsigned int status_watch;
+       GKeyFile *settings;
+       char *imsi;
+       int bearer;
+       enum sms_alphabet alphabet;
+       const struct ofono_sms_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       ofono_bool_t use_delivery_reports;
+       struct status_report_assembly *sr_assembly;
+       GHashTable *messages;
+       struct ofono_watchlist *text_handlers;
+       struct ofono_watchlist *datagram_handlers;
+};
+
+struct pending_pdu {
+       unsigned char pdu[176];
+       int tpdu_len;
+       int pdu_len;
+};
+
+struct tx_queue_entry {
+       struct pending_pdu *pdus;
+       unsigned char num_pdus;
+       unsigned char cur_pdu;
+       struct sms_address receiver;
+       struct ofono_uuid uuid;
+       unsigned int retry;
+       unsigned int flags;
+       ofono_sms_txq_submit_cb_t cb;
+       void *data;
+       ofono_destroy_func destroy;
+       unsigned long id;
+};
+
+static gboolean uuid_equal(gconstpointer v1, gconstpointer v2)
+{
+       return memcmp(v1, v2, OFONO_SHA1_UUID_LEN) == 0;
+}
+
+static gboolean port_equal(int received, int expected)
+{
+       return expected == -1 || received == expected;
+}
+
+static guint uuid_hash(gconstpointer v)
+{
+       const struct ofono_uuid *uuid = v;
+       guint h;
+
+       memcpy(&h, uuid->uuid, sizeof(h));
+
+       return h;
+}
+
+static const char *sms_bearer_to_string(int bearer)
+{
+       switch (bearer) {
+       case 0:
+               return "ps-only";
+       case 1:
+               return "cs-only";
+       case 2:
+               return "ps-preferred";
+       case 3:
+               return "cs-preferred";
+       };
+
+       return NULL;
+}
+
+static gboolean sms_bearer_from_string(const char *str, int *bearer)
+{
+       if (g_str_equal(str, "ps-only"))
+               *bearer = 0;
+       else if (g_str_equal(str, "cs-only"))
+               *bearer = 1;
+       else if (g_str_equal(str, "ps-preferred"))
+               *bearer = 2;
+       else if (g_str_equal(str, "cs-preferred"))
+               *bearer = 3;
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+static const char *sms_alphabet_to_string(enum sms_alphabet alphabet)
+{
+       switch (alphabet) {
+       case SMS_ALPHABET_TURKISH:
+               return "turkish";
+       case SMS_ALPHABET_SPANISH:
+               return "spanish";
+       case SMS_ALPHABET_PORTUGUESE:
+               return "portuguese";
+       case SMS_ALPHABET_DEFAULT:
+               return "default";
+       }
+
+       return NULL;
+}
+
+static gboolean sms_alphabet_from_string(const char *str,
+                                               enum sms_alphabet *alphabet)
+{
+       if (g_str_equal(str, "default"))
+               *alphabet = SMS_ALPHABET_DEFAULT;
+       else if (g_str_equal(str, "turkish"))
+               *alphabet = SMS_ALPHABET_TURKISH;
+       else if (g_str_equal(str, "spanish"))
+               *alphabet = SMS_ALPHABET_SPANISH;
+       else if (g_str_equal(str, "portuguese"))
+               *alphabet = SMS_ALPHABET_PORTUGUESE;
+       else
+               return FALSE;
+
+       return TRUE;
+}
+
+static unsigned int add_sms_handler(struct ofono_watchlist *watchlist,
+                                       int dst, int src, void *notify,
+                                       void *data, ofono_destroy_func destroy)
+{
+       struct sms_handler *handler;
+
+       if (notify == NULL)
+               return 0;
+
+       handler = g_try_new0(struct sms_handler, 1);
+       if (handler == NULL)
+               return 0;
+
+       handler->dst = dst;
+       handler->src = src;
+       handler->item.notify = notify;
+       handler->item.notify_data = data;
+       handler->item.destroy = destroy;
+
+       return __ofono_watchlist_add_item(watchlist,
+                               (struct ofono_watchlist_item *) handler);
+}
+
+unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms,
+                                       ofono_sms_text_notify_cb_t cb,
+                                       void *data, ofono_destroy_func destroy)
+{
+       if (sms == NULL)
+               return 0;
+
+       DBG("%p", sms);
+
+       return add_sms_handler(sms->text_handlers, -1, -1, cb, data, destroy);
+}
+
+gboolean __ofono_sms_text_watch_remove(struct ofono_sms *sms,
+                                       unsigned int id)
+{
+       if (sms == NULL)
+               return FALSE;
+
+       DBG("%p", sms);
+
+       return __ofono_watchlist_remove_item(sms->text_handlers, id);
+}
+
+unsigned int __ofono_sms_datagram_watch_add(struct ofono_sms *sms,
+                                       ofono_sms_datagram_notify_cb_t cb,
+                                       int dst, int src, void *data,
+                                       ofono_destroy_func destroy)
+{
+       if (sms == NULL)
+               return 0;
+
+       DBG("%p: dst %d, src %d", sms, dst, src);
+
+       return add_sms_handler(sms->datagram_handlers, dst, src, cb, data,
+                               destroy);
+}
+
+gboolean __ofono_sms_datagram_watch_remove(struct ofono_sms *sms,
+                                       unsigned int id)
+{
+       if (sms == NULL)
+               return FALSE;
+
+       DBG("%p", sms);
+
+       return __ofono_watchlist_remove_item(sms->datagram_handlers, id);
+}
+
+const char *__ofono_sms_message_path_from_uuid(struct ofono_sms *sms,
+                                               const struct ofono_uuid *uuid)
+{
+       return message_path_from_uuid(sms->atom, uuid);
+}
+
+static void set_bearer(struct ofono_sms *sms, int bearer)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sms->atom);
+       const char *value;
+
+       if (sms->bearer == bearer)
+               return;
+
+       sms->bearer = bearer;
+
+       value = sms_bearer_to_string(sms->bearer);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MESSAGE_MANAGER_INTERFACE,
+                                               "Bearer",
+                                               DBUS_TYPE_STRING, &value);
+}
+
+static void set_alphabet(struct ofono_sms *sms, enum sms_alphabet alphabet)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sms->atom);
+       const char *value;
+
+       if (sms->alphabet == alphabet)
+               return;
+
+       sms->alphabet = alphabet;
+
+       value = sms_alphabet_to_string(sms->alphabet);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MESSAGE_MANAGER_INTERFACE,
+                                               "Alphabet",
+                                               DBUS_TYPE_STRING, &value);
+}
+
+static void set_sca(struct ofono_sms *sms,
+                       const struct ofono_phone_number *sca)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sms->atom);
+       const char *value;
+
+       if (sms->sca.type == sca->type &&
+                       !strcmp(sms->sca.number, sca->number))
+               return;
+
+       sms->sca.type = sca->type;
+       strncpy(sms->sca.number, sca->number, OFONO_MAX_PHONE_NUMBER_LENGTH);
+       sms->sca.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+
+       value = phone_number_to_string(&sms->sca);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MESSAGE_MANAGER_INTERFACE,
+                                               "ServiceCenterAddress",
+                                               DBUS_TYPE_STRING, &value);
+}
+
+static DBusMessage *generate_get_properties_reply(struct ofono_sms *sms,
+                                                       DBusMessage *msg)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *sca;
+       const char *bearer;
+       const char *alphabet;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                               &dict);
+
+       sca = phone_number_to_string(&sms->sca);
+
+       ofono_dbus_dict_append(&dict, "ServiceCenterAddress", DBUS_TYPE_STRING,
+                               &sca);
+
+       ofono_dbus_dict_append(&dict, "UseDeliveryReports", DBUS_TYPE_BOOLEAN,
+                               &sms->use_delivery_reports);
+
+       bearer = sms_bearer_to_string(sms->bearer);
+       ofono_dbus_dict_append(&dict, "Bearer", DBUS_TYPE_STRING, &bearer);
+
+       alphabet = sms_alphabet_to_string(sms->alphabet);
+       ofono_dbus_dict_append(&dict, "Alphabet", DBUS_TYPE_STRING, &alphabet);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void sms_sca_query_cb(const struct ofono_error *error,
+                               const struct ofono_phone_number *sca,
+                               void *data)
+{
+       struct ofono_sms *sms = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               goto out;
+
+       set_sca(sms, sca);
+
+       sms->flags |= MESSAGE_MANAGER_FLAG_CACHED;
+
+out:
+       if (sms->pending) {
+               DBusMessage *reply = generate_get_properties_reply(sms,
+                                                               sms->pending);
+               __ofono_dbus_pending_reply(&sms->pending, reply);
+       }
+}
+
+static DBusMessage *sms_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_sms *sms = data;
+
+       if (sms->flags & MESSAGE_MANAGER_FLAG_CACHED)
+               return generate_get_properties_reply(sms, msg);
+
+       if (sms->pending)
+               return __ofono_error_busy(msg);
+
+       if (sms->driver->sca_query == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       sms->pending = dbus_message_ref(msg);
+
+       sms->driver->sca_query(sms, sms_sca_query_cb, sms);
+
+       return NULL;
+}
+
+static void bearer_set_query_callback(const struct ofono_error *error,
+                                       int bearer, void *data)
+{
+       struct ofono_sms *sms = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Set Bearer succeeded, but query failed");
+               reply = __ofono_error_failed(sms->pending);
+               __ofono_dbus_pending_reply(&sms->pending, reply);
+               return;
+       }
+
+       reply = dbus_message_new_method_return(sms->pending);
+       __ofono_dbus_pending_reply(&sms->pending, reply);
+
+       set_bearer(sms, bearer);
+}
+
+static void bearer_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_sms *sms = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Setting Bearer failed");
+               __ofono_dbus_pending_reply(&sms->pending,
+                                       __ofono_error_failed(sms->pending));
+               return;
+       }
+
+       sms->driver->bearer_query(sms, bearer_set_query_callback, sms);
+}
+
+static void sca_set_query_callback(const struct ofono_error *error,
+                                       const struct ofono_phone_number *sca,
+                                       void *data)
+{
+       struct ofono_sms *sms = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("Set SCA succeeded, but query failed");
+               sms->flags &= ~MESSAGE_MANAGER_FLAG_CACHED;
+               reply = __ofono_error_failed(sms->pending);
+               __ofono_dbus_pending_reply(&sms->pending, reply);
+               return;
+       }
+
+       set_sca(sms, sca);
+
+       reply = dbus_message_new_method_return(sms->pending);
+       __ofono_dbus_pending_reply(&sms->pending, reply);
+}
+
+static void sca_set_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_sms *sms = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Setting SCA failed");
+               __ofono_dbus_pending_reply(&sms->pending,
+                                       __ofono_error_failed(sms->pending));
+               return;
+       }
+
+       sms->driver->sca_query(sms, sca_set_query_callback, sms);
+}
+
+static DBusMessage *sms_set_property(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sms *sms = data;
+       DBusMessageIter iter;
+       DBusMessageIter var;
+       const char *property;
+
+       if (sms->pending)
+               return __ofono_error_busy(msg);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       if (!strcmp(property, "ServiceCenterAddress")) {
+               const char *value;
+               struct ofono_phone_number sca;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (strlen(value) == 0 || !valid_phone_number_format(value))
+                       return __ofono_error_invalid_format(msg);
+
+               if (sms->driver->sca_set == NULL ||
+                               sms->driver->sca_query == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               string_to_phone_number(value, &sca);
+
+               sms->pending = dbus_message_ref(msg);
+
+               sms->driver->sca_set(sms, &sca, sca_set_callback, sms);
+               return NULL;
+       }
+
+       if (!strcmp(property, "Bearer")) {
+               const char *value;
+               int bearer;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (sms_bearer_from_string(value, &bearer) != TRUE)
+                       return __ofono_error_invalid_format(msg);
+
+               if (sms->driver->bearer_set == NULL ||
+                               sms->driver->bearer_query == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               sms->pending = dbus_message_ref(msg);
+
+               sms->driver->bearer_set(sms, bearer, bearer_set_callback, sms);
+               return NULL;
+       }
+
+       if (!strcmp(property, "UseDeliveryReports")) {
+               const char *path = __ofono_atom_get_path(sms->atom);
+               dbus_bool_t value;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+
+               if (sms->use_delivery_reports != (ofono_bool_t) value) {
+                       sms->use_delivery_reports = value;
+                       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_MESSAGE_MANAGER_INTERFACE,
+                                               "UseDeliveryReports",
+                                               DBUS_TYPE_BOOLEAN, &value);
+               }
+
+               return NULL;
+       }
+
+       if (!strcmp(property, "Alphabet")) {
+               const char *value;
+               enum sms_alphabet alphabet;
+
+               if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+                       return __ofono_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (!sms_alphabet_from_string(value, &alphabet))
+                       return __ofono_error_invalid_format(msg);
+
+               set_alphabet(sms, alphabet);
+
+               g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+               return NULL;
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+/*
+ * Destroy/release the contents of a 'struct tx_queue_entry'
+ *
+ * This releases resources allocated *inside* @entry and @entry
+ * itself.
+ */
+static void tx_queue_entry_destroy(struct tx_queue_entry *entry)
+{
+       if (entry->destroy)
+               entry->destroy(entry->data);
+
+       g_free(entry->pdus);
+       g_free(entry);
+}
+
+static void tx_queue_entry_destroy_foreach(gpointer _entry, gpointer unused)
+{
+       tx_queue_entry_destroy(_entry);
+}
+
+static void sms_tx_queue_remove_entry(struct ofono_sms *sms, GList *entry_list,
+                                       enum message_state tx_state)
+{
+       struct tx_queue_entry *entry = entry_list->data;
+       struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom);
+
+       g_queue_delete_link(sms->txq, entry_list);
+
+       DBG("%p", entry);
+
+       if (entry->cb)
+               entry->cb(tx_state == MESSAGE_STATE_SENT, entry->data);
+
+       if (entry->flags & OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY) {
+               enum ofono_history_sms_status hs;
+
+               switch(tx_state) {
+               case MESSAGE_STATE_SENT:
+                       hs = OFONO_HISTORY_SMS_STATUS_SUBMITTED;
+                       break;
+               case MESSAGE_STATE_FAILED:
+                       hs = OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED;
+                       break;
+               case MESSAGE_STATE_CANCELLED:
+                       hs = OFONO_HISTORY_SMS_STATUS_SUBMIT_CANCELLED;
+                       break;
+               default:
+                       ofono_error("Unexpected sms state %d", tx_state);
+                       goto done;
+               }
+
+               __ofono_history_sms_send_status(modem, &entry->uuid,
+                                                               time(NULL), hs);
+       }
+
+       if (entry->flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) {
+               struct message *m;
+
+               sms_tx_backup_free(sms->imsi, entry->id, entry->flags,
+                                       ofono_uuid_to_str(&entry->uuid));
+
+               m = g_hash_table_lookup(sms->messages, &entry->uuid);
+
+               if (m != NULL) {
+                       message_set_state(m, tx_state);
+                       g_hash_table_remove(sms->messages, &entry->uuid);
+                       message_emit_removed(m,
+                                       OFONO_MESSAGE_MANAGER_INTERFACE);
+                       message_dbus_unregister(m);
+               }
+       }
+
+done:
+       tx_queue_entry_destroy(entry);
+}
+
+static void tx_finished(const struct ofono_error *error, int mr, void *data)
+{
+       struct ofono_sms *sms = data;
+       struct tx_queue_entry *entry = g_queue_peek_head(sms->txq);
+       gboolean ok = error->type == OFONO_ERROR_TYPE_NO_ERROR;
+       enum message_state tx_state;
+
+       DBG("tx_finished %p", entry);
+
+       sms->flags &= ~MESSAGE_MANAGER_FLAG_TXQ_ACTIVE;
+
+       if (ok == FALSE) {
+               /* Retry again when back in online mode */
+               /* Note this does not increment retry count */
+               if (sms->registered == FALSE)
+                       return;
+
+               tx_state = MESSAGE_STATE_FAILED;
+
+               /* Retry done only for Network Timeout failure */
+               if (error->type == OFONO_ERROR_TYPE_CMS &&
+                               error->error != NETWORK_TIMEOUT)
+                       goto next_q;
+
+               if (!(entry->flags & OFONO_SMS_SUBMIT_FLAG_RETRY))
+                       goto next_q;
+
+               entry->retry += 1;
+
+               if (entry->retry < TXQ_MAX_RETRIES) {
+                       DBG("Sending failed, retry in %d secs",
+                                       entry->retry * 5);
+                       sms->tx_source = g_timeout_add_seconds(entry->retry * 5,
+                                                               tx_next, sms);
+                       return;
+               }
+
+               DBG("Max retries reached, giving up");
+               goto next_q;
+       }
+
+       if (entry->flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS)
+               sms_tx_backup_remove(sms->imsi, entry->id, entry->flags,
+                                               ofono_uuid_to_str(&entry->uuid),
+                                               entry->cur_pdu);
+
+       entry->cur_pdu += 1;
+       entry->retry = 0;
+
+       if (entry->flags & OFONO_SMS_SUBMIT_FLAG_REQUEST_SR)
+               status_report_assembly_add_fragment(sms->sr_assembly,
+                                                       entry->uuid.uuid,
+                                                       &entry->receiver,
+                                                       mr, time(NULL),
+                                                       entry->num_pdus);
+
+       if (entry->cur_pdu < entry->num_pdus) {
+               sms->tx_source = g_timeout_add(0, tx_next, sms);
+               return;
+       }
+
+       tx_state = MESSAGE_STATE_SENT;
+
+next_q:
+       sms_tx_queue_remove_entry(sms, g_queue_peek_head_link(sms->txq),
+                                       tx_state);
+
+       if (sms->registered == FALSE)
+               return;
+
+       if (g_queue_peek_head(sms->txq)) {
+               DBG("Scheduling next");
+               sms->tx_source = g_timeout_add(0, tx_next, sms);
+       }
+}
+
+static gboolean tx_next(gpointer user_data)
+{
+       struct ofono_sms *sms = user_data;
+       int send_mms = 0;
+       struct tx_queue_entry *entry = g_queue_peek_head(sms->txq);
+       struct pending_pdu *pdu = &entry->pdus[entry->cur_pdu];
+
+       DBG("tx_next: %p", entry);
+
+       sms->tx_source = 0;
+
+       if (sms->registered == FALSE)
+               return FALSE;
+
+       if (g_queue_get_length(sms->txq) > 1
+                       || (entry->num_pdus - entry->cur_pdu) > 1)
+               send_mms = 1;
+
+       sms->flags |= MESSAGE_MANAGER_FLAG_TXQ_ACTIVE;
+
+       sms->driver->submit(sms, pdu->pdu, pdu->pdu_len, pdu->tpdu_len,
+                               send_mms, tx_finished, sms);
+
+       return FALSE;
+}
+
+static void netreg_status_watch(int status, int lac, int ci, int tech,
+                                       const char *mcc, const char *mnc,
+                                       void *data)
+{
+       struct ofono_sms *sms = data;
+
+       switch (status) {
+       case NETWORK_REGISTRATION_STATUS_REGISTERED:
+       case NETWORK_REGISTRATION_STATUS_ROAMING:
+               sms->registered = TRUE;
+               break;
+       default:
+               sms->registered = FALSE;
+               break;
+       }
+
+       if (sms->registered == FALSE)
+               return;
+
+       if (sms->tx_source > 0)
+               return;
+
+       if (g_queue_get_length(sms->txq))
+               sms->tx_source = g_timeout_add(0, tx_next, sms);
+}
+
+static void netreg_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct ofono_sms *sms = data;
+       int status;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               sms->registered = FALSE;
+               sms->status_watch = 0;
+               sms->netreg = NULL;
+               return;
+       }
+
+       sms->netreg = __ofono_atom_get_data(atom);
+       sms->status_watch = __ofono_netreg_add_status_watch(sms->netreg,
+                                       netreg_status_watch, sms, NULL);
+
+       status = ofono_netreg_get_status(sms->netreg);
+       netreg_status_watch(status, 0, 0, 0, NULL, NULL, sms);
+}
+
+
+/**
+ * Generate a UUID from an SMS PDU List
+ *
+ * @param pdu Pointer to array of PDUs data to generate the ID from
+ * @param pdus Number of entries in the \e pdu array
+ * @return 0 in error (no memory or serious code inconsistency in the
+ *     input data structures), otherwise the SMS UUID.
+ *
+ * @internal
+ *
+ * The current time is added to avoid the UUID being the same when the
+ * same message is sent to the same destination repeatedly. Note we
+ * need a high resolution time (not just seconds), otherwise resending
+ * in the same second (not that rare) could yield the same UUID.
+ */
+static gboolean sms_uuid_from_pdus(const struct pending_pdu *pdu,
+                                       unsigned char pdus,
+                                       struct ofono_uuid *uuid)
+
+{
+       GChecksum *checksum;
+       gsize uuid_size = sizeof(uuid->uuid);
+       unsigned int cnt;
+       struct timeval now;
+
+       checksum = g_checksum_new(G_CHECKSUM_SHA1);
+       if (checksum == NULL)
+               return FALSE;
+
+       for (cnt = 0; cnt < pdus; cnt++)
+               g_checksum_update(checksum, pdu[cnt].pdu, pdu[cnt].pdu_len);
+
+       gettimeofday(&now, NULL);
+       g_checksum_update(checksum, (void *) &now, sizeof(now));
+
+       g_checksum_get_digest(checksum, uuid->uuid, &uuid_size);
+       g_checksum_free(checksum);
+
+       return TRUE;
+}
+
+static struct tx_queue_entry *tx_queue_entry_new(GSList *msg_list,
+                                                       unsigned int flags)
+{
+       struct tx_queue_entry *entry;
+       int i = 0;
+       GSList *l;
+
+       entry = g_try_new0(struct tx_queue_entry, 1);
+       if (entry == NULL)
+               return NULL;
+
+       entry->num_pdus = g_slist_length(msg_list);
+
+       entry->pdus = g_try_new0(struct pending_pdu, entry->num_pdus);
+       if (entry->pdus == NULL)
+               goto error;
+
+       if (flags & OFONO_SMS_SUBMIT_FLAG_REQUEST_SR) {
+               struct sms *head = msg_list->data;
+
+               memcpy(&entry->receiver, &head->submit.daddr,
+                               sizeof(entry->receiver));
+       }
+
+       entry->flags = flags;
+
+       for (l = msg_list; l; l = l->next) {
+               struct pending_pdu *pdu = &entry->pdus[i++];
+               struct sms *s = l->data;
+
+               sms_encode(s, &pdu->pdu_len, &pdu->tpdu_len, pdu->pdu);
+
+               DBG("pdu_len: %d, tpdu_len: %d",
+                               pdu->pdu_len, pdu->tpdu_len);
+       }
+
+       if (flags & OFONO_SMS_SUBMIT_FLAG_REUSE_UUID)
+               return entry;
+
+       if (sms_uuid_from_pdus(entry->pdus, entry->num_pdus, &entry->uuid))
+               return entry;
+
+error:
+       g_free(entry->pdus);
+       g_free(entry);
+
+       return NULL;
+}
+
+static void tx_queue_entry_set_submit_notify(struct tx_queue_entry *entry,
+                                               ofono_sms_txq_submit_cb_t cb,
+                                               void *data,
+                                               ofono_destroy_func destroy)
+{
+       entry->cb = cb;
+       entry->data = data;
+       entry->destroy = destroy;
+}
+
+static void message_queued(struct ofono_sms *sms,
+                               const struct ofono_uuid *uuid, void *data)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *msg = data;
+       const char *path;
+
+       path = __ofono_sms_message_path_from_uuid(sms, uuid);
+       g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID);
+}
+
+/*
+ * Pre-process a SMS text message and deliver it [D-Bus SendMessage()]
+ *
+ * @conn: D-Bus connection
+ * @msg: message data (telephone number and text)
+ * @data: SMS object to use for transmision
+ *
+ * An alphabet is chosen for the text and it (might be) segmented in
+ * fragments by sms_text_prepare() into @msg_list. A queue list @entry
+ * is created by tx_queue_entry_new() and g_queue_push_tail()
+ * appends that entry to the SMS transmit queue. Then the tx_next()
+ * function is scheduled to run to process the queue.
+ */
+static DBusMessage *sms_send_message(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sms *sms = data;
+       const char *to;
+       const char *text;
+       GSList *msg_list;
+       struct ofono_modem *modem;
+       unsigned int flags;
+       gboolean use_16bit_ref = FALSE;
+       int err;
+       struct ofono_uuid uuid;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_INVALID))
+               return __ofono_error_invalid_args(msg);
+
+       if (valid_phone_number_format(to) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       msg_list = sms_text_prepare_with_alphabet(to, text, sms->ref,
+                                               use_16bit_ref,
+                                               sms->use_delivery_reports,
+                                               sms->alphabet);
+
+       if (msg_list == NULL)
+               return __ofono_error_invalid_format(msg);
+
+       flags = OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY;
+       flags |= OFONO_SMS_SUBMIT_FLAG_RETRY;
+       flags |= OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS;
+       if (sms->use_delivery_reports)
+               flags |= OFONO_SMS_SUBMIT_FLAG_REQUEST_SR;
+
+       err = __ofono_sms_txq_submit(sms, msg_list, flags, &uuid,
+                                       message_queued, msg);
+
+       g_slist_foreach(msg_list, (GFunc) g_free, NULL);
+       g_slist_free(msg_list);
+
+       if (err < 0)
+               return __ofono_error_failed(msg);
+
+       modem = __ofono_atom_get_modem(sms->atom);
+       __ofono_history_sms_send_pending(modem, &uuid, to, time(NULL), text);
+
+       return NULL;
+}
+
+static DBusMessage *sms_get_messages(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_sms *sms = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter array;
+       DBusMessageIter entry, dict;
+       const char *path;
+       GHashTableIter hashiter;
+       gpointer key, value;
+       struct message *m;
+       const struct ofono_uuid *uuid;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+
+       g_hash_table_iter_init(&hashiter, sms->messages);
+
+       while (g_hash_table_iter_next(&hashiter, &key, &value)) {
+               m = value;
+               uuid = message_get_uuid(m);
+
+               path = __ofono_sms_message_path_from_uuid(sms, uuid);
+
+               dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT,
+                                                       NULL, &entry);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                                               &path);
+               dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+               message_append_properties(m, &dict);
+               dbus_message_iter_close_container(&entry, &dict);
+               dbus_message_iter_close_container(&array, &entry);
+       }
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static gint entry_compare_by_uuid(gconstpointer a, gconstpointer b)
+{
+       const struct tx_queue_entry *entry = a;
+       const struct ofono_uuid *uuid = b;
+
+       return memcmp(&entry->uuid, uuid, sizeof(entry->uuid));
+}
+
+int __ofono_sms_txq_cancel(struct ofono_sms *sms, const struct ofono_uuid *uuid)
+{
+       GList *l;
+       struct tx_queue_entry *entry;
+
+       l = g_queue_find_custom(sms->txq, uuid, entry_compare_by_uuid);
+
+       if (l == NULL)
+               return -ENOENT;
+
+       entry = l->data;
+
+       if (entry == g_queue_peek_head(sms->txq)) {
+               /*
+                * Fail if any pdu was already transmitted or if we are
+                * waiting the answer from driver.
+                */
+               if (entry->cur_pdu > 0)
+                       return -EPERM;
+
+               if (sms->flags & MESSAGE_MANAGER_FLAG_TXQ_ACTIVE)
+                       return -EPERM;
+               /*
+                * Make sure we don't call tx_next() if there are no entries
+                * and that next entry doesn't have to wait a 'retry time'
+                * from this one.
+                */
+               if (sms->tx_source) {
+                       g_source_remove(sms->tx_source);
+                       sms->tx_source = 0;
+
+                       if (g_queue_get_length(sms->txq) > 1)
+                               sms->tx_source = g_timeout_add(0, tx_next, sms);
+               }
+       }
+
+       sms_tx_queue_remove_entry(sms, l, MESSAGE_STATE_CANCELLED);
+
+       return 0;
+}
+
+static GDBusMethodTable sms_manager_methods[] = {
+       { "GetProperties",    "",    "a{sv}",        sms_get_properties,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SetProperty",      "sv",  "",             sms_set_property,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SendMessage",      "ss",  "o",             sms_send_message,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "GetMessages",       "",    "a(oa{sv})",    sms_get_messages },
+       { }
+};
+
+static GDBusSignalTable sms_manager_signals[] = {
+       { "PropertyChanged",    "sv"            },
+       { "IncomingMessage",    "sa{sv}"        },
+       { "ImmediateMessage",   "sa{sv}"        },
+       { "MessageAdded",       "oa{sv}"        },
+       { "MessageRemoved",     "o"             },
+       { }
+};
+
+static gboolean compute_incoming_msgid(GSList *sms_list,
+                                               struct ofono_uuid *uuid)
+{
+       GChecksum *checksum;
+       GSList *l;
+       const struct sms *s;
+       unsigned char buf[176];
+       gsize uuid_size = sizeof(uuid->uuid);
+       int len;
+
+       checksum = g_checksum_new(G_CHECKSUM_SHA1);
+       if (checksum == NULL)
+               return FALSE;
+
+       for (l = sms_list; l; l = l->next) {
+               s = l->data;
+
+               if (sms_encode(s, &len, NULL, buf) == FALSE) {
+                       g_checksum_free(checksum);
+                       return FALSE;
+               }
+
+               g_checksum_update(checksum, buf, len);
+       }
+
+       g_checksum_get_digest(checksum, uuid->uuid, &uuid_size);
+       g_checksum_free(checksum);
+
+       return TRUE;
+}
+
+static void dispatch_app_datagram(struct ofono_sms *sms,
+                                       const struct ofono_uuid *uuid,
+                                       int dst, int src,
+                                       unsigned char *buf, unsigned len,
+                                       const struct sms_address *addr,
+                                       const struct sms_scts *scts)
+{
+       const char *sender = sms_address_to_string(addr);
+       time_t ts;
+       struct tm remote;
+       struct tm local;
+
+       ofono_sms_datagram_notify_cb_t notify;
+       struct sms_handler *h;
+       GSList *l;
+
+       ts = sms_scts_to_time(scts, &remote);
+       localtime_r(&ts, &local);
+
+       for (l = sms->datagram_handlers->items; l; l = l->next) {
+               h = l->data;
+               notify = h->item.notify;
+
+               if (!port_equal(dst, h->dst) || !port_equal(src, h->src))
+                       continue;
+
+               notify(sender, &remote, &local, dst, src, buf, len,
+                       h->item.notify_data);
+       }
+}
+
+static void dispatch_text_message(struct ofono_sms *sms,
+                                       const struct ofono_uuid *uuid,
+                                       const char *message,
+                                       enum sms_class cls,
+                                       const struct sms_address *addr,
+                                       const struct sms_scts *scts)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(sms->atom);
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       char buf[128];
+       const char *signal_name;
+       time_t ts;
+       struct tm remote;
+       struct tm local;
+       const char *str = buf;
+       ofono_sms_text_notify_cb_t notify;
+       struct sms_handler *h;
+       GSList *l;
+
+       if (message == NULL)
+               return;
+
+       if (cls == SMS_CLASS_0)
+               signal_name = "ImmediateMessage";
+       else
+               signal_name = "IncomingMessage";
+
+       signal = dbus_message_new_signal(path, OFONO_MESSAGE_MANAGER_INTERFACE,
+                                               signal_name);
+
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                               &dict);
+
+       ts = sms_scts_to_time(scts, &remote);
+       localtime_r(&ts, &local);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", &local);
+       buf[127] = '\0';
+       ofono_dbus_dict_append(&dict, "LocalSentTime", DBUS_TYPE_STRING, &str);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", &remote);
+       buf[127] = '\0';
+       ofono_dbus_dict_append(&dict, "SentTime", DBUS_TYPE_STRING, &str);
+
+       str = sms_address_to_string(addr);
+       ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &str);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(conn, signal);
+
+       if (cls == SMS_CLASS_0)
+               return;
+
+       for (l = sms->text_handlers->items; l; l = l->next) {
+               h = l->data;
+               notify = h->item.notify;
+
+               notify(str, &remote, &local, message, h->item.notify_data);
+       }
+
+       __ofono_history_sms_received(modem, uuid, str, &remote, &local,
+                                       message);
+}
+
+static void sms_dispatch(struct ofono_sms *sms, GSList *sms_list)
+{
+       GSList *l;
+       const struct sms *s;
+       struct ofono_uuid uuid;
+       enum sms_charset uninitialized_var(old_charset);
+       enum sms_class cls;
+       int srcport = -1;
+       int dstport = -1;
+
+       DBG("");
+
+       if (sms_list == NULL)
+               return;
+
+       /*
+        * Qutoting 23.040: The TP elements in the SMS‑SUBMIT PDU, apart from
+        * TP‑MR, TP-SRR, TP‑UDL and TP‑UD, should remain unchanged for each
+        * SM which forms part of a concatenated SM, otherwise this may lead
+        * to irrational behaviour
+        *
+        * This means that we assume that at least the charset is the same
+        * across all parts of the SMS in the case of 8-bit data.  Other
+        * cases can be handled by converting to UTF8.
+        *
+        * We also check that if 8-bit or 16-bit application addressing is
+        * used, the addresses are the same across all segments.
+        */
+
+       for (l = sms_list; l; l = l->next) {
+               guint8 dcs;
+               gboolean comp = FALSE;
+               enum sms_charset charset;
+               int cdst = -1;
+               int csrc = -1;
+               gboolean is_8bit;
+
+               s = l->data;
+               dcs = s->deliver.dcs;
+
+               if (sms_mwi_dcs_decode(dcs, NULL, &charset, NULL, NULL))
+                       cls = SMS_CLASS_UNSPECIFIED;
+               else if (!sms_dcs_decode(dcs, &cls, &charset, &comp, NULL)) {
+                       ofono_error("The deliver DCS is not recognized");
+                       return;
+               }
+
+               if (comp) {
+                       ofono_error("Compressed data not supported");
+                       return;
+               }
+
+               if (l == sms_list)
+                       old_charset = charset;
+
+               if (charset == SMS_CHARSET_8BIT && charset != old_charset) {
+                       ofono_error("Can't concatenate disparate charsets");
+                       return;
+               }
+
+               if (sms_extract_app_port(s, &cdst, &csrc, &is_8bit)) {
+                       csrc = is_8bit ? (csrc << 16) : csrc;
+                       cdst = is_8bit ? (cdst << 16) : cdst;
+
+                       if (l == sms_list) {
+                               srcport = csrc;
+                               dstport = cdst;
+                       }
+               }
+
+               DBG("dst %d src %d", cdst, csrc);
+
+               if (srcport != csrc || dstport != cdst) {
+                       ofono_error("Source / Destination ports across "
+                                       "concatenated message are not the "
+                                       "same, ignoring");
+                       return;
+               }
+       }
+
+       if (!compute_incoming_msgid(sms_list, &uuid))
+               return;
+
+       s = sms_list->data;
+
+       /* Handle datagram */
+       if (old_charset == SMS_CHARSET_8BIT) {
+               unsigned char *buf;
+               long len;
+
+               if (srcport == -1 || dstport == -1) {
+                       ofono_error("Got an 8-bit encoded message, however "
+                                       "no valid src/address port, ignore");
+                       return;
+               }
+
+               buf = sms_decode_datagram(sms_list, &len);
+               if (buf == NULL)
+                       return;
+
+               dispatch_app_datagram(sms, &uuid, dstport, srcport, buf, len,
+                                       &s->deliver.oaddr, &s->deliver.scts);
+
+               g_free(buf);
+       } else {
+               char *message = sms_decode_text(sms_list);
+
+               if (message == NULL)
+                       return;
+
+               dispatch_text_message(sms, &uuid, message, cls,
+                                       &s->deliver.oaddr, &s->deliver.scts);
+
+               g_free(message);
+       }
+}
+
+static void handle_deliver(struct ofono_sms *sms, const struct sms *incoming)
+{
+       GSList *l;
+       guint16 ref;
+       guint8 max;
+       guint8 seq;
+
+       DBG("");
+
+       if (sms_extract_concatenation(incoming, &ref, &max, &seq)) {
+               GSList *sms_list;
+
+               if (sms->assembly == NULL)
+                       return;
+
+               sms_list = sms_assembly_add_fragment(sms->assembly,
+                                               incoming, time(NULL),
+                                               &incoming->deliver.oaddr,
+                                               ref, max, seq);
+
+               if (sms_list == NULL)
+                       return;
+
+               sms_dispatch(sms, sms_list);
+               g_slist_foreach(sms_list, (GFunc) g_free, NULL);
+               g_slist_free(sms_list);
+
+               return;
+       }
+
+       l = g_slist_append(NULL, (void *) incoming);
+       sms_dispatch(sms, l);
+       g_slist_free(l);
+}
+
+static void handle_sms_status_report(struct ofono_sms *sms,
+                                               const struct sms *incoming)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom);
+       gboolean delivered;
+       struct ofono_uuid uuid;
+
+       DBG("");
+
+       if (status_report_assembly_report(sms->sr_assembly, incoming, uuid.uuid,
+                                               &delivered) == FALSE)
+               return;
+
+       __ofono_history_sms_send_status(modem, &uuid, time(NULL),
+                       delivered ? OFONO_HISTORY_SMS_STATUS_DELIVERED :
+                       OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED);
+}
+
+
+static inline gboolean handle_mwi(struct ofono_sms *sms, struct sms *s)
+{
+       gboolean discard;
+
+       DBG("");
+
+       if (sms->mw == NULL)
+               return FALSE;
+
+       __ofono_message_waiting_mwi(sms->mw, s, &discard);
+
+       return discard;
+}
+
+void ofono_sms_deliver_notify(struct ofono_sms *sms, unsigned char *pdu,
+                               int len, int tpdu_len)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom);
+       struct ofono_sim *sim;
+       struct ofono_stk *stk;
+       struct sms s;
+       enum sms_class cls;
+
+       DBG("len %d tpdu len %d", len, tpdu_len);
+
+       if (!sms_decode(pdu, len, FALSE, tpdu_len, &s)) {
+               ofono_error("Unable to decode PDU");
+               return;
+       }
+
+       if (s.type != SMS_TYPE_DELIVER) {
+               ofono_error("Expecting a DELIVER pdu");
+               return;
+       }
+
+       if (s.deliver.pid == SMS_PID_TYPE_SM_TYPE_0) {
+               DBG("Explicitly ignoring type 0 SMS");
+               return;
+       }
+
+       /*
+        * This is an older style MWI notification, process MWI
+        * headers and handle it like any other message
+        */
+       if (s.deliver.pid == SMS_PID_TYPE_RETURN_CALL) {
+               if (handle_mwi(sms, &s))
+                       return;
+
+               goto out;
+       }
+
+       /*
+        * The DCS indicates this is an MWI notification, process it
+        * and then handle the User-Data as any other message
+        */
+       if (sms_mwi_dcs_decode(s.deliver.dcs, NULL, NULL, NULL, NULL)) {
+               if (handle_mwi(sms, &s))
+                       return;
+
+               goto out;
+       }
+
+       if (!sms_dcs_decode(s.deliver.dcs, &cls, NULL, NULL, NULL)) {
+               ofono_error("Unknown / Reserved DCS.  Ignoring");
+               return;
+       }
+
+       switch (s.deliver.pid) {
+       case SMS_PID_TYPE_ME_DOWNLOAD:
+               if (cls == SMS_CLASS_1) {
+                       ofono_error("ME Download message ignored");
+                       return;
+               }
+
+               break;
+       case SMS_PID_TYPE_ME_DEPERSONALIZATION:
+               if (s.deliver.dcs == 0x11) {
+                       ofono_error("ME Depersonalization message ignored");
+                       return;
+               }
+
+               break;
+       case SMS_PID_TYPE_USIM_DOWNLOAD:
+       case SMS_PID_TYPE_ANSI136:
+               /* If not Class 2, handle in a "normal" way */
+               if (cls != SMS_CLASS_2)
+                       break;
+
+               sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+               if (sim == NULL)
+                       return;
+
+               if (!__ofono_sim_service_available(sim,
+                                       SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_PP,
+                                       SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_PP))
+                       return;
+
+               stk = __ofono_atom_find(OFONO_ATOM_TYPE_STK, modem);
+               if (stk == NULL)
+                       return;
+
+               __ofono_sms_sim_download(stk, &s, NULL, sms);
+
+               /*
+                * Passing the USIM response back to network is not
+                * currently supported
+                *
+                * TODO: store in EFsms if not handled
+                */
+               return;
+       default:
+               break;
+       }
+
+       /*
+        * Check to see if the SMS has any other MWI related headers,
+        * as sometimes they are "tacked on" by the SMSC.
+        * While we're doing this we also check for messages containing
+        * WCMP headers or headers that can't possibly be in a normal
+        * message.  If we find messages like that, we ignore them.
+        */
+       if (s.deliver.udhi) {
+               struct sms_udh_iter iter;
+               enum sms_iei iei;
+
+               if (!sms_udh_iter_init(&s, &iter))
+                       goto out;
+
+               while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
+                               SMS_IEI_INVALID) {
+                       if (iei > 0x25) {
+                               ofono_error("Reserved / Unknown / USAT"
+                                               "header in use, ignore");
+                               return;
+                       }
+
+                       switch (iei) {
+                       case SMS_IEI_SPECIAL_MESSAGE_INDICATION:
+                       case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION:
+                               /*
+                                * TODO: ignore if not in the very first
+                                * segment of a concatenated SM so as not
+                                * to repeat the indication.
+                                */
+                               if (handle_mwi(sms, &s))
+                                       return;
+
+                               goto out;
+                       case SMS_IEI_WCMP:
+                               ofono_error("No support for WCMP, ignoring");
+                               return;
+                       default:
+                               sms_udh_iter_next(&iter);
+                       }
+               }
+       }
+
+out:
+       handle_deliver(sms, &s);
+}
+
+void ofono_sms_status_notify(struct ofono_sms *sms, unsigned char *pdu,
+                               int len, int tpdu_len)
+{
+       struct sms s;
+       enum sms_class cls;
+
+       DBG("len %d tpdu len %d", len, tpdu_len);
+
+       if (!sms_decode(pdu, len, FALSE, tpdu_len, &s)) {
+               ofono_error("Unable to decode PDU");
+               return;
+       }
+
+       if (s.type != SMS_TYPE_STATUS_REPORT) {
+               ofono_error("Expecting a STATUS REPORT pdu");
+               return;
+       }
+
+       if (s.status_report.srq) {
+               ofono_error("Waiting an answer to SMS-SUBMIT, not SMS-COMMAND");
+               return;
+       }
+
+       if (!sms_dcs_decode(s.status_report.dcs, &cls, NULL, NULL, NULL)) {
+               ofono_error("Unknown / Reserved DCS.  Ignoring");
+               return;
+       }
+
+       handle_sms_status_report(sms, &s);
+}
+
+int ofono_sms_driver_register(const struct ofono_sms_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_sms_driver_unregister(const struct ofono_sms_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void sms_unregister(struct ofono_atom *atom)
+{
+       struct ofono_sms *sms = __ofono_atom_get_data(atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_MESSAGE_MANAGER_INTERFACE);
+       ofono_modem_remove_interface(modem, OFONO_MESSAGE_MANAGER_INTERFACE);
+
+       if (sms->mw_watch) {
+               __ofono_modem_remove_atom_watch(modem, sms->mw_watch);
+               sms->mw_watch = 0;
+               sms->mw = NULL;
+       }
+
+       if (sms->status_watch) {
+               __ofono_netreg_remove_status_watch(sms->netreg,
+                                                       sms->status_watch);
+               sms->status_watch = 0;
+       }
+
+       if (sms->netreg_watch) {
+               __ofono_modem_remove_atom_watch(modem, sms->netreg_watch);
+               sms->netreg_watch = 0;
+       }
+
+       sms->netreg = NULL;
+
+       if (sms->messages) {
+               GHashTableIter iter;
+               struct message *m;
+               gpointer key, value;
+
+               g_hash_table_iter_init(&iter, sms->messages);
+
+               while (g_hash_table_iter_next(&iter, &key, &value)) {
+                       m = value;
+                       message_dbus_unregister(m);
+               }
+
+               g_hash_table_destroy(sms->messages);
+               sms->messages = NULL;
+       }
+
+       __ofono_watchlist_free(sms->text_handlers);
+       sms->text_handlers = NULL;
+
+       __ofono_watchlist_free(sms->datagram_handlers);
+       sms->datagram_handlers = NULL;
+}
+
+static void sms_remove(struct ofono_atom *atom)
+{
+       struct ofono_sms *sms = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (sms == NULL)
+               return;
+
+       if (sms->driver && sms->driver->remove)
+               sms->driver->remove(sms);
+
+       if (sms->tx_source) {
+               g_source_remove(sms->tx_source);
+               sms->tx_source = 0;
+       }
+
+       if (sms->assembly) {
+               sms_assembly_free(sms->assembly);
+               sms->assembly = NULL;
+       }
+
+       if (sms->txq) {
+               g_queue_foreach(sms->txq, tx_queue_entry_destroy_foreach, NULL);
+               g_queue_free(sms->txq);
+               sms->txq = NULL;
+       }
+
+       if (sms->settings) {
+               g_key_file_set_integer(sms->settings, SETTINGS_GROUP,
+                                       "NextReference", sms->ref);
+               g_key_file_set_boolean(sms->settings, SETTINGS_GROUP,
+                                       "UseDeliveryReports",
+                                       sms->use_delivery_reports);
+               g_key_file_set_integer(sms->settings, SETTINGS_GROUP,
+                                       "Bearer", sms->bearer);
+               g_key_file_set_integer(sms->settings, SETTINGS_GROUP,
+                                       "Alphabet", sms->alphabet);
+
+               storage_close(sms->imsi, SETTINGS_STORE, sms->settings, TRUE);
+
+               g_free(sms->imsi);
+               sms->imsi = NULL;
+               sms->settings = NULL;
+       }
+
+       if (sms->sr_assembly) {
+               status_report_assembly_free(sms->sr_assembly);
+               sms->sr_assembly = NULL;
+       }
+
+       g_free(sms);
+}
+
+
+/*
+ * Create a SMS driver
+ *
+ * This creates a SMS driver that is hung off a @modem
+ * object. However, for the driver to be used by the system, it has to
+ * be registered with the oFono core using ofono_sms_register().
+ *
+ * This is done once the modem driver determines that SMS is properly
+ * supported by the hardware.
+ */
+struct ofono_sms *ofono_sms_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_sms *sms;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       sms = g_try_new0(struct ofono_sms, 1);
+
+       if (sms == NULL)
+               return NULL;
+
+       sms->sca.type = 129;
+       sms->ref = 1;
+       sms->txq = g_queue_new();
+       sms->messages = g_hash_table_new(uuid_hash, uuid_equal);
+
+       sms->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SMS,
+                                               sms_remove, sms);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_sms_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(sms, vendor, data) < 0)
+                       continue;
+
+               sms->driver = drv;
+               break;
+       }
+
+       return sms;
+}
+
+static void mw_watch(struct ofono_atom *atom,
+                       enum ofono_atom_watch_condition cond, void *data)
+{
+       struct ofono_sms *sms = data;
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               sms->mw = NULL;
+               return;
+       }
+
+       sms->mw = __ofono_atom_get_data(atom);
+}
+
+static void sms_load_settings(struct ofono_sms *sms, const char *imsi)
+{
+       GError *error;
+
+       sms->settings = storage_open(imsi, SETTINGS_STORE);
+
+       if (sms->settings == NULL)
+               return;
+
+       sms->imsi = g_strdup(imsi);
+
+       error = NULL;
+       sms->ref = g_key_file_get_integer(sms->settings, SETTINGS_GROUP,
+                                               "NextReference", &error);
+
+       if (error || sms->ref > 65536) {
+               g_error_free(error);
+               sms->ref = 1;
+               g_key_file_set_integer(sms->settings, SETTINGS_GROUP,
+                                       "NextReference", sms->ref);
+       }
+
+       error = NULL;
+       sms->use_delivery_reports =
+               g_key_file_get_boolean(sms->settings, SETTINGS_GROUP,
+                                       "UseDeliveryReports", &error);
+
+       if (error) {
+               g_error_free(error);
+               g_key_file_set_boolean(sms->settings, SETTINGS_GROUP,
+                                       "UseDeliveryReports",
+                                       sms->use_delivery_reports);
+       }
+
+       error = NULL;
+       sms->bearer = g_key_file_get_integer(sms->settings, SETTINGS_GROUP,
+                                                       "Bearer", &error);
+
+       if (error || sms_bearer_to_string(sms->bearer) == NULL) {
+               g_error_free(error);
+               sms->bearer = 3; /* Default to CS then PS */
+               g_key_file_set_integer(sms->settings, SETTINGS_GROUP,
+                                       "Bearer", sms->bearer);
+       }
+
+       error = NULL;
+       sms->alphabet = g_key_file_get_integer(sms->settings, SETTINGS_GROUP,
+                                               "Alphabet", &error);
+
+       if (error || sms_alphabet_to_string(sms->alphabet) == NULL) {
+               g_error_free(error);
+               sms->alphabet = SMS_ALPHABET_DEFAULT;
+               g_key_file_set_integer(sms->settings, SETTINGS_GROUP,
+                                       "Alphabet", sms->alphabet);
+       }
+}
+
+static void bearer_init_callback(const struct ofono_error *error, void *data)
+{
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               ofono_error("Error bootstrapping SMS Bearer Preference");
+}
+
+static void sms_restore_tx_queue(struct ofono_sms *sms)
+{
+       GQueue *backupq;
+       struct txq_backup_entry *backup_entry;
+
+       DBG("");
+
+       backupq = sms_tx_queue_load(sms->imsi);
+
+       if (backupq == NULL)
+               return;
+
+       while ((backup_entry = g_queue_pop_head(backupq))) {
+               struct message *m;
+               struct tx_queue_entry *txq_entry;
+
+               backup_entry->flags |= OFONO_SMS_SUBMIT_FLAG_REUSE_UUID;
+               txq_entry = tx_queue_entry_new(backup_entry->msg_list,
+                                                       backup_entry->flags);
+               if (txq_entry == NULL)
+                       goto loop_out;
+
+               txq_entry->flags &= ~OFONO_SMS_SUBMIT_FLAG_REUSE_UUID;
+               memcpy(&txq_entry->uuid.uuid, &backup_entry->uuid,
+                                                               SMS_MSGID_LEN);
+
+               m = message_create(&txq_entry->uuid, sms->atom);
+               if (m == NULL) {
+                       tx_queue_entry_destroy(txq_entry);
+
+                       goto loop_out;
+               }
+
+               if (message_dbus_register(m) == FALSE) {
+                       tx_queue_entry_destroy(txq_entry);
+
+                       goto loop_out;
+               }
+
+               message_set_data(m, txq_entry);
+               g_hash_table_insert(sms->messages, &txq_entry->uuid, m);
+
+               txq_entry->id = sms->tx_counter++;
+               g_queue_push_tail(sms->txq, txq_entry);
+
+loop_out:
+               g_slist_foreach(backup_entry->msg_list, (GFunc)g_free, NULL);
+               g_slist_free(backup_entry->msg_list);
+               g_free(backup_entry);
+       }
+
+       if (g_queue_get_length(sms->txq) > 0)
+               sms->tx_source = g_timeout_add(0, tx_next, sms);
+
+       g_queue_free(backupq);
+}
+
+/*
+ * Indicate oFono that a SMS driver is ready for operation
+ *
+ * This is called after ofono_sms_create() was done and the modem
+ * driver determined that a modem supports SMS correctly. Once this
+ * call succeeds, the D-BUS interface for SMS goes live.
+ */
+void ofono_sms_register(struct ofono_sms *sms)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom);
+       const char *path = __ofono_atom_get_path(sms->atom);
+       struct ofono_sim *sim;
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_MESSAGE_MANAGER_INTERFACE,
+                                       sms_manager_methods,
+                                       sms_manager_signals,
+                                       NULL, sms, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_MESSAGE_MANAGER_INTERFACE);
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_MESSAGE_MANAGER_INTERFACE);
+
+       sms->mw_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_MESSAGE_WAITING,
+                                       mw_watch, sms, NULL);
+
+       sms->netreg_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_NETREG,
+                                       netreg_watch, sms, NULL);
+
+       sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+
+       /*
+        * If we have a sim atom, we can uniquely identify the SIM,
+        * otherwise create an sms assembly which doesn't backup the fragment
+        * store.
+        */
+       if (sim) {
+               const char *imsi;
+
+               imsi = ofono_sim_get_imsi(sim);
+               sms->assembly = sms_assembly_new(imsi);
+
+               sms->sr_assembly = status_report_assembly_new(imsi);
+
+               sms_load_settings(sms, imsi);
+       } else {
+               sms->assembly = sms_assembly_new(NULL);
+               sms->sr_assembly = status_report_assembly_new(NULL);
+               sms->bearer = 3; /* Default to CS then PS */
+       }
+
+       if (sms->driver->bearer_set)
+               sms->driver->bearer_set(sms, sms->bearer,
+                                               bearer_init_callback, sms);
+
+       sms_restore_tx_queue(sms);
+
+       sms->text_handlers = __ofono_watchlist_new(g_free);
+       sms->datagram_handlers = __ofono_watchlist_new(g_free);
+
+       __ofono_atom_register(sms->atom, sms_unregister);
+}
+
+void ofono_sms_remove(struct ofono_sms *sms)
+{
+       __ofono_atom_free(sms->atom);
+}
+
+void ofono_sms_set_data(struct ofono_sms *sms, void *data)
+{
+       sms->driver_data = data;
+}
+
+void *ofono_sms_get_data(struct ofono_sms *sms)
+{
+       return sms->driver_data;
+}
+
+unsigned short __ofono_sms_get_next_ref(struct ofono_sms *sms)
+{
+       return sms->ref;
+}
+
+int __ofono_sms_txq_submit(struct ofono_sms *sms, GSList *list,
+                               unsigned int flags,
+                               struct ofono_uuid *uuid,
+                               ofono_sms_txq_queued_cb_t cb, void *data)
+{
+       struct message *m = NULL;
+       struct tx_queue_entry *entry;
+
+       entry = tx_queue_entry_new(list, flags);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       if (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) {
+               m = message_create(&entry->uuid, sms->atom);
+               if (m == NULL)
+                       goto err;
+
+               if (message_dbus_register(m) == FALSE)
+                       goto err;
+
+               message_set_data(m, entry);
+
+               g_hash_table_insert(sms->messages, &entry->uuid, m);
+       }
+
+       if (list->next != NULL) {
+               if (sms->ref == 65536)
+                       sms->ref = 1;
+               else
+                       sms->ref = sms->ref + 1;
+       }
+
+       entry->id = sms->tx_counter++;
+
+       g_queue_push_tail(sms->txq, entry);
+
+       if (sms->registered && g_queue_get_length(sms->txq) == 1)
+               sms->tx_source = g_timeout_add(0, tx_next, sms);
+
+       if (uuid)
+               memcpy(uuid, &entry->uuid, sizeof(*uuid));
+
+       if (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) {
+               const char *uuid_str;
+               unsigned char i;
+
+               uuid_str = ofono_uuid_to_str(&entry->uuid);
+
+               for (i = 0; i < entry->num_pdus; i++) {
+                       struct pending_pdu *pdu;
+
+                       pdu = &entry->pdus[i];
+
+                       sms_tx_backup_store(sms->imsi, entry->id, entry->flags,
+                                               uuid_str, i, pdu->pdu,
+                                               pdu->pdu_len, pdu->tpdu_len);
+               }
+       }
+
+       if (cb)
+               cb(sms, &entry->uuid, data);
+
+       if (m && (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS))
+               message_emit_added(m, OFONO_MESSAGE_MANAGER_INTERFACE);
+
+       return 0;
+
+err:
+       tx_queue_entry_destroy(entry);
+
+       return -EINVAL;
+}
+
+int __ofono_sms_txq_set_submit_notify(struct ofono_sms *sms,
+                                       struct ofono_uuid *uuid,
+                                       ofono_sms_txq_submit_cb_t cb,
+                                       void *data,
+                                       ofono_destroy_func destroy)
+{
+       struct message *m;
+       struct tx_queue_entry *entry;
+
+       m = g_hash_table_lookup(sms->messages, uuid);
+       if (m == NULL)
+               return -ENOENT;
+
+       entry = message_get_data(m);
+       if (entry == NULL)
+               return -ENOTSUP;
+
+       tx_queue_entry_set_submit_notify(entry, cb, data, destroy);
+
+       return 0;
+}
diff --git a/src/smsagent.c b/src/smsagent.c
new file mode 100644 (file)
index 0000000..68ca54e
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "smsagent.h"
+
+struct sms_agent {
+       char *interface;
+       char *path;
+       char *service;
+       guint disconnect_watch;
+       ofono_destroy_func removed_cb;
+       void *removed_data;
+       GSList *reqs;
+};
+
+struct sms_agent_request {
+       struct sms_agent *agent;
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       sms_agent_dispatch_cb dispatch_cb;
+       void *dispatch_data;
+       ofono_destroy_func destroy;
+};
+
+static struct sms_agent_request *sms_agent_request_new(struct sms_agent *agent,
+                                               sms_agent_dispatch_cb cb,
+                                               void *user_data,
+                                               ofono_destroy_func destroy)
+{
+       struct sms_agent_request *req;
+
+       req = g_try_new0(struct sms_agent_request, 1);
+       if (req == NULL)
+               return NULL;
+
+       req->agent = agent;
+       req->dispatch_cb = cb;
+       req->dispatch_data = user_data;
+       req->destroy = destroy;
+
+       return req;
+}
+
+static void sms_agent_request_free(struct sms_agent_request *req)
+{
+       if (req->msg) {
+               dbus_message_unref(req->msg);
+               req->msg = NULL;
+       }
+
+       if (req->call) {
+               dbus_pending_call_unref(req->call);
+               req->call = NULL;
+       }
+
+       if (req->destroy)
+               req->destroy(req->dispatch_data);
+
+       g_free(req);
+}
+
+static void sms_agent_send_noreply(struct sms_agent *agent, const char *method)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *message;
+
+       message = dbus_message_new_method_call(agent->service, agent->path,
+                                               agent->interface, method);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       DBG("Sending: '%s.%s' to '%s' at '%s'", agent->interface, method,
+                       agent->service, agent->path);
+
+       g_dbus_send_message(conn, message);
+}
+
+static inline void sms_agent_send_release(struct sms_agent *agent)
+{
+       sms_agent_send_noreply(agent, "Release");
+}
+
+static void sms_agent_disconnect_cb(DBusConnection *conn, void *data)
+{
+       struct sms_agent *agent = data;
+
+       agent->disconnect_watch = 0;
+
+       sms_agent_free(agent);
+}
+
+struct sms_agent *sms_agent_new(const char *interface,
+                               const char *service, const char *path)
+{
+       struct sms_agent *agent = g_try_new0(struct sms_agent, 1);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (agent == NULL)
+               return NULL;
+
+       agent->interface = g_strdup(interface);
+       agent->service = g_strdup(service);
+       agent->path = g_strdup(path);
+
+       agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, service,
+                                                       sms_agent_disconnect_cb,
+                                                       agent, NULL);
+
+       return agent;
+}
+
+void sms_agent_set_removed_notify(struct sms_agent *agent,
+                                       ofono_destroy_func destroy,
+                                       void *user_data)
+{
+       agent->removed_cb = destroy;
+       agent->removed_data = user_data;
+}
+
+static void sms_agent_request_cancel(gpointer element, gpointer userdata)
+{
+       struct sms_agent_request *req = element;
+
+       dbus_pending_call_cancel(req->call);
+       sms_agent_request_free(req);
+}
+
+void sms_agent_free(struct sms_agent *agent)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (agent == NULL)
+               return;
+
+       if (agent->disconnect_watch) {
+               sms_agent_send_release(agent);
+
+               g_dbus_remove_watch(conn, agent->disconnect_watch);
+               agent->disconnect_watch = 0;
+       }
+
+       if (agent->removed_cb)
+               agent->removed_cb(agent->removed_data);
+
+       g_slist_foreach(agent->reqs, sms_agent_request_cancel, NULL);
+       g_slist_free(agent->reqs);
+
+       g_free(agent->path);
+       g_free(agent->service);
+       g_free(agent->interface);
+       g_free(agent);
+}
+
+ofono_bool_t sms_agent_matches(struct sms_agent *agent, const char *service,
+                               const char *path)
+{
+       if (path == NULL || service == NULL)
+               return FALSE;
+
+       return g_str_equal(agent->path, path) &&
+                       g_str_equal(agent->service, service);
+}
+
+static int check_error(struct sms_agent *agent, DBusMessage *reply,
+                               enum sms_agent_result *out_result)
+{
+       DBusError err;
+       int result = 0;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply) == FALSE) {
+               *out_result = SMS_AGENT_RESULT_OK;
+               return 0;
+       }
+
+       DBG("SmsAgent %s replied with error %s, %s",
+                       agent->path, err.name, err.message);
+
+       /* Timeout is always valid */
+       if (g_str_equal(err.name, DBUS_ERROR_NO_REPLY)) {
+               *out_result = SMS_AGENT_RESULT_TIMEOUT;
+               goto out;
+       }
+
+       result = -EINVAL;
+
+out:
+       dbus_error_free(&err);
+       return result;
+}
+
+static void sms_agent_dispatch_reply_cb(DBusPendingCall *call, void *data)
+{
+       struct sms_agent_request *req = data;
+       struct sms_agent *agent = req->agent;
+       sms_agent_dispatch_cb cb = req->dispatch_cb;
+       void *dispatch_data = req->dispatch_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(req->call);
+       enum sms_agent_result result;
+
+       if (check_error(agent, reply, &result) == -EINVAL) {
+               dbus_message_unref(reply);
+               sms_agent_free(agent);
+               return;
+       }
+
+       agent->reqs = g_slist_remove(agent->reqs, req);
+       sms_agent_request_free(req);
+
+       if (cb)
+               cb(agent, result, dispatch_data);
+
+       dbus_message_unref(reply);
+}
+
+int sms_agent_dispatch_datagram(struct sms_agent *agent, const char *method,
+                               const char *from,
+                               const struct tm *remote_sent_time,
+                               const struct tm *local_sent_time,
+                               const unsigned char *content, unsigned int len,
+                               sms_agent_dispatch_cb cb, void *user_data,
+                               ofono_destroy_func destroy)
+{
+       struct sms_agent_request *req;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessageIter array;
+       char buf[128];
+       const char *str = buf;
+
+       req = sms_agent_request_new(agent, cb, user_data, destroy);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->msg = dbus_message_new_method_call(agent->service, agent->path,
+                                               agent->interface, method);
+       if (req->msg == NULL) {
+               sms_agent_request_free(req);
+               return -ENOMEM;
+       }
+
+       dbus_message_iter_init_append(req->msg, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_BYTE_AS_STRING, &array);
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                               &content, len);
+       dbus_message_iter_close_container(&iter, &array);
+
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", local_sent_time);
+       buf[127] = '\0';
+       ofono_dbus_dict_append(&dict, "LocalSentTime", DBUS_TYPE_STRING, &str);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", remote_sent_time);
+       buf[127] = '\0';
+       ofono_dbus_dict_append(&dict, "SentTime", DBUS_TYPE_STRING, &str);
+
+       ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &from);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       if (!dbus_connection_send_with_reply(conn, req->msg, &req->call, -1)) {
+               ofono_error("Sending D-Bus method failed");
+               sms_agent_request_free(req);
+               return -EIO;
+       }
+
+       agent->reqs = g_slist_append(agent->reqs, req);
+
+       dbus_pending_call_set_notify(req->call, sms_agent_dispatch_reply_cb,
+                                       req, NULL);
+
+       return 0;
+}
diff --git a/src/smsagent.h b/src/smsagent.h
new file mode 100644 (file)
index 0000000..5873975
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct sms_agent;
+
+enum sms_agent_result {
+       SMS_AGENT_RESULT_OK = 0,
+       SMS_AGENT_RESULT_FAILED,
+       SMS_AGENT_RESULT_TIMEOUT,
+};
+
+typedef void (*sms_agent_dispatch_cb)(struct sms_agent *agent,
+                                       enum sms_agent_result result,
+                                       void *data);
+
+struct sms_agent *sms_agent_new(const char *interface,
+                                       const char *service, const char *path);
+
+void sms_agent_set_removed_notify(struct sms_agent *agent,
+                                       ofono_destroy_func destroy,
+                                       void *user_data);
+
+ofono_bool_t sms_agent_matches(struct sms_agent *agent, const char *service,
+                               const char *path);
+
+void sms_agent_free(struct sms_agent *agent);
+
+int sms_agent_dispatch_datagram(struct sms_agent *agent, const char *method,
+                               const char *from,
+                               const struct tm *remote_sent_time,
+                               const struct tm *local_sent_time,
+                               const unsigned char *content, unsigned int len,
+                               sms_agent_dispatch_cb cb, void *user_data,
+                               ofono_destroy_func destroy);
diff --git a/src/smsutil.c b/src/smsutil.c
new file mode 100644 (file)
index 0000000..a541964
--- /dev/null
@@ -0,0 +1,4741 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "util.h"
+#include "storage.h"
+#include "smsutil.h"
+
+#define uninitialized_var(x) x = x
+
+#define SMS_BACKUP_MODE 0600
+#define SMS_BACKUP_PATH STORAGEDIR "/%s/sms_assembly"
+#define SMS_BACKUP_PATH_DIR SMS_BACKUP_PATH "/%s-%i-%i"
+#define SMS_BACKUP_PATH_FILE SMS_BACKUP_PATH_DIR "/%03i"
+
+#define SMS_SR_BACKUP_PATH STORAGEDIR "/%s/sms_sr"
+#define SMS_SR_BACKUP_PATH_FILE SMS_SR_BACKUP_PATH "/%s-%s"
+
+#define SMS_TX_BACKUP_PATH STORAGEDIR "/%s/tx_queue"
+#define SMS_TX_BACKUP_PATH_DIR SMS_TX_BACKUP_PATH "/%lu-%lu-%s"
+#define SMS_TX_BACKUP_PATH_FILE SMS_TX_BACKUP_PATH_DIR "/%03i"
+
+#define SMS_ADDR_FMT "%24[0-9A-F]"
+#define SMS_MSGID_FMT "%40[0-9A-F]"
+
+/*
+ * Time zone accounts for daylight saving time, and the two extreme time
+ * zones on earth are UTC-12 and UTC+14.
+ */
+#define MAX_TIMEZONE 56
+#define MIN_TIMEZONE -48
+
+static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly,
+                                       const struct sms *sms, time_t ts,
+                                       const struct sms_address *addr,
+                                       guint16 ref, guint8 max, guint8 seq,
+                                       gboolean backup);
+
+/*
+ * This function uses the meanings of digits 10..15 according to the rules
+ * defined in 23.040 Section 9.1.2.3 and 24.008 Table 10.5.118
+ */
+void extract_bcd_number(const unsigned char *buf, int len, char *out)
+{
+       static const char digit_lut[] = "0123456789*#abc\0";
+       unsigned char oct;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               oct = buf[i];
+
+               out[i*2] = digit_lut[oct & 0x0f];
+               out[i*2+1] = digit_lut[(oct & 0xf0) >> 4];
+       }
+
+       out[i*2] = '\0';
+}
+
+static inline int to_semi_oct(char in)
+{
+       int digit;
+
+       switch (in) {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+               digit = in - '0';
+               break;
+       case '*':
+               digit = 10;
+               break;
+       case '#':
+               digit = 11;
+               break;
+       case 'A':
+       case 'a':
+               digit = 12;
+               break;
+       case 'B':
+       case 'b':
+               digit = 13;
+               break;
+       case 'C':
+       case 'c':
+               digit = 14;
+               break;
+       default:
+               digit = -1;
+               break;
+       }
+
+       return digit;
+}
+
+void encode_bcd_number(const char *number, unsigned char *out)
+{
+       while (number[0] != '\0' && number[1] != '\0') {
+               *out = to_semi_oct(*number++);
+               *out++ |= to_semi_oct(*number++) << 4;
+       }
+
+       if (*number)
+               *out = to_semi_oct(*number) | 0xf0;
+}
+
+/*
+ * Returns whether the DCS could be parsed successfully, e.g. no reserved
+ * values were used
+ */
+gboolean sms_dcs_decode(guint8 dcs, enum sms_class *cls,
+                       enum sms_charset *charset,
+                       gboolean *compressed, gboolean *autodelete)
+{
+       guint8 upper = (dcs & 0xf0) >> 4;
+       enum sms_charset ch;
+       enum sms_class cl;
+       gboolean comp;
+       gboolean autodel;
+
+       /* MWI DCS types are handled in sms_mwi_dcs_decode */
+       if (upper >= 0x8 && upper <= 0xE)
+               return FALSE;
+
+       upper = (dcs & 0xc0) >> 6;
+
+       switch (upper) {
+       case 0:
+       case 1:
+               autodel = upper;
+               comp = (dcs & 0x20) ? TRUE : FALSE;
+
+               if (dcs & 0x10)
+                       cl = (enum sms_class) (dcs & 0x03);
+               else
+                       cl = SMS_CLASS_UNSPECIFIED;
+
+               if (((dcs & 0x0c) >> 2) < 3)
+                       ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
+               else
+                       return FALSE;
+
+               break;
+       case 3:
+               comp = FALSE;
+               autodel = FALSE;
+
+               if (dcs & 0x4)
+                       ch = SMS_CHARSET_8BIT;
+               else
+                       ch = SMS_CHARSET_7BIT;
+
+               cl = (enum sms_class) (dcs & 0x03);
+
+               break;
+       default:
+               return FALSE;
+       };
+
+       if (compressed)
+               *compressed = comp;
+
+       if (autodelete)
+               *autodelete = autodel;
+
+       if (cls)
+               *cls = cl;
+
+       if (charset)
+               *charset = ch;
+
+       return TRUE;
+}
+
+gboolean sms_mwi_dcs_decode(guint8 dcs, enum sms_mwi_type *type,
+                               enum sms_charset *charset,
+                               gboolean *active, gboolean *discard)
+{
+       guint8 upper = (dcs & 0xf0) >> 4;
+       enum sms_mwi_type t;
+       enum sms_charset ch;
+       gboolean dis;
+       gboolean act;
+
+       if (upper < 0xC || upper > 0xE)
+               return FALSE;
+
+       upper = (dcs & 0x30) >> 4;
+
+       if (upper == 0)
+               dis = TRUE;
+       else
+               dis = FALSE;
+
+       /*
+        * As per 3GPP TS 23.038 specification, if bits 7..4 set to 1110,
+        * text included in the user data is coded in the uncompresssed
+        * UCS2 character set.
+        */
+       if (upper == 2)
+               ch = SMS_CHARSET_UCS2;
+       else
+               ch = SMS_CHARSET_7BIT;
+
+       act = dcs & 0x8;
+
+       t = (enum sms_mwi_type) (dcs & 0x3);
+
+       if (type)
+               *type = t;
+
+       if (charset)
+               *charset = ch;
+
+       if (active)
+               *active = act;
+
+       if (discard)
+               *discard = dis;
+
+       return TRUE;
+}
+
+int sms_udl_in_bytes(guint8 ud_len, guint8 dcs)
+{
+       int len_7bit = (ud_len + 1) * 7 / 8;
+       int len_8bit = ud_len;
+       guint8 upper;
+
+       if (dcs == 0)
+               return len_7bit;
+
+       upper = (dcs & 0xc0) >> 6;
+
+       switch (upper) {
+       case 0:
+       case 1:
+               if (dcs & 0x20) /* compressed */
+                       return len_8bit;
+
+               switch ((dcs & 0x0c) >> 2) {
+               case 0:
+                       return len_7bit;
+               case 1:
+                       return len_8bit;
+               case 2:
+                       return len_8bit;
+               }
+
+               return 0;
+       case 2:
+               return 0;
+       case 3:
+               switch ((dcs & 0x30) >> 4) {
+               case 0:
+               case 1:
+                       return len_7bit;
+               case 2:
+                       return len_8bit;
+               case 3:
+                       if (dcs & 0x4)
+                               return len_8bit;
+                       else
+                               return len_7bit;
+               }
+
+               break;
+       default:
+               break;
+       };
+
+       return 0;
+}
+
+static inline gboolean next_octet(const unsigned char *pdu, int len,
+                                       int *offset, unsigned char *oct)
+{
+       if (len == *offset)
+               return FALSE;
+
+       *oct = pdu[*offset];
+
+       *offset = *offset + 1;
+
+       return TRUE;
+}
+
+static inline gboolean set_octet(unsigned char *pdu, int *offset,
+                                       unsigned char oct)
+{
+       pdu[*offset] = oct;
+       *offset = *offset + 1;
+
+       return TRUE;
+}
+
+gboolean sms_encode_scts(const struct sms_scts *in, unsigned char *pdu,
+                               int *offset)
+{
+       guint timezone;
+
+       if (in->year > 99)
+               return FALSE;
+
+       if (in->month > 12)
+               return FALSE;
+
+       if (in->day > 31)
+               return FALSE;
+
+       if (in->hour > 23)
+               return FALSE;
+
+       if (in->minute > 59)
+               return FALSE;
+
+       if (in->second > 59)
+               return FALSE;
+
+       if ((in->timezone > MAX_TIMEZONE || in->timezone < MIN_TIMEZONE) &&
+                       in->has_timezone == TRUE)
+               return FALSE;
+
+       pdu = pdu + *offset;
+
+       pdu[0] = ((in->year / 10) & 0x0f) | (((in->year % 10) & 0x0f) << 4);
+       pdu[1] = ((in->month / 10) & 0x0f) | (((in->month % 10) & 0x0f) << 4);
+       pdu[2] = ((in->day / 10) & 0x0f) | (((in->day % 10) & 0x0f) << 4);
+       pdu[3] = ((in->hour / 10) & 0x0f) | (((in->hour % 10) & 0x0f) << 4);
+       pdu[4] = ((in->minute / 10) & 0x0f) | (((in->minute % 10) & 0x0f) << 4);
+       pdu[5] = ((in->second / 10) & 0x0f) | (((in->second % 10) & 0x0f) << 4);
+
+       if (in->has_timezone == FALSE) {
+               pdu[6] = 0xff;
+               goto out;
+       }
+
+       timezone = abs(in->timezone);
+
+       pdu[6] = ((timezone / 10) & 0x07) | (((timezone % 10) & 0x0f) << 4);
+
+       if (in->timezone < 0)
+               pdu[6] |= 0x8;
+
+out:
+       *offset += 7;
+
+       return TRUE;
+}
+
+guint8 sms_decode_semi_octet(guint8 in)
+{
+       return (in & 0x0f) * 10 + (in >> 4);
+}
+
+gboolean sms_decode_scts(const unsigned char *pdu, int len,
+                               int *offset, struct sms_scts *out)
+{
+       unsigned char oct = 0;
+
+       if ((len - *offset) < 7)
+               return FALSE;
+
+       next_octet(pdu, len, offset, &oct);
+       out->year = sms_decode_semi_octet(oct);
+
+       if (out->year > 99)
+               return FALSE;
+
+       next_octet(pdu, len, offset, &oct);
+       out->month = sms_decode_semi_octet(oct);
+
+       if (out->month > 12)
+               return FALSE;
+
+       next_octet(pdu, len, offset, &oct);
+       out->day = sms_decode_semi_octet(oct);
+
+       if (out->day > 31)
+               return FALSE;
+
+       next_octet(pdu, len, offset, &oct);
+       out->hour = sms_decode_semi_octet(oct);
+
+       if (out->hour > 23)
+               return FALSE;
+
+       next_octet(pdu, len, offset, &oct);
+       out->minute = sms_decode_semi_octet(oct);
+
+       if (out->minute > 59)
+               return FALSE;
+
+       next_octet(pdu, len, offset, &oct);
+       out->second = sms_decode_semi_octet(oct);
+
+       if (out->second > 59)
+               return FALSE;
+
+       next_octet(pdu, len, offset, &oct);
+
+       /*
+        * Time Zone indicates the difference, expressed in quarters
+        * of an hour, between the local time and GMT. In the first of the two
+        * semi‑octets, the first bit (bit 3 of the seventh octet of the
+        * TP‑Service‑Centre‑Time‑Stamp field) represents the algebraic
+        * sign of this difference (0: positive, 1: negative).
+        */
+       out->timezone = (oct & 0x07) * 10 + ((oct & 0xf0) >> 4);
+
+       if (oct & 0x08)
+               out->timezone = out->timezone * -1;
+
+       if ((out->timezone > MAX_TIMEZONE) || (out->timezone < MIN_TIMEZONE))
+               return FALSE;
+
+       out->has_timezone = TRUE;
+
+       return TRUE;
+}
+
+static gboolean decode_validity_period(const unsigned char *pdu, int len,
+                                       int *offset,
+                                       enum sms_validity_period_format vpf,
+                                       struct sms_validity_period *vp)
+{
+       switch (vpf) {
+       case SMS_VALIDITY_PERIOD_FORMAT_ABSENT:
+               return TRUE;
+       case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE:
+               if (!next_octet(pdu, len, offset, &vp->relative))
+                       return FALSE;
+
+               return TRUE;
+       case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE:
+               if (!sms_decode_scts(pdu, len, offset, &vp->absolute))
+                       return FALSE;
+
+               return TRUE;
+       case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED:
+               /*
+                * TODO: Parse out enhanced structure properly
+                * 23.040 Section 9.2.3.12.3
+                */
+               if ((len - *offset) < 7)
+                       return FALSE;
+
+               memcpy(vp->enhanced, pdu + *offset, 7);
+
+               *offset = *offset + 7;
+
+               return TRUE;
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+static gboolean encode_validity_period(const struct sms_validity_period *vp,
+                                       enum sms_validity_period_format vpf,
+                                       unsigned char *pdu, int *offset)
+{
+       switch (vpf) {
+       case SMS_VALIDITY_PERIOD_FORMAT_ABSENT:
+               return TRUE;
+       case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE:
+               set_octet(pdu, offset, vp->relative);
+               return TRUE;
+       case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE:
+               return sms_encode_scts(&vp->absolute, pdu, offset);
+       case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED:
+               /* TODO: Write out proper enhanced VP structure */
+               memcpy(pdu + *offset, vp->enhanced, 7);
+
+               *offset = *offset + 7;
+
+               return TRUE;
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+gboolean sms_encode_address_field(const struct sms_address *in, gboolean sc,
+                                       unsigned char *pdu, int *offset)
+{
+       size_t len = strlen(in->address);
+       unsigned char addr_len = 0;
+       unsigned char p[10];
+
+       pdu = pdu + *offset;
+
+       if (len == 0 && sc) {
+               pdu[0] = 0;
+               *offset = *offset + 1;
+
+               return TRUE;
+       }
+
+       if (len == 0)
+               goto out;
+
+       if (in->number_type == SMS_NUMBER_TYPE_ALPHANUMERIC) {
+               long written;
+               long packed;
+               unsigned char *gsm;
+               unsigned char *r;
+
+               if (len > 11)
+                       return FALSE;
+
+               gsm = convert_utf8_to_gsm(in->address, len, NULL, &written, 0);
+               if (gsm == NULL)
+                       return FALSE;
+
+               r = pack_7bit_own_buf(gsm, written, 0, FALSE, &packed, 0, p);
+
+               g_free(gsm);
+
+               if (r == NULL)
+                       return FALSE;
+
+               if (sc)
+                       addr_len = packed + 1;
+               else
+                       addr_len = (written * 7 + 3) / 4;
+       } else {
+               int j = 0;
+               int i;
+               int c;
+
+               if (len > 20)
+                       return FALSE;
+
+               for (i = 0; in->address[i]; i++) {
+                       c = to_semi_oct(in->address[i]);
+
+                       if (c < 0)
+                               return FALSE;
+
+                       if ((i % 2) == 0) {
+                               p[j] = c;
+                       } else {
+                               p[j] |= c << 4;
+                               j++;
+                       }
+               }
+
+               if ((i % 2) == 1) {
+                       p[j] |= 0xf0;
+                       j++;
+               }
+
+               if (sc)
+                       addr_len = j + 1;
+               else
+                       addr_len = i;
+       }
+
+out:
+       pdu[0] = addr_len;
+       pdu[1] = (in->number_type << 4) | in->numbering_plan | 0x80;
+       memcpy(pdu+2, p, (sc ? addr_len - 1 : (addr_len + 1) / 2));
+
+       *offset = *offset + 2 + (sc ? addr_len - 1 : (addr_len + 1) / 2);
+
+       return TRUE;
+}
+
+gboolean sms_decode_address_field(const unsigned char *pdu, int len,
+                                       int *offset, gboolean sc,
+                                       struct sms_address *out)
+{
+       unsigned char addr_len;
+       unsigned char addr_type;
+       int byte_len;
+
+       if (!next_octet(pdu, len, offset, &addr_len))
+               return FALSE;
+
+       if (sc && addr_len == 0) {
+               out->address[0] = '\0';
+               return TRUE;
+       }
+
+       if (!next_octet(pdu, len, offset, &addr_type))
+               return FALSE;
+
+       if (sc)
+               byte_len = addr_len - 1;
+       else
+               byte_len = (addr_len + 1) / 2;
+
+       if ((len - *offset) < byte_len)
+               return FALSE;
+
+       out->number_type = bit_field(addr_type, 4, 3);
+       out->numbering_plan = bit_field(addr_type, 0, 4);
+
+       if (out->number_type != SMS_NUMBER_TYPE_ALPHANUMERIC) {
+               extract_bcd_number(pdu + *offset, byte_len, out->address);
+               *offset += byte_len;
+       } else {
+               int chars;
+               long written;
+               unsigned char *res;
+               char *utf8;
+
+               if (sc)
+                       chars = byte_len * 8 / 7;
+               else
+                       chars = addr_len * 4 / 7;
+
+               /*
+                * This cannot happen according to 24.011, however
+                * nothing is said in 23.040
+                */
+               if (chars == 0) {
+                       out->address[0] = '\0';
+                       return TRUE;
+               }
+
+               res = unpack_7bit(pdu + *offset, byte_len, 0, FALSE, chars,
+                                       &written, 0);
+
+               *offset = *offset + (addr_len + 1) / 2;
+
+               if (res == NULL)
+                       return FALSE;
+
+               utf8 = convert_gsm_to_utf8(res, written, NULL, NULL, 0);
+
+               g_free(res);
+
+               if (utf8 == NULL)
+                       return FALSE;
+
+               if (strlen(utf8) > 20) {
+                       g_free(utf8);
+                       return FALSE;
+               }
+
+               strcpy(out->address, utf8);
+
+               g_free(utf8);
+       }
+
+       return TRUE;
+}
+
+static gboolean encode_deliver(const struct sms_deliver *in, unsigned char *pdu,
+                               int *offset)
+{
+       int ud_oct_len;
+       unsigned char oct;
+
+       oct = 0;
+
+       if (!in->mms)
+               oct |= 1 << 2;
+
+       if (in->sri)
+               oct |= 1 << 5;
+
+       if (in->rp)
+               oct |= 1 << 7;
+
+       if (in->udhi)
+               oct |= 1 << 6;
+
+       set_octet(pdu, offset, oct);
+
+       if (sms_encode_address_field(&in->oaddr, FALSE, pdu, offset) == FALSE)
+               return FALSE;
+
+       set_octet(pdu, offset, in->pid);
+       set_octet(pdu, offset, in->dcs);
+
+       if (sms_encode_scts(&in->scts, pdu, offset) == FALSE)
+               return FALSE;
+
+       set_octet(pdu, offset, in->udl);
+
+       ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
+
+       memcpy(pdu + *offset, in->ud, ud_oct_len);
+
+       *offset = *offset + ud_oct_len;
+
+       return TRUE;
+}
+
+static gboolean decode_deliver(const unsigned char *pdu, int len,
+                               struct sms *out)
+{
+       int offset = 0;
+       int expected;
+       unsigned char octet;
+
+       out->type = SMS_TYPE_DELIVER;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       out->deliver.mms = !is_bit_set(octet, 2);
+       out->deliver.sri = is_bit_set(octet, 5);
+       out->deliver.udhi = is_bit_set(octet, 6);
+       out->deliver.rp = is_bit_set(octet, 7);
+
+       if (!sms_decode_address_field(pdu, len, &offset,
+                                       FALSE, &out->deliver.oaddr))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->deliver.pid))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->deliver.dcs))
+               return FALSE;
+
+       if (!sms_decode_scts(pdu, len, &offset, &out->deliver.scts))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->deliver.udl))
+               return FALSE;
+
+       expected = sms_udl_in_bytes(out->deliver.udl, out->deliver.dcs);
+
+       if ((len - offset) < expected)
+               return FALSE;
+
+       memcpy(out->deliver.ud, pdu+offset, expected);
+
+       return TRUE;
+}
+
+static gboolean encode_submit_ack_report(const struct sms_submit_ack_report *in,
+                                               unsigned char *pdu, int *offset)
+{
+       unsigned char oct;
+
+       oct = 1;
+
+       if (in->udhi)
+               oct |= 1 << 6;
+
+       set_octet(pdu, offset, oct);
+
+       set_octet(pdu, offset, in->pi);
+
+       if (!sms_encode_scts(&in->scts, pdu, offset))
+               return FALSE;
+
+       if (in->pi & 0x1)
+               set_octet(pdu, offset, in->pid);
+
+       if (in->pi & 0x2)
+               set_octet(pdu, offset, in->dcs);
+
+       if (in->pi & 0x4) {
+               int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
+
+               set_octet(pdu, offset, in->udl);
+               memcpy(pdu + *offset, in->ud, ud_oct_len);
+               *offset = *offset + ud_oct_len;
+       }
+
+       return TRUE;
+}
+
+static gboolean encode_submit_err_report(const struct sms_submit_err_report *in,
+                                               unsigned char *pdu, int *offset)
+{
+       unsigned char oct;
+
+       oct = 0x1;
+
+       if (in->udhi)
+               oct |= 1 << 6;
+
+       set_octet(pdu, offset, oct);
+
+       set_octet(pdu, offset, in->fcs);
+
+       set_octet(pdu, offset, in->pi);
+
+       if (!sms_encode_scts(&in->scts, pdu, offset))
+               return FALSE;
+
+       if (in->pi & 0x1)
+               set_octet(pdu, offset, in->pid);
+
+       if (in->pi & 0x2)
+               set_octet(pdu, offset, in->dcs);
+
+       if (in->pi & 0x4) {
+               int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
+
+               set_octet(pdu, offset, in->udl);
+               memcpy(pdu + *offset, in->ud, ud_oct_len);
+               *offset = *offset + ud_oct_len;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_submit_report(const unsigned char *pdu, int len,
+                                       struct sms *out)
+{
+       int offset = 0;
+       unsigned char octet;
+       gboolean udhi;
+       guint8 uninitialized_var(fcs);
+       guint8 pi;
+       struct sms_scts *scts;
+       guint8 pid = 0;
+       guint8 dcs = 0;
+       guint8 udl = 0;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       udhi = is_bit_set(octet, 6);
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       /*
+        * At this point we don't know whether this is an ACK or an ERROR.
+        * FCS can only have values 0x80 and above, as 0x00 - 0x7F are reserved
+        * according to 3GPP 23.040.  For PI, the values can be only in
+        * bit 0, 1, 2 with the 7th bit reserved as an extension.  Since
+        * bits 3-6 are not used, assume no extension is feasible, so if the
+        * value of this octet is >= 0x80, this is an FCS and thus an error
+        * report tpdu.
+        */
+
+       if (octet >= 0x80) {
+               out->type = SMS_TYPE_SUBMIT_REPORT_ERROR;
+               fcs = octet;
+
+               if (!next_octet(pdu, len, &offset, &octet))
+                       return FALSE;
+
+               scts = &out->submit_err_report.scts;
+       } else {
+               scts = &out->submit_ack_report.scts;
+               out->type = SMS_TYPE_SUBMIT_REPORT_ACK;
+       }
+
+       pi = octet & 0x07;
+
+       if (!sms_decode_scts(pdu, len, &offset, scts))
+               return FALSE;
+
+       if (pi & 0x01) {
+               if (!next_octet(pdu, len, &offset, &pid))
+                       return FALSE;
+       }
+
+       if (pi & 0x02) {
+               if (!next_octet(pdu, len, &offset, &dcs))
+                       return FALSE;
+       }
+
+       if (out->type == SMS_TYPE_SUBMIT_REPORT_ERROR) {
+               out->submit_err_report.udhi = udhi;
+               out->submit_err_report.fcs = fcs;
+               out->submit_err_report.pi = pi;
+               out->submit_err_report.pid = pid;
+               out->submit_err_report.dcs = dcs;
+       } else {
+               out->submit_ack_report.udhi = udhi;
+               out->submit_ack_report.pi = pi;
+               out->submit_ack_report.pid = pid;
+               out->submit_ack_report.dcs = dcs;
+       }
+
+       if (pi & 0x04) {
+               int expected;
+
+               if (!next_octet(pdu, len, &offset, &udl))
+                       return FALSE;
+
+               expected = sms_udl_in_bytes(udl, dcs);
+
+               if ((len - offset) < expected)
+                       return FALSE;
+
+               if (out->type == SMS_TYPE_SUBMIT_REPORT_ERROR) {
+                       out->submit_err_report.udl = udl;
+                       memcpy(out->submit_err_report.ud,
+                                       pdu+offset, expected);
+               } else {
+                       out->submit_ack_report.udl = udl;
+                       memcpy(out->submit_ack_report.ud,
+                                       pdu+offset, expected);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean encode_status_report(const struct sms_status_report *in,
+                                       unsigned char *pdu, int *offset)
+{
+       unsigned char octet;
+
+       octet = 0x2;
+
+       if (!in->mms)
+               octet |= 1 << 2;
+
+       if (!in->srq)
+               octet |= 1 << 5;
+
+       if (!in->udhi)
+               octet |= 1 << 6;
+
+       set_octet(pdu, offset, octet);
+
+       set_octet(pdu, offset, in->mr);
+
+       if (!sms_encode_address_field(&in->raddr, FALSE, pdu, offset))
+               return FALSE;
+
+       if (!sms_encode_scts(&in->scts, pdu, offset))
+               return FALSE;
+
+       if (!sms_encode_scts(&in->dt, pdu, offset))
+               return FALSE;
+
+       octet = in->st;
+       set_octet(pdu, offset, octet);
+
+       if (in->pi == 0)
+               return TRUE;
+
+       set_octet(pdu, offset, in->pi);
+
+       if (in->pi & 0x01)
+               set_octet(pdu, offset, in->pid);
+
+       if (in->pi & 0x02)
+               set_octet(pdu, offset, in->dcs);
+
+       if (in->pi & 0x4) {
+               int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
+
+               set_octet(pdu, offset, in->udl);
+               memcpy(pdu + *offset, in->ud, ud_oct_len);
+               *offset = *offset + ud_oct_len;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_status_report(const unsigned char *pdu, int len,
+                                       struct sms *out)
+{
+       int offset = 0;
+       unsigned char octet;
+
+       out->type = SMS_TYPE_STATUS_REPORT;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       out->status_report.mms = !is_bit_set(octet, 2);
+       out->status_report.srq = is_bit_set(octet, 5);
+       out->status_report.udhi = is_bit_set(octet, 6);
+
+       if (!next_octet(pdu, len, &offset, &out->status_report.mr))
+               return FALSE;
+
+       if (!sms_decode_address_field(pdu, len, &offset, FALSE,
+                                       &out->status_report.raddr))
+               return FALSE;
+
+       if (!sms_decode_scts(pdu, len, &offset, &out->status_report.scts))
+               return FALSE;
+
+       if (!sms_decode_scts(pdu, len, &offset, &out->status_report.dt))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       out->status_report.st = octet;
+
+       /*
+        * We have to be careful here, PI is labeled as Optional in 23.040
+        * which is different from RP-ERR & RP-ACK for both Deliver & Submit
+        * reports
+        */
+
+       if ((len - offset) == 0)
+               return TRUE;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       out->status_report.pi = octet & 0x07;
+
+       if (out->status_report.pi & 0x01) {
+               if (!next_octet(pdu, len, &offset, &out->status_report.pid))
+                       return FALSE;
+       }
+
+       if (out->status_report.pi & 0x02) {
+               if (!next_octet(pdu, len, &offset, &out->status_report.dcs))
+                       return FALSE;
+       }
+
+       if (out->status_report.pi & 0x04) {
+               int expected;
+
+               if (!next_octet(pdu, len, &offset, &out->status_report.udl))
+                       return FALSE;
+
+               expected = sms_udl_in_bytes(out->status_report.udl,
+                                               out->status_report.dcs);
+
+               if ((len - offset) < expected)
+                       return FALSE;
+
+               memcpy(out->status_report.ud, pdu+offset, expected);
+       }
+
+       return TRUE;
+}
+
+static gboolean encode_deliver_ack_report(const struct sms_deliver_ack_report *in,
+                                               unsigned char *pdu,
+                                               int *offset)
+{
+       unsigned char oct;
+
+       oct = 0;
+
+       if (in->udhi)
+               oct |= 1 << 6;
+
+       set_octet(pdu, offset, oct);
+
+       set_octet(pdu, offset, in->pi);
+
+       if (in->pi & 0x1)
+               set_octet(pdu, offset, in->pid);
+
+       if (in->pi & 0x2)
+               set_octet(pdu, offset, in->dcs);
+
+       if (in->pi & 0x4) {
+               int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
+
+               set_octet(pdu, offset, in->udl);
+               memcpy(pdu + *offset, in->ud, ud_oct_len);
+               *offset = *offset + ud_oct_len;
+       }
+
+       return TRUE;
+}
+
+static gboolean encode_deliver_err_report(const struct sms_deliver_err_report *in,
+                                               unsigned char *pdu,
+                                               int *offset)
+{
+       unsigned char oct;
+
+       oct = 0;
+
+       if (in->udhi)
+               oct |= 1 << 6;
+
+       set_octet(pdu, offset, oct);
+
+       set_octet(pdu, offset, in->fcs);
+
+       set_octet(pdu, offset, in->pi);
+
+       if (in->pi & 0x1)
+               set_octet(pdu, offset, in->pid);
+
+       if (in->pi & 0x2)
+               set_octet(pdu, offset, in->dcs);
+
+       if (in->pi & 0x4) {
+               int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
+
+               set_octet(pdu, offset, in->udl);
+               memcpy(pdu + *offset, in->ud, ud_oct_len);
+               *offset = *offset + ud_oct_len;
+       }
+
+       return TRUE;
+}
+
+static gboolean decode_deliver_report(const unsigned char *pdu, int len,
+                                       struct sms *out)
+{
+       int offset = 0;
+       unsigned char octet;
+       gboolean udhi;
+       guint8 uninitialized_var(fcs);
+       guint8 pi;
+       guint8 pid = 0;
+       guint8 dcs = 0;
+       guint8 udl = 0;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       udhi = is_bit_set(octet, 6);
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       /*
+        * At this point we don't know whether this is an ACK or an ERROR.
+        * FCS can only have values 0x80 and above, as 0x00 - 0x7F are reserved
+        * according to 3GPP 23.040.  For PI, the values can be only in
+        * bit 0, 1, 2 with the 7th bit reserved as an extension.  Since
+        * bits 3-6 are not used, assume no extension is feasible, so if the
+        * value of this octet is >= 0x80, this is an FCS and thus an error
+        * report tpdu.
+        */
+
+       if (octet >= 0x80) {
+               out->type = SMS_TYPE_DELIVER_REPORT_ERROR;
+               fcs = octet;
+
+               if (!next_octet(pdu, len, &offset, &octet))
+                       return FALSE;
+       } else {
+               out->type = SMS_TYPE_DELIVER_REPORT_ACK;
+       }
+
+       pi = octet & 0x07;
+
+       if (pi & 0x01) {
+               if (!next_octet(pdu, len, &offset, &pid))
+                       return FALSE;
+       }
+
+       if (pi & 0x02) {
+               if (!next_octet(pdu, len, &offset, &dcs))
+                       return FALSE;
+       }
+
+       if (out->type == SMS_TYPE_DELIVER_REPORT_ERROR) {
+               out->deliver_err_report.udhi = udhi;
+               out->deliver_err_report.fcs = fcs;
+               out->deliver_err_report.pi = pi;
+               out->deliver_err_report.pid = pid;
+               out->deliver_err_report.dcs = dcs;
+       } else {
+               out->deliver_ack_report.udhi = udhi;
+               out->deliver_ack_report.pi = pi;
+               out->deliver_ack_report.pid = pid;
+               out->deliver_ack_report.dcs = dcs;
+       }
+
+       if (pi & 0x04) {
+               int expected;
+
+               if (!next_octet(pdu, len, &offset, &udl))
+                       return FALSE;
+
+               expected = sms_udl_in_bytes(udl, dcs);
+
+               if ((len - offset) < expected)
+                       return FALSE;
+
+               if (out->type == SMS_TYPE_DELIVER_REPORT_ERROR) {
+                       out->deliver_err_report.udl = udl;
+                       memcpy(out->deliver_err_report.ud,
+                                       pdu+offset, expected);
+               } else {
+                       out->deliver_ack_report.udl = udl;
+                       memcpy(out->deliver_ack_report.ud,
+                                       pdu+offset, expected);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean encode_submit(const struct sms_submit *in,
+                                       unsigned char *pdu, int *offset)
+{
+       unsigned char octet;
+       int ud_oct_len;
+
+       /* SMS Submit */
+       octet = 0x1;
+
+       if (in->rd)
+               octet |= 1 << 2;
+
+       if (in->rp)
+               octet |= 1 << 7;
+
+       octet |= in->vpf << 3;
+
+       if (in->udhi)
+               octet |= 1 << 6;
+
+       if (in->srr)
+               octet |= 1 << 5;
+
+       set_octet(pdu, offset, octet);
+
+       set_octet(pdu, offset, in->mr);
+
+       if (sms_encode_address_field(&in->daddr, FALSE, pdu, offset) == FALSE)
+               return FALSE;
+
+       set_octet(pdu, offset, in->pid);
+
+       set_octet(pdu, offset, in->dcs);
+
+       if (!encode_validity_period(&in->vp, in->vpf, pdu, offset))
+               return FALSE;
+
+       set_octet(pdu, offset, in->udl);
+
+       ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs);
+
+       memcpy(pdu + *offset, in->ud, ud_oct_len);
+
+       *offset = *offset + ud_oct_len;
+
+       return TRUE;
+}
+
+gboolean sms_decode_unpacked_stk_pdu(const unsigned char *pdu, int len,
+                                       struct sms *out)
+{
+       unsigned char octet;
+       int offset = 0;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       if ((octet & 0x3) != 1)
+               return FALSE;
+
+       out->type = SMS_TYPE_SUBMIT;
+
+       out->submit.rd = is_bit_set(octet, 2);
+       out->submit.vpf = bit_field(octet, 3, 2);
+       out->submit.rp = is_bit_set(octet, 7);
+       out->submit.udhi = is_bit_set(octet, 6);
+       out->submit.srr = is_bit_set(octet, 5);
+
+       if (!next_octet(pdu, len, &offset, &out->submit.mr))
+               return FALSE;
+
+       if (!sms_decode_address_field(pdu, len, &offset,
+                                       FALSE, &out->submit.daddr))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->submit.pid))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->submit.dcs))
+               return FALSE;
+
+       /* Now we override the DCS */
+       out->submit.dcs = 0xF0;
+
+       if (!decode_validity_period(pdu, len, &offset, out->submit.vpf,
+                                       &out->submit.vp))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->submit.udl))
+               return FALSE;
+
+       if ((len - offset) < out->submit.udl)
+               return FALSE;
+
+       pack_7bit_own_buf(pdu + offset, out->submit.udl, 0, FALSE,
+                               NULL, 0, out->submit.ud);
+
+       return TRUE;
+}
+
+static gboolean decode_submit(const unsigned char *pdu, int len,
+                                       struct sms *out)
+{
+       unsigned char octet;
+       int offset = 0;
+       int expected;
+
+       out->type = SMS_TYPE_SUBMIT;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       out->submit.rd = is_bit_set(octet, 2);
+       out->submit.vpf = bit_field(octet, 3, 2);
+       out->submit.rp = is_bit_set(octet, 7);
+       out->submit.udhi = is_bit_set(octet, 6);
+       out->submit.srr = is_bit_set(octet, 5);
+
+       if (!next_octet(pdu, len, &offset, &out->submit.mr))
+               return FALSE;
+
+       if (!sms_decode_address_field(pdu, len, &offset,
+                                       FALSE, &out->submit.daddr))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->submit.pid))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->submit.dcs))
+               return FALSE;
+
+       if (!decode_validity_period(pdu, len, &offset, out->submit.vpf,
+                                       &out->submit.vp))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->submit.udl))
+               return FALSE;
+
+       expected = sms_udl_in_bytes(out->submit.udl, out->submit.dcs);
+
+       if ((len - offset) < expected)
+               return FALSE;
+
+       if (expected > (int) sizeof(out->submit.ud))
+               return FALSE;
+
+       memcpy(out->submit.ud, pdu+offset, expected);
+
+       return TRUE;
+}
+
+static gboolean encode_command(const struct sms_command *in,
+                                       unsigned char *pdu, int *offset)
+{
+       unsigned char octet;
+
+       octet = 0x2;
+
+       if (in->udhi)
+               octet |= 1 << 6;
+
+       if (in->srr)
+               octet |= 1 << 5;
+
+       set_octet(pdu, offset, octet);
+
+       set_octet(pdu, offset, in->mr);
+
+       set_octet(pdu, offset, in->pid);
+
+       octet = in->ct;
+       set_octet(pdu, offset, octet);
+
+       set_octet(pdu, offset, in->mn);
+
+       if (!sms_encode_address_field(&in->daddr, FALSE, pdu, offset))
+               return FALSE;
+
+       set_octet(pdu, offset, in->cdl);
+
+       memcpy(pdu + *offset, in->cd, in->cdl);
+
+       *offset = *offset + in->cdl;
+
+       return TRUE;
+}
+
+static gboolean decode_command(const unsigned char *pdu, int len,
+                                       struct sms *out)
+{
+       unsigned char octet;
+       int offset = 0;
+
+       out->type = SMS_TYPE_COMMAND;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       out->command.udhi = is_bit_set(octet, 6);
+       out->command.srr = is_bit_set(octet, 5);
+
+       if (!next_octet(pdu, len, &offset, &out->command.mr))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->command.pid))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &octet))
+               return FALSE;
+
+       out->command.ct = octet;
+
+       if (!next_octet(pdu, len, &offset, &out->command.mn))
+               return FALSE;
+
+       if (!sms_decode_address_field(pdu, len, &offset,
+                                       FALSE, &out->command.daddr))
+               return FALSE;
+
+       if (!next_octet(pdu, len, &offset, &out->command.cdl))
+               return FALSE;
+
+       if ((len - offset) < out->command.cdl)
+               return FALSE;
+
+       memcpy(out->command.cd, pdu+offset, out->command.cdl);
+
+       return TRUE;
+}
+
+/* Buffer must be at least 164 (tpud) + 12 (SC address) bytes long */
+gboolean sms_encode(const struct sms *in, int *len, int *tpdu_len,
+                       unsigned char *pdu)
+{
+       int offset = 0;
+       int tpdu_start;
+
+       if (in->type == SMS_TYPE_DELIVER || in->type == SMS_TYPE_SUBMIT ||
+                       in->type == SMS_TYPE_COMMAND ||
+                       in->type == SMS_TYPE_STATUS_REPORT)
+               if (!sms_encode_address_field(&in->sc_addr, TRUE, pdu, &offset))
+                       return FALSE;
+
+       tpdu_start = offset;
+
+       switch (in->type) {
+       case SMS_TYPE_DELIVER:
+               if (encode_deliver(&in->deliver, pdu, &offset) == FALSE)
+                       return FALSE;
+               break;
+       case SMS_TYPE_DELIVER_REPORT_ACK:
+               if (!encode_deliver_ack_report(&in->deliver_ack_report, pdu,
+                                               &offset))
+                       return FALSE;
+               break;
+       case SMS_TYPE_DELIVER_REPORT_ERROR:
+               if (!encode_deliver_err_report(&in->deliver_err_report, pdu,
+                                               &offset))
+                       return FALSE;
+               break;
+       case SMS_TYPE_STATUS_REPORT:
+               if (!encode_status_report(&in->status_report, pdu, &offset))
+                       return FALSE;
+               break;
+       case SMS_TYPE_SUBMIT:
+               if (!encode_submit(&in->submit, pdu, &offset))
+                       return FALSE;
+               break;
+       case SMS_TYPE_SUBMIT_REPORT_ACK:
+               if (!encode_submit_ack_report(&in->submit_ack_report, pdu,
+                                               &offset))
+                       return FALSE;
+               break;
+       case SMS_TYPE_SUBMIT_REPORT_ERROR:
+               if (!encode_submit_err_report(&in->submit_err_report, pdu,
+                                               &offset))
+                       return FALSE;
+               break;
+       case SMS_TYPE_COMMAND:
+               if (!encode_command(&in->command, pdu, &offset))
+                       return FALSE;
+               break;
+       default:
+               return FALSE;
+       };
+
+       if (tpdu_len)
+               *tpdu_len = offset - tpdu_start;
+
+       if (len)
+               *len = offset;
+
+       return TRUE;
+}
+
+gboolean sms_decode(const unsigned char *pdu, int len, gboolean outgoing,
+                       int tpdu_len, struct sms *out)
+{
+       unsigned char type;
+       int offset = 0;
+
+       if (out == NULL)
+               return FALSE;
+
+       if (len == 0)
+               return FALSE;
+
+       memset(out, 0, sizeof(*out));
+
+       if (tpdu_len < len) {
+               if (!sms_decode_address_field(pdu, len, &offset,
+                                               TRUE, &out->sc_addr))
+                       return FALSE;
+       }
+
+       if ((len - offset) < tpdu_len)
+               return FALSE;
+
+       /* 23.040 9.2.3.1 */
+       type = pdu[offset] & 0x3;
+
+       if (outgoing)
+               type |= 0x4;
+
+       pdu = pdu + offset;
+
+       switch (type) {
+       case 0:
+               return decode_deliver(pdu, tpdu_len, out);
+       case 1:
+               return decode_submit_report(pdu, tpdu_len, out);
+       case 2:
+               return decode_status_report(pdu, tpdu_len, out);
+       case 3:
+               /* According to 9.2.3.1, Reserved treated as deliver */
+               return decode_deliver(pdu, tpdu_len, out);
+       case 4:
+               return decode_deliver_report(pdu, tpdu_len, out);
+       case 5:
+               return decode_submit(pdu, tpdu_len, out);
+       case 6:
+               return decode_command(pdu, tpdu_len, out);
+       }
+
+       return FALSE;
+}
+
+const guint8 *sms_extract_common(const struct sms *sms, gboolean *out_udhi,
+                                       guint8 *out_dcs, guint8 *out_udl,
+                                       guint8 *out_max)
+{
+       const guint8 *ud = NULL;
+       guint8 uninitialized_var(udl);
+       guint8 uninitialized_var(max);
+       gboolean uninitialized_var(udhi);
+       guint8 uninitialized_var(dcs);
+
+       switch (sms->type) {
+       case SMS_TYPE_DELIVER:
+               udhi = sms->deliver.udhi;
+               ud = sms->deliver.ud;
+               udl = sms->deliver.udl;
+               dcs = sms->deliver.dcs;
+               max = sizeof(sms->deliver.ud);
+               break;
+       case SMS_TYPE_DELIVER_REPORT_ACK:
+               udhi = sms->deliver_ack_report.udhi;
+               ud = sms->deliver_ack_report.ud;
+               udl = sms->deliver_ack_report.udl;
+               dcs = sms->deliver_ack_report.dcs;
+               max = sizeof(sms->deliver_ack_report.ud);
+               break;
+       case SMS_TYPE_DELIVER_REPORT_ERROR:
+               udhi = sms->deliver_err_report.udhi;
+               ud = sms->deliver_err_report.ud;
+               udl = sms->deliver_err_report.udl;
+               dcs = sms->deliver_err_report.dcs;
+               max = sizeof(sms->deliver_err_report.ud);
+               break;
+       case SMS_TYPE_STATUS_REPORT:
+               udhi = sms->status_report.udhi;
+               ud = sms->status_report.ud;
+               udl = sms->status_report.udl;
+               dcs = sms->status_report.dcs;
+               max = sizeof(sms->status_report.ud);
+               break;
+       case SMS_TYPE_SUBMIT:
+               udhi = sms->submit.udhi;
+               ud = sms->submit.ud;
+               udl = sms->submit.udl;
+               dcs = sms->submit.dcs;
+               max = sizeof(sms->submit.ud);
+               break;
+       case SMS_TYPE_SUBMIT_REPORT_ACK:
+               udhi = sms->submit_ack_report.udhi;
+               ud = sms->submit_ack_report.ud;
+               udl = sms->submit_ack_report.udl;
+               dcs = sms->submit_ack_report.dcs;
+               max = sizeof(sms->submit_ack_report.ud);
+               break;
+       case SMS_TYPE_SUBMIT_REPORT_ERROR:
+               udhi = sms->submit_err_report.udhi;
+               ud = sms->submit_err_report.ud;
+               udl = sms->submit_err_report.udl;
+               dcs = sms->submit_err_report.dcs;
+               max = sizeof(sms->submit_err_report.ud);
+               break;
+       case SMS_TYPE_COMMAND:
+               udhi = sms->command.udhi;
+               ud = sms->command.cd;
+               udl = sms->command.cdl;
+               dcs = 0;
+               max = sizeof(sms->command.cd);
+               break;
+       };
+
+       if (ud == NULL)
+               return NULL;
+
+       if (out_udhi)
+               *out_udhi = udhi;
+
+       if (out_dcs)
+               *out_dcs = dcs;
+
+       if (out_udl)
+               *out_udl = udl;
+
+       if (out_max)
+               *out_max = max;
+
+       return ud;
+}
+
+static gboolean verify_udh(const guint8 *hdr, guint8 max_len)
+{
+       guint8 max_offset;
+       guint8 offset;
+
+       /* Must have at least one information-element if udhi is true */
+       if (hdr[0] < 2)
+               return FALSE;
+
+       if (hdr[0] >= max_len)
+               return FALSE;
+
+       /*
+        * According to 23.040: If the length of the User Data Header is
+        * such that there are too few or too many octets in the final
+        * Information Element then the whole User Data Header shall be
+        * ignored.
+        */
+
+       max_offset = hdr[0] + 1;
+       offset = 1;
+       do {
+               if ((offset + 2) > max_offset)
+                       return FALSE;
+
+               if ((offset + 2 + hdr[offset + 1]) > max_offset)
+                       return FALSE;
+
+               offset = offset + 2 + hdr[offset + 1];
+       } while (offset < max_offset);
+
+       if (offset != max_offset)
+               return FALSE;
+
+       return TRUE;
+}
+
+gboolean sms_udh_iter_init(const struct sms *sms, struct sms_udh_iter *iter)
+{
+       gboolean udhi = FALSE;
+       const guint8 *hdr;
+       guint8 udl;
+       guint8 dcs;
+       guint8 max_len;
+       guint8 max_ud_len;
+
+       hdr = sms_extract_common(sms, &udhi, &dcs, &udl, &max_ud_len);
+       if (hdr == NULL)
+               return FALSE;
+
+       if (!udhi)
+               return FALSE;
+
+       if (sms->type == SMS_TYPE_COMMAND)
+               max_len = udl;
+       else
+               max_len = sms_udl_in_bytes(udl, dcs);
+
+       /* Can't actually store the HDL + IEI / IEL */
+       if (max_len < 3)
+               return FALSE;
+
+       if (max_len > max_ud_len)
+               return FALSE;
+
+       if (!verify_udh(hdr, max_len))
+               return FALSE;
+
+       iter->data = hdr;
+       iter->offset = 1;
+
+       return TRUE;
+}
+
+gboolean sms_udh_iter_init_from_cbs(const struct cbs *cbs,
+                                       struct sms_udh_iter *iter)
+{
+       gboolean udhi = FALSE;
+       const guint8 *hdr;
+       guint8 max_ud_len;
+
+       cbs_dcs_decode(cbs->dcs, &udhi, NULL, NULL, NULL, NULL, NULL);
+
+       if (!udhi)
+               return FALSE;
+
+       hdr = cbs->ud;
+       max_ud_len = 82;
+
+       /* Must have at least one information-element if udhi is true */
+       if (hdr[0] < 2)
+               return FALSE;
+
+       if (hdr[0] >= max_ud_len)
+               return FALSE;
+
+       if (!verify_udh(hdr, max_ud_len))
+               return FALSE;
+
+       iter->data = hdr;
+       iter->offset = 1;
+
+       return TRUE;
+}
+guint8 sms_udh_iter_get_udh_length(struct sms_udh_iter *iter)
+{
+       return iter->data[0];
+}
+
+const guint8 *sms_udh_iter_get_ud_after_header(struct sms_udh_iter *iter)
+{
+       return iter->data + iter->data[0] + 1;
+}
+
+enum sms_iei sms_udh_iter_get_ie_type(struct sms_udh_iter *iter)
+{
+       if (iter->offset > iter->data[0])
+               return SMS_IEI_INVALID;
+
+       return (enum sms_iei) iter->data[iter->offset];
+}
+
+guint8 sms_udh_iter_get_ie_length(struct sms_udh_iter *iter)
+{
+       guint8 ie_len;
+
+       ie_len = iter->data[iter->offset + 1];
+
+       return ie_len;
+}
+
+void sms_udh_iter_get_ie_data(struct sms_udh_iter *iter, guint8 *data)
+{
+       guint8 ie_len;
+
+       ie_len = iter->data[iter->offset + 1];
+
+       memcpy(data, &iter->data[iter->offset + 2], ie_len);
+}
+
+gboolean sms_udh_iter_has_next(struct sms_udh_iter *iter)
+{
+       guint8 total_len = iter->data[0];
+       guint8 cur_ie_len = iter->data[iter->offset + 1];
+
+       if ((iter->offset + 2 + cur_ie_len) > total_len)
+               return FALSE;
+
+       return TRUE;
+}
+
+gboolean sms_udh_iter_next(struct sms_udh_iter *iter)
+{
+       if (iter->offset > iter->data[0])
+               return FALSE;
+
+       iter->offset = iter->offset + 2 + iter->data[iter->offset + 1];
+
+       if (iter->offset > iter->data[0])
+               return FALSE;
+
+       return TRUE;
+}
+
+/*
+ * Returns both forms of time.  The time_t value returns the time in local
+ * timezone.  The struct tm is filled out with the remote time information
+ */
+time_t sms_scts_to_time(const struct sms_scts *scts, struct tm *remote)
+{
+       struct tm t;
+       time_t ret;
+
+       t.tm_sec = scts->second;
+       t.tm_min = scts->minute;
+       t.tm_hour = scts->hour;
+       t.tm_mday = scts->day;
+       t.tm_mon = scts->month - 1;
+       t.tm_isdst = -1;
+
+       if (scts->year > 80)
+               t.tm_year = scts->year;
+       else
+               t.tm_year = scts->year + 100;
+
+       ret = mktime(&t);
+
+       /* Adjust local time by the local timezone information */
+       ret += t.tm_gmtoff;
+
+       /* Set the proper timezone on the remote side */
+       t.tm_gmtoff = scts->timezone * 15 * 60;
+
+       /* Now adjust by the remote timezone information */
+       ret -= t.tm_gmtoff;
+
+       if (remote)
+               memcpy(remote, &t, sizeof(struct tm));
+
+       return ret;
+}
+
+void sms_address_from_string(struct sms_address *addr, const char *str)
+{
+       addr->numbering_plan = SMS_NUMBERING_PLAN_ISDN;
+       if (str[0] == '+') {
+               addr->number_type = SMS_NUMBER_TYPE_INTERNATIONAL;
+               strcpy(addr->address, str+1);
+       } else {
+               addr->number_type = SMS_NUMBER_TYPE_UNKNOWN;
+               strcpy(addr->address, str);
+       }
+}
+
+const char *sms_address_to_string(const struct sms_address *addr)
+{
+       static char buffer[64];
+
+       if (addr->number_type == SMS_NUMBER_TYPE_INTERNATIONAL &&
+                       (strlen(addr->address) > 0) &&
+                               addr->address[0] != '+') {
+               buffer[0] = '+';
+               strcpy(buffer + 1, addr->address);
+       } else {
+               strcpy(buffer, addr->address);
+       }
+
+       return buffer;
+}
+
+static gboolean extract_app_port_common(struct sms_udh_iter *iter, int *dst,
+                                       int *src, gboolean *is_8bit)
+{
+       enum sms_iei iei;
+       guint8 addr_hdr[4];
+       int srcport = -1;
+       int dstport = -1;
+       gboolean uninitialized_var(is_addr_8bit);
+
+       /*
+        * According to the specification, we have to use the last
+        * useable header.  Also, we have to ignore ports that are reserved:
+        * A receiving entity shall ignore (i.e. skip over and commence
+        * processing at the next information element) any information element
+        * where the value of the Information-Element-Data is Reserved or not
+        * supported.
+        */
+       while ((iei = sms_udh_iter_get_ie_type(iter)) !=
+                       SMS_IEI_INVALID) {
+               switch (iei) {
+               case SMS_IEI_APPLICATION_ADDRESS_8BIT:
+                       if (sms_udh_iter_get_ie_length(iter) != 2)
+                               break;
+
+                       sms_udh_iter_get_ie_data(iter, addr_hdr);
+
+                       if (addr_hdr[0] < 240)
+                               break;
+
+                       if (addr_hdr[1] < 240)
+                               break;
+
+                       dstport = addr_hdr[0];
+                       srcport = addr_hdr[1];
+                       is_addr_8bit = TRUE;
+                       break;
+
+               case SMS_IEI_APPLICATION_ADDRESS_16BIT:
+                       if (sms_udh_iter_get_ie_length(iter) != 4)
+                               break;
+
+                       sms_udh_iter_get_ie_data(iter, addr_hdr);
+
+                       if (((addr_hdr[0] << 8) | addr_hdr[1]) > 49151)
+                               break;
+
+                       if (((addr_hdr[2] << 8) | addr_hdr[3]) > 49151)
+                               break;
+
+                       dstport = (addr_hdr[0] << 8) | addr_hdr[1];
+                       srcport = (addr_hdr[2] << 8) | addr_hdr[3];
+                       is_addr_8bit = FALSE;
+                       break;
+
+               default:
+                       break;
+               }
+
+               sms_udh_iter_next(iter);
+       }
+
+       if (dstport == -1 || srcport == -1)
+               return FALSE;
+
+       if (dst)
+               *dst = dstport;
+
+       if (src)
+               *src = srcport;
+
+       if (is_8bit)
+               *is_8bit = is_addr_8bit;
+
+       return TRUE;
+
+}
+
+gboolean sms_extract_app_port(const struct sms *sms, int *dst, int *src,
+                               gboolean *is_8bit)
+{
+       struct sms_udh_iter iter;
+
+       if (!sms_udh_iter_init(sms, &iter))
+               return FALSE;
+
+       return extract_app_port_common(&iter, dst, src, is_8bit);
+}
+
+gboolean sms_extract_concatenation(const struct sms *sms, guint16 *ref_num,
+                                       guint8 *max_msgs, guint8 *seq_num)
+{
+       struct sms_udh_iter iter;
+       enum sms_iei iei;
+       guint8 concat_hdr[4];
+       guint16 uninitialized_var(rn);
+       guint8 uninitialized_var(max), uninitialized_var(seq);
+       gboolean concatenated = FALSE;
+
+       /*
+        * We must ignore the entire user_data header here:
+        * If the length of the User Data Header is such that there
+        * are too few or too many octets in the final Information
+        * Element then the whole User Data Header shall be ignored.
+        */
+       if (!sms_udh_iter_init(sms, &iter))
+               return FALSE;
+
+       /*
+        * According to the specification, we have to use the last
+        * useable header:
+        * In the event that IEs determined as not repeatable are
+        * duplicated, the last occurrence of the IE shall be used.
+        * In the event that two or more IEs occur which have mutually
+        * exclusive meanings (e.g. an 8bit port address and a 16bit
+        * port address), then the last occurring IE shall be used.
+        */
+       while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
+                       SMS_IEI_INVALID) {
+               switch (iei) {
+               case SMS_IEI_CONCATENATED_8BIT:
+                       if (sms_udh_iter_get_ie_length(&iter) != 3)
+                               break;
+
+                       sms_udh_iter_get_ie_data(&iter, concat_hdr);
+
+                       if (concat_hdr[1] == 0)
+                               break;
+
+                       if (concat_hdr[2] == 0 || concat_hdr[2] > concat_hdr[1])
+                               break;
+
+                       rn = concat_hdr[0];
+                       max = concat_hdr[1];
+                       seq = concat_hdr[2];
+                       concatenated = TRUE;
+                       break;
+
+               case SMS_IEI_CONCATENATED_16BIT:
+                       if (sms_udh_iter_get_ie_length(&iter) != 4)
+                               break;
+
+                       sms_udh_iter_get_ie_data(&iter, concat_hdr);
+
+                       if (concat_hdr[2] == 0)
+                               break;
+
+                       if (concat_hdr[3] == 0 ||
+                                       concat_hdr[3] > concat_hdr[2])
+                               break;
+
+                       rn = (concat_hdr[0] << 8) | concat_hdr[1];
+                       max = concat_hdr[2];
+                       seq = concat_hdr[3];
+                       concatenated = TRUE;
+                       break;
+               default:
+                       break;
+               }
+
+               sms_udh_iter_next(&iter);
+       }
+
+       if (!concatenated)
+               return FALSE;
+
+       if (ref_num)
+               *ref_num = rn;
+
+       if (max_msgs)
+               *max_msgs = max;
+
+       if (seq_num)
+               *seq_num = seq;
+
+       return TRUE;
+}
+
+gboolean sms_extract_language_variant(const struct sms *sms, guint8 *locking,
+                                       guint8 *single)
+{
+       struct sms_udh_iter iter;
+       enum sms_iei iei;
+       guint8 variant;
+
+       /*
+        * We must ignore the entire user_data header here:
+        * If the length of the User Data Header is such that there
+        * are too few or too many octets in the final Information
+        * Element then the whole User Data Header shall be ignored.
+        */
+       if (!sms_udh_iter_init(sms, &iter))
+               return FALSE;
+
+       /*
+        * According to the specification, we have to use the last
+        * useable header:
+        * In the event that IEs determined as not repeatable are
+        * duplicated, the last occurrence of the IE shall be used.
+        * In the event that two or more IEs occur which have mutually
+        * exclusive meanings (e.g. an 8bit port address and a 16bit
+        * port address), then the last occurring IE shall be used.
+        */
+       while ((iei = sms_udh_iter_get_ie_type(&iter)) !=
+                       SMS_IEI_INVALID) {
+               switch (iei) {
+               case SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT:
+                       if (sms_udh_iter_get_ie_length(&iter) != 1)
+                               break;
+
+                       sms_udh_iter_get_ie_data(&iter, &variant);
+                       if (single)
+                               *single = variant;
+                       break;
+
+               case SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT:
+                       if (sms_udh_iter_get_ie_length(&iter) != 1)
+                               break;
+
+                       sms_udh_iter_get_ie_data(&iter, &variant);
+                       if (locking)
+                               *locking = variant;
+                       break;
+               default:
+                       break;
+               }
+
+               sms_udh_iter_next(&iter);
+       }
+
+       return TRUE;
+}
+
+/*!
+ * Decodes a list of SMSes that contain a datagram.  The list must be
+ * sorted in order of the sequence number.  This function assumes that
+ * all fragments are coded using 8-bit character set.
+ *
+ * Returns a pointer to a newly allocated array or NULL if the
+ * conversion could not be performed
+ */
+unsigned char *sms_decode_datagram(GSList *sms_list, long *out_len)
+{
+       GSList *l;
+       const struct sms *sms;
+       unsigned char *buf;
+       long len = 0;
+
+       for (l = sms_list; l; l = l->next) {
+               guint8 taken = 0;
+               guint8 udl;
+               const guint8 *ud;
+               struct sms_udh_iter iter;
+
+               sms = l->data;
+
+               ud = sms_extract_common(sms, NULL, NULL, &udl, NULL);
+               if (ud == NULL)
+                       return NULL;
+
+               /*
+                * Note we do this because we must check whether the UDH
+                * is properly formatted.  If not, the entire UDH is ignored
+                */
+               if (sms_udh_iter_init(sms, &iter))
+                       taken = sms_udh_iter_get_udh_length(&iter) + 1;
+
+               len += udl - taken;
+       }
+
+       /* Data is probably in headers we can't understand */
+       if (len == 0)
+               return NULL;
+
+       buf = g_try_new(unsigned char, len);
+       if (buf == NULL)
+               return NULL;
+
+       len = 0;
+       for (l = sms_list; l; l = l->next) {
+               guint8 taken = 0;
+               guint8 udl;
+               const guint8 *ud;
+               struct sms_udh_iter iter;
+
+               sms = l->data;
+
+               ud = sms_extract_common(sms, NULL, NULL, &udl, NULL);
+
+               if (sms_udh_iter_init(sms, &iter))
+                       taken = sms_udh_iter_get_udh_length(&iter) + 1;
+
+               memcpy(buf + len, ud + taken, udl - taken);
+               len += udl - taken;
+       }
+
+       if (out_len)
+               *out_len = len;
+
+       return buf;
+}
+
+static inline int sms_text_capacity_gsm(int max, int offset)
+{
+       return max - (offset * 8 + 6) / 7;
+}
+
+/*!
+ * Decodes a list of SMSes that contain a text in either 7bit or UCS2 encoding.
+ * The list must be sorted in order of the sequence number.  This function
+ * assumes that all fragments have a proper DCS.
+ *
+ * Returns a pointer to a newly allocated string or NULL if the conversion
+ * failed.
+ */
+char *sms_decode_text(GSList *sms_list)
+{
+       GSList *l;
+       GString *str;
+       const struct sms *sms;
+       int guess_size = g_slist_length(sms_list);
+       char *utf8;
+
+       if (guess_size == 1)
+               guess_size = 160;
+       else
+               guess_size = (guess_size - 1) * 160;
+
+       str = g_string_sized_new(guess_size);
+
+       for (l = sms_list; l; l = l->next) {
+               guint8 taken = 0;
+               guint8 dcs;
+               guint8 udl;
+               enum sms_charset charset;
+               int udl_in_bytes;
+               const guint8 *ud;
+               struct sms_udh_iter iter;
+               char *converted;
+
+               sms = l->data;
+
+               ud = sms_extract_common(sms, NULL, &dcs, &udl, NULL);
+
+               if (!sms_mwi_dcs_decode(dcs, NULL, &charset, NULL, NULL) &&
+                       !sms_dcs_decode(dcs, NULL, &charset, NULL, NULL))
+                       continue;
+
+               if (charset == SMS_CHARSET_8BIT)
+                       continue;
+
+               if (sms_udh_iter_init(sms, &iter))
+                       taken = sms_udh_iter_get_udh_length(&iter) + 1;
+
+               udl_in_bytes = sms_udl_in_bytes(udl, dcs);
+
+               if (udl_in_bytes == taken)
+                       continue;
+
+               if (charset == SMS_CHARSET_7BIT) {
+                       unsigned char buf[160];
+                       long written;
+                       guint8 locking_shift = 0;
+                       guint8 single_shift = 0;
+                       int max_chars = sms_text_capacity_gsm(udl, taken);
+
+                       if (unpack_7bit_own_buf(ud + taken,
+                                               udl_in_bytes - taken,
+                                               taken, FALSE, max_chars,
+                                               &written, 0, buf) == NULL)
+                               continue;
+
+                       /* Take care of improperly split fragments */
+                       if (buf[written-1] == 0x1b)
+                               written = written - 1;
+
+                       sms_extract_language_variant(sms, &locking_shift,
+                                                               &single_shift);
+
+                       /*
+                        * If language is not defined in 3GPP TS 23.038,
+                        * implementations are instructed to ignore it
+                        */
+                       if (locking_shift > SMS_ALPHABET_PORTUGUESE)
+                               locking_shift = GSM_DIALECT_DEFAULT;
+
+                       if (single_shift > SMS_ALPHABET_PORTUGUESE)
+                               single_shift = GSM_DIALECT_DEFAULT;
+
+                       converted = convert_gsm_to_utf8_with_lang(buf, written,
+                                                               NULL, NULL, 0,
+                                                               locking_shift,
+                                                               single_shift);
+               } else {
+                       const gchar *from = (const gchar *) (ud + taken);
+                       /*
+                        * According to the spec: A UCS2 character shall not be
+                        * split in the middle; if the length of the User Data
+                        * Header is odd, the maximum length of the whole TP-UD
+                        * field is 139 octets
+                        */
+                       gssize num_ucs2_chars = (udl_in_bytes - taken) >> 1;
+                       num_ucs2_chars = num_ucs2_chars << 1;
+
+                       converted = g_convert(from, num_ucs2_chars,
+                                               "UTF-8//TRANSLIT", "UCS-2BE",
+                                               NULL, NULL, NULL);
+               }
+
+               if (converted) {
+                       g_string_append(str, converted);
+                       g_free(converted);
+               }
+       }
+
+       utf8 = g_string_free(str, FALSE);
+
+       return utf8;
+}
+
+static int sms_serialize(unsigned char *buf, const struct sms *sms)
+{
+       int len, tpdu_len;
+
+       sms_encode(sms, &len, &tpdu_len, buf + 1);
+       buf[0] = tpdu_len;
+
+       return len + 1;
+}
+
+static gboolean sms_deserialize(const unsigned char *buf,
+               struct sms *sms, int len)
+{
+       if (len < 1)
+               return FALSE;
+
+       return sms_decode(buf + 1, len - 1, FALSE, buf[0], sms);
+}
+
+static gboolean sms_deserialize_outgoing(const unsigned char *buf,
+               struct sms *sms, int len)
+{
+       if (len < 1)
+               return FALSE;
+
+       return sms_decode(buf + 1, len - 1, TRUE, buf[0], sms);
+}
+
+static gboolean sms_assembly_extract_address(const char *straddr,
+                                               struct sms_address *out)
+{
+       unsigned char pdu[12];
+       long len;
+       int offset = 0;
+
+       if (decode_hex_own_buf(straddr, -1, &len, 0, pdu) == NULL)
+               return FALSE;
+
+       return sms_decode_address_field(pdu, len, &offset, FALSE, out);
+}
+
+gboolean sms_address_to_hex_string(const struct sms_address *in, char *straddr)
+{
+       unsigned char pdu[12];
+       int offset = 0;
+
+       if (sms_encode_address_field(in, FALSE, pdu, &offset) == FALSE)
+               return FALSE;
+
+       if (encode_hex_own_buf(pdu, offset, 0, straddr) == NULL)
+               return FALSE;
+
+       straddr[offset * 2 + 1] = '\0';
+
+       return TRUE;
+}
+
+static void sms_assembly_load(struct sms_assembly *assembly,
+                               const struct dirent *dir)
+{
+       struct sms_address addr;
+       DECLARE_SMS_ADDR_STR(straddr);
+       guint16 ref;
+       guint8 max;
+       guint8 seq;
+       char *path;
+       int len;
+       struct stat segment_stat;
+       struct dirent **segments;
+       char *endp;
+       int r;
+       int i;
+       unsigned char buf[177];
+       struct sms segment;
+
+       if (dir->d_type != DT_DIR)
+               return;
+
+       /* Max of SMS address size is 12 bytes, hex encoded */
+       if (sscanf(dir->d_name, SMS_ADDR_FMT "-%hi-%hhi",
+                               straddr, &ref, &max) < 3)
+               return;
+
+       if (sms_assembly_extract_address(straddr, &addr) == FALSE)
+               return;
+
+       path = g_strdup_printf(SMS_BACKUP_PATH "/%s",
+                       assembly->imsi, dir->d_name);
+       len = scandir(path, &segments, NULL, versionsort);
+       g_free(path);
+
+       if (len < 0)
+               return;
+
+       for (i = 0; i < len; i++) {
+               if (segments[i]->d_type != DT_REG)
+                       continue;
+
+               seq = strtol(segments[i]->d_name, &endp, 10);
+               if (*endp != '\0')
+                       continue;
+
+               r = read_file(buf, sizeof(buf), SMS_BACKUP_PATH "/%s/%s",
+                               assembly->imsi,
+                               dir->d_name, segments[i]->d_name);
+               if (r < 0)
+                       continue;
+
+               if (!sms_deserialize(buf, &segment, r))
+                       continue;
+
+               path = g_strdup_printf(SMS_BACKUP_PATH "/%s/%s",
+                               assembly->imsi,
+                               dir->d_name, segments[i]->d_name);
+               r = stat(path, &segment_stat);
+               g_free(path);
+
+               if (r != 0)
+                       continue;
+
+               /* Errors cannot occur here */
+               sms_assembly_add_fragment_backup(assembly, &segment,
+                                               segment_stat.st_mtime,
+                                               &addr, ref, max, seq, FALSE);
+       }
+
+       for (i = 0; i < len; i++)
+               free(segments[i]);
+
+       free(segments);
+}
+
+static gboolean sms_assembly_store(struct sms_assembly *assembly,
+                               struct sms_assembly_node *node,
+                               const struct sms *sms, guint8 seq)
+{
+       unsigned char buf[177];
+       int len;
+       DECLARE_SMS_ADDR_STR(straddr);
+
+       if (assembly->imsi == NULL)
+               return FALSE;
+
+       if (sms_address_to_hex_string(&node->addr, straddr) == FALSE)
+               return FALSE;
+
+       len = sms_serialize(buf, sms);
+
+       if (write_file(buf, len, SMS_BACKUP_MODE,
+                               SMS_BACKUP_PATH_FILE, assembly->imsi, straddr,
+                               node->ref, node->max_fragments, seq) != len)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void sms_assembly_backup_free(struct sms_assembly *assembly,
+                                       struct sms_assembly_node *node)
+{
+       char *path;
+       int seq;
+       DECLARE_SMS_ADDR_STR(straddr);
+
+       if (assembly->imsi == NULL)
+               return;
+
+       if (sms_address_to_hex_string(&node->addr, straddr) == FALSE)
+               return;
+
+       for (seq = 0; seq < node->max_fragments; seq++) {
+               int offset = seq / 32;
+               int bit = 1 << (seq % 32);
+
+               if (node->bitmap[offset] & bit) {
+                       path = g_strdup_printf(SMS_BACKUP_PATH_FILE,
+                                       assembly->imsi, straddr,
+                                       node->ref, node->max_fragments, seq);
+                       unlink(path);
+                       g_free(path);
+               }
+       }
+
+       path = g_strdup_printf(SMS_BACKUP_PATH_DIR, assembly->imsi, straddr,
+                               node->ref, node->max_fragments);
+       rmdir(path);
+       g_free(path);
+}
+
+struct sms_assembly *sms_assembly_new(const char *imsi)
+{
+       struct sms_assembly *ret = g_new0(struct sms_assembly, 1);
+       char *path;
+       struct dirent **entries;
+       int len;
+
+       if (imsi) {
+               ret->imsi = imsi;
+
+               /* Restore state from backup */
+
+               path = g_strdup_printf(SMS_BACKUP_PATH, imsi);
+               len = scandir(path, &entries, NULL, alphasort);
+               g_free(path);
+
+               if (len < 0)
+                       return ret;
+
+               while (len--) {
+                       sms_assembly_load(ret, entries[len]);
+                       free(entries[len]);
+               }
+
+               free(entries);
+       }
+
+       return ret;
+}
+
+void sms_assembly_free(struct sms_assembly *assembly)
+{
+       GSList *l;
+
+       for (l = assembly->assembly_list; l; l = l->next) {
+               struct sms_assembly_node *node = l->data;
+
+               g_slist_foreach(node->fragment_list, (GFunc) g_free, 0);
+               g_slist_free(node->fragment_list);
+               g_free(node);
+       }
+
+       g_slist_free(assembly->assembly_list);
+       g_free(assembly);
+}
+
+GSList *sms_assembly_add_fragment(struct sms_assembly *assembly,
+                                       const struct sms *sms, time_t ts,
+                                       const struct sms_address *addr,
+                                       guint16 ref, guint8 max, guint8 seq)
+{
+       return sms_assembly_add_fragment_backup(assembly, sms,
+                                               ts, addr, ref, max, seq, TRUE);
+}
+
+static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly,
+                                       const struct sms *sms, time_t ts,
+                                       const struct sms_address *addr,
+                                       guint16 ref, guint8 max, guint8 seq,
+                                       gboolean backup)
+{
+       unsigned int offset = seq / 32;
+       unsigned int bit = 1 << (seq % 32);
+       GSList *l;
+       GSList *prev;
+       struct sms *newsms;
+       struct sms_assembly_node *node;
+       GSList *completed;
+       unsigned int position;
+       unsigned int i;
+       unsigned int j;
+
+       prev = NULL;
+
+       for (l = assembly->assembly_list; l; prev = l, l = l->next) {
+               node = l->data;
+
+               if (node->addr.number_type != addr->number_type)
+                       continue;
+
+               if (node->addr.numbering_plan != addr->numbering_plan)
+                       continue;
+
+               if (strcmp(node->addr.address, addr->address))
+                       continue;
+
+               if (ref != node->ref)
+                       continue;
+
+               /*
+                * Message Reference and address the same, but max is not
+                * ignore the SMS completely
+                */
+               if (max != node->max_fragments)
+                       return NULL;
+
+               /* Now check if we already have this seq number */
+               if (node->bitmap[offset] & bit)
+                       return NULL;
+
+               /*
+                * Iterate over the bitmap to find in which position
+                * should the fragment be inserted -- basically we
+                * walk each bit in the bitmap until the bit we care
+                * about (offset:bit) and count which are stored --
+                * that gives us in which position we have to insert.
+                */
+               position = 0;
+               for (i = 0; i < offset; i++)
+                       for (j = 0; j < 32; j++)
+                               if (node->bitmap[i] & (1 << j))
+                                       position += 1;
+
+               for (j = 1; j < bit; j = j << 1)
+                       if (node->bitmap[offset] & j)
+                               position += 1;
+
+               goto out;
+       }
+
+       node = g_new0(struct sms_assembly_node, 1);
+       memcpy(&node->addr, addr, sizeof(struct sms_address));
+       node->ts = ts;
+       node->ref = ref;
+       node->max_fragments = max;
+
+       assembly->assembly_list = g_slist_prepend(assembly->assembly_list,
+                                                       node);
+
+       prev = NULL;
+       l = assembly->assembly_list;
+       position = 0;
+
+out:
+       newsms = g_new(struct sms, 1);
+
+       memcpy(newsms, sms, sizeof(struct sms));
+       node->fragment_list = g_slist_insert(node->fragment_list,
+                                               newsms, position);
+       node->bitmap[offset] |= bit;
+       node->num_fragments += 1;
+
+       if (node->num_fragments < node->max_fragments) {
+               if (backup)
+                       sms_assembly_store(assembly, node, sms, seq);
+
+               return NULL;
+       }
+
+       completed = node->fragment_list;
+
+       sms_assembly_backup_free(assembly, node);
+
+       if (prev)
+               prev->next = l->next;
+       else
+               assembly->assembly_list = l->next;
+
+       g_free(node);
+       g_slist_free_1(l);
+       return completed;
+}
+
+/*!
+ * Expires all incomplete messages that have been received at time prior
+ * to one given by before argument.  The fragment list is freed and the
+ * SMSes are vaporized.
+ */
+void sms_assembly_expire(struct sms_assembly *assembly, time_t before)
+{
+       GSList *cur;
+       GSList *prev;
+       GSList *tmp;
+
+       prev = NULL;
+       cur = assembly->assembly_list;
+
+       while (cur) {
+               struct sms_assembly_node *node = cur->data;
+
+               if (node->ts > before) {
+                       prev = cur;
+                       cur = cur->next;
+                       continue;
+               }
+
+               sms_assembly_backup_free(assembly, node);
+
+               g_slist_foreach(node->fragment_list, (GFunc) g_free, 0);
+               g_slist_free(node->fragment_list);
+               g_free(node);
+
+               if (prev)
+                       prev->next = cur->next;
+               else
+                       assembly->assembly_list = cur->next;
+
+               tmp = cur;
+               cur = cur->next;
+               g_slist_free_1(tmp);
+       }
+}
+
+static gboolean sha1_equal(gconstpointer v1, gconstpointer v2)
+{
+       return memcmp(v1, v2, SMS_MSGID_LEN) == 0;
+}
+
+static guint sha1_hash(gconstpointer v)
+{
+       guint h;
+
+       memcpy(&h, v, sizeof(h));
+
+       return h;
+}
+
+static void sr_assembly_load_backup(GHashTable *assembly_table,
+                                       const char *imsi,
+                                       const struct dirent *addr_dir)
+{
+       struct sms_address addr;
+       DECLARE_SMS_ADDR_STR(straddr);
+       struct id_table_node *node;
+       GHashTable *id_table;
+       int r;
+       char *assembly_table_key;
+       unsigned int *id_table_key;
+       char msgid_str[SMS_MSGID_LEN * 2 + 1];
+       unsigned char msgid[SMS_MSGID_LEN];
+       char endc;
+
+       if (addr_dir->d_type != DT_REG)
+               return;
+
+       /*
+        * All SMS-messages under the same IMSI-code are
+        * included in the same directory.
+        * So, SMS-address and message ID are included in the same file name
+        * Max of SMS address size is 12 bytes, hex encoded
+        * Max of SMS SHA1 hash is 20 bytes, hex encoded
+        */
+       if (sscanf(addr_dir->d_name, SMS_ADDR_FMT "-" SMS_MSGID_FMT "%c",
+                               straddr, msgid_str, &endc) != 2)
+               return;
+
+       if (sms_assembly_extract_address(straddr, &addr) == FALSE)
+               return;
+
+       if (strlen(msgid_str) != 2 * SMS_MSGID_LEN)
+               return;
+
+       if (decode_hex_own_buf(msgid_str, 2 * SMS_MSGID_LEN,
+                               NULL, 0, msgid) == NULL)
+               return;
+
+       node = g_new0(struct id_table_node, 1);
+
+       r = read_file((unsigned char *) node,
+                       sizeof(struct id_table_node),
+                       SMS_SR_BACKUP_PATH "/%s",
+                       imsi, addr_dir->d_name);
+
+       if (r < 0) {
+               g_free(node);
+               return;
+       }
+
+       id_table = g_hash_table_lookup(assembly_table,
+                                       sms_address_to_string(&addr));
+
+       /* Create hashtable keyed by the to address if required */
+       if (id_table == NULL) {
+               id_table = g_hash_table_new_full(sha1_hash, sha1_equal,
+                                                       g_free, g_free);
+
+               assembly_table_key = g_strdup(sms_address_to_string(&addr));
+               g_hash_table_insert(assembly_table, assembly_table_key,
+                                       id_table);
+       }
+
+       /* Node ready, create key and add them to the table */
+       id_table_key = g_memdup(msgid, SMS_MSGID_LEN);
+
+       g_hash_table_insert(id_table, id_table_key, node);
+}
+
+struct status_report_assembly *status_report_assembly_new(const char *imsi)
+{
+       char *path;
+       int len;
+       struct dirent **addresses;
+       struct status_report_assembly *ret =
+                               g_new0(struct status_report_assembly, 1);
+
+       ret->assembly_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                               g_free, (GDestroyNotify) g_hash_table_destroy);
+
+       if (imsi) {
+               ret->imsi = imsi;
+
+               /* Restore state from backup */
+               path = g_strdup_printf(SMS_SR_BACKUP_PATH, imsi);
+               len = scandir(path, &addresses, NULL, alphasort);
+
+               g_free(path);
+
+               if (len < 0)
+                       return ret;
+
+               /*
+                * Go through different addresses. Each address can relate to
+                * 1-n msg_ids.
+                */
+
+               while (len--) {
+                       sr_assembly_load_backup(ret->assembly_table, imsi,
+                                                               addresses[len]);
+                       g_free(addresses[len]);
+               }
+
+               g_free(addresses);
+       }
+
+       return ret;
+}
+
+static gboolean sr_assembly_add_fragment_backup(const char *imsi,
+                                       const struct id_table_node *node,
+                                       const struct sms_address *addr,
+                                       const unsigned char *msgid)
+{
+       int len = sizeof(struct id_table_node);
+       DECLARE_SMS_ADDR_STR(straddr);
+       char msgid_str[SMS_MSGID_LEN * 2 + 1];
+
+       if (imsi == NULL)
+               return FALSE;
+
+       if (sms_address_to_hex_string(addr, straddr) == FALSE)
+               return FALSE;
+
+       if (encode_hex_own_buf(msgid, SMS_MSGID_LEN, 0, msgid_str) == NULL)
+               return FALSE;
+
+       /* storagedir/%s/sms_sr/%s-%s */
+       if (write_file((unsigned char *) node, len, SMS_BACKUP_MODE,
+                       SMS_SR_BACKUP_PATH_FILE, imsi,
+                       straddr, msgid_str) != len)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean sr_assembly_remove_fragment_backup(const char *imsi,
+                                       const struct sms_address *addr,
+                                       const unsigned char *sha1)
+{
+       char *path;
+       DECLARE_SMS_ADDR_STR(straddr);
+       char msgid_str[SMS_MSGID_LEN * 2 + 1];
+
+       if (imsi == NULL)
+               return FALSE;
+
+       if (sms_address_to_hex_string(addr, straddr) == FALSE)
+               return FALSE;
+
+       if (encode_hex_own_buf(sha1, SMS_MSGID_LEN, 0, msgid_str) == FALSE)
+               return FALSE;
+
+       path = g_strdup_printf(SMS_SR_BACKUP_PATH_FILE,
+                                       imsi, straddr, msgid_str);
+
+       unlink(path);
+       g_free(path);
+
+       return TRUE;
+}
+
+void status_report_assembly_free(struct status_report_assembly *assembly)
+{
+       g_hash_table_destroy(assembly->assembly_table);
+       g_free(assembly);
+}
+
+static gboolean sr_st_to_delivered(enum sms_st st, gboolean *delivered)
+{
+       if (st >= SMS_ST_TEMPFINAL_CONGESTION && st <= SMS_ST_TEMPFINAL_LAST)
+               return FALSE;
+
+       if (st >= SMS_ST_TEMPORARY_CONGESTION && st <= SMS_ST_TEMPORARY_LAST)
+               return FALSE;
+
+       if (st <= SMS_ST_COMPLETED_LAST) {
+               *delivered = TRUE;
+               return TRUE;
+       }
+
+       if (st >= SMS_ST_PERMANENT_RP_ERROR && st <= SMS_ST_PERMANENT_LAST) {
+               *delivered = FALSE;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static struct id_table_node *find_by_mr_and_mark(GHashTable *id_table,
+                                               unsigned char mr,
+                                               GHashTableIter *out_iter,
+                                               unsigned char **out_id)
+{
+       unsigned int offset = mr / 32;
+       unsigned int bit = 1 << (mr % 32);
+       gpointer key, value;
+       struct id_table_node *node;
+
+       g_hash_table_iter_init(out_iter, id_table);
+       while (g_hash_table_iter_next(out_iter, &key, &value)) {
+               node = value;
+
+               /* Address and MR matched */
+               if (node->mrs[offset] & bit) {
+                       node->mrs[offset] ^= bit;
+                       *out_id = key;
+
+                       return node;
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * Key (receiver address) does not exist in assembly. Some networks can change
+ * address to international format, although address is sent in the national
+ * format. Handle also change from national to international format.
+ * Notify these special cases by comparing only last six digits of the assembly
+ * addresses and received address. If address contains less than six digits,
+ * compare only existing digits.
+ */
+static struct id_table_node *fuzzy_lookup(struct status_report_assembly *assy,
+                                               const struct sms *sr,
+                                               const char **out_addr,
+                                               GHashTableIter *out_iter,
+                                               unsigned char **out_msgid)
+{
+       GHashTableIter iter_addr;
+       gpointer key, value;
+       const char *r_addr;
+
+       r_addr = sms_address_to_string(&sr->status_report.raddr);
+       g_hash_table_iter_init(&iter_addr, assy->assembly_table);
+
+       while (g_hash_table_iter_next(&iter_addr, &key, &value)) {
+               const char *s_addr = key;
+               GHashTable *id_table = value;
+               unsigned int len, r_len, s_len;
+               unsigned int i;
+               struct id_table_node *node;
+
+               if (r_addr[0] == '+' && s_addr[0] == '+')
+                       continue;
+
+               if (r_addr[0] != '+' && s_addr[0] != '+')
+                       continue;
+
+               r_len = strlen(r_addr);
+               s_len = strlen(s_addr);
+
+               len = MIN(6, MIN(r_len, s_len));
+
+               for (i = 0; i < len; i++)
+                       if (s_addr[s_len - i - 1] != r_addr[r_len - i - 1])
+                               break;
+
+               /* Not all digits matched. */
+               if (i < len)
+                       continue;
+
+               /* Address matched. Check message reference. */
+               node = find_by_mr_and_mark(id_table, sr->status_report.mr,
+                                               out_iter, out_msgid);
+               if (node != NULL) {
+                       *out_addr = s_addr;
+                       return node;
+               }
+       }
+
+       return NULL;
+}
+
+gboolean status_report_assembly_report(struct status_report_assembly *assembly,
+                                       const struct sms *sr,
+                                       unsigned char *out_msgid,
+                                       gboolean *out_delivered)
+{
+       const char *straddr;
+       GHashTable *id_table;
+       GHashTableIter iter;
+       struct sms_address addr;
+       struct id_table_node *node;
+       gboolean delivered;
+       gboolean pending;
+       unsigned char *msgid;
+       int i;
+
+       /* We ignore temporary or tempfinal status reports */
+       if (sr_st_to_delivered(sr->status_report.st, &delivered) == FALSE)
+               return FALSE;
+
+       straddr = sms_address_to_string(&sr->status_report.raddr);
+       id_table = g_hash_table_lookup(assembly->assembly_table, straddr);
+
+       if (id_table != NULL)
+               node = find_by_mr_and_mark(id_table, sr->status_report.mr,
+                                               &iter, &msgid);
+       else
+               node = fuzzy_lookup(assembly, sr, &straddr, &iter, &msgid);
+
+       /* Unable to find a message reference belonging to this address */
+       if (node == NULL)
+               return FALSE;
+
+       node->deliverable = node->deliverable && delivered;
+
+       /* If we haven't sent the entire message yet, wait until sent */
+       if (node->sent_mrs < node->total_mrs)
+               return FALSE;
+
+       /* Figure out if we are expecting more status reports */
+       for (i = 0, pending = FALSE; i < 8; i++) {
+               /* There are still pending mr(s). */
+               if (node->mrs[i] != 0) {
+                       pending = TRUE;
+                       break;
+               }
+       }
+
+       sms_address_from_string(&addr, straddr);
+
+       if (pending == TRUE && node->deliverable == TRUE) {
+               /*
+                * More status reports expected, and already received
+                * reports completed. Update backup file.
+                */
+               sr_assembly_add_fragment_backup(assembly->imsi, node,
+                                               &addr, msgid);
+
+               return FALSE;
+       }
+
+       if (out_delivered)
+               *out_delivered = node->deliverable;
+
+       if (out_msgid)
+               memcpy(out_msgid, msgid, SMS_MSGID_LEN);
+
+       sr_assembly_remove_fragment_backup(assembly->imsi, &addr, msgid);
+       id_table = g_hash_table_iter_get_hash_table(&iter);
+       g_hash_table_iter_remove(&iter);
+
+       if (g_hash_table_size(id_table) == 0)
+               g_hash_table_remove(assembly->assembly_table, straddr);
+
+       return TRUE;
+}
+
+void status_report_assembly_add_fragment(
+                                       struct status_report_assembly *assembly,
+                                       const unsigned char *msgid,
+                                       const struct sms_address *to,
+                                       unsigned char mr, time_t expiration,
+                                       unsigned char total_mrs)
+{
+       unsigned int offset = mr / 32;
+       unsigned int bit = 1 << (mr % 32);
+       GHashTable *id_table;
+       struct id_table_node *node;
+       unsigned char *id_table_key;
+
+       id_table = g_hash_table_lookup(assembly->assembly_table,
+                                       sms_address_to_string(to));
+
+       /* Create hashtable keyed by the to address if required */
+       if (id_table == NULL) {
+               id_table = g_hash_table_new_full(sha1_hash, sha1_equal,
+                                                               g_free, g_free);
+               g_hash_table_insert(assembly->assembly_table,
+                                       g_strdup(sms_address_to_string(to)),
+                                       id_table);
+       }
+
+       node = g_hash_table_lookup(id_table, msgid);
+
+       /* Create node in the message id hashtable if required */
+       if (node == NULL) {
+               id_table_key = g_memdup(msgid, SMS_MSGID_LEN);
+
+               node = g_new0(struct id_table_node, 1);
+               node->total_mrs = total_mrs;
+               node->deliverable = TRUE;
+
+               g_hash_table_insert(id_table, id_table_key, node);
+       }
+
+       /* id_table and node both exists */
+       node->mrs[offset] |= bit;
+       node->expiration = expiration;
+       node->sent_mrs++;
+       sr_assembly_add_fragment_backup(assembly->imsi, node, to, msgid);
+}
+
+void status_report_assembly_expire(struct status_report_assembly *assembly,
+                                       time_t before)
+{
+       GHashTable *id_table;
+       GHashTableIter iter_addr, iter_node;
+       struct sms_address addr;
+       char *straddr;
+       gpointer key;
+       struct id_table_node *node;
+
+       g_hash_table_iter_init(&iter_addr, assembly->assembly_table);
+
+       /*
+        * Go through different addresses. Each address can relate to
+        * 1-n msg_ids.
+        */
+       while (g_hash_table_iter_next(&iter_addr, (gpointer) &straddr,
+                                       (gpointer) &id_table)) {
+
+               sms_address_from_string(&addr, straddr);
+               g_hash_table_iter_init(&iter_node, id_table);
+
+               /* Go through different messages. */
+               while (g_hash_table_iter_next(&iter_node, &key,
+                                               (gpointer) &node)) {
+                       /*
+                        * If message is expired, removed it from the
+                        * hash-table and remove the backup-file
+                        */
+                       if (node->expiration <= before) {
+                               g_hash_table_iter_remove(&iter_node);
+
+                               sr_assembly_remove_fragment_backup(
+                                                               assembly->imsi,
+                                                               &addr,
+                                                               key);
+                       }
+               }
+
+               /*
+                * If all messages are removed, remove address
+                * from the hash-table.
+                */
+               if (g_hash_table_size(id_table) == 0)
+                       g_hash_table_iter_remove(&iter_addr);
+       }
+}
+
+static int sms_tx_load_filter(const struct dirent *dent)
+{
+       char *endp;
+       guint8 seq __attribute__ ((unused));
+
+       if (dent->d_type != DT_REG)
+               return 0;
+
+       seq = strtol(dent->d_name, &endp, 10);
+       if (*endp != '\0')
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Each directory contains a file per pdu.
+ */
+static GSList *sms_tx_load(const char *imsi, const struct dirent *dir)
+{
+       GSList *list = NULL;
+       struct dirent **pdus;
+       char *path;
+       int len, r;
+       unsigned char buf[177];
+       struct sms s;
+
+       if (dir->d_type != DT_DIR)
+               return NULL;
+
+       path = g_strdup_printf(SMS_TX_BACKUP_PATH "/%s", imsi, dir->d_name);
+       len = scandir(path, &pdus, sms_tx_load_filter, versionsort);
+       g_free(path);
+
+       if (len < 0)
+               return NULL;
+
+       while (len--) {
+               r = read_file(buf, sizeof(buf), SMS_TX_BACKUP_PATH "/%s/%s",
+                                       imsi, dir->d_name, pdus[len]->d_name);
+
+               if (r < 0)
+                       goto free_pdu;
+
+               if (sms_deserialize_outgoing(buf, &s, r) == FALSE)
+                       goto free_pdu;
+
+               list = g_slist_prepend(list, g_memdup(&s, sizeof(s)));
+
+free_pdu:
+               g_free(pdus[len]);
+       }
+
+       g_free(pdus);
+
+       return list;
+}
+
+static int sms_tx_queue_filter(const struct dirent *dirent)
+{
+       if (dirent->d_type != DT_DIR)
+               return 0;
+
+       if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))
+               return 0;
+
+       return 1;
+}
+
+/*
+ * populate the queue with tx_backup_entry from stored backup
+ * data.
+ */
+GQueue *sms_tx_queue_load(const char *imsi)
+{
+       GQueue *retq = 0;
+       char *path;
+       struct dirent **entries;
+       int len;
+       int i;
+       unsigned long id;
+
+       if (imsi == NULL)
+               return NULL;
+
+       path = g_strdup_printf(SMS_TX_BACKUP_PATH, imsi);
+
+       len = scandir(path, &entries, sms_tx_queue_filter, versionsort);
+       if (len < 0)
+               goto nodir_exit;
+
+       retq = g_queue_new();
+
+       for (i = 0, id = 0; i < len; i++) {
+               char uuid[SMS_MSGID_LEN * 2 + 1];
+               GSList *msg_list;
+               unsigned long oldid;
+               unsigned long flags;
+               char *oldpath, *newpath;
+               struct txq_backup_entry *entry;
+               struct dirent *dir = entries[i];
+               char endc;
+
+               if (sscanf(dir->d_name, "%lu-%lu-" SMS_MSGID_FMT "%c",
+                                       &oldid, &flags, uuid, &endc) != 3)
+                       continue;
+
+               if (strlen(uuid) !=  2 * SMS_MSGID_LEN)
+                       continue;
+
+               msg_list = sms_tx_load(imsi, dir);
+               if (msg_list == NULL)
+                       continue;
+
+               entry = g_new0(struct txq_backup_entry, 1);
+               entry->msg_list = msg_list;
+               entry->flags = flags;
+               decode_hex_own_buf(uuid, -1, NULL, 0, entry->uuid);
+
+               g_queue_push_tail(retq, entry);
+
+               /* Don't bother re-shuffling the ids if they are the same */
+               if (oldid == id) {
+                       id++;
+                       continue;
+               }
+
+               oldpath = g_strdup_printf("%s/%s", path, dir->d_name);
+               newpath = g_strdup_printf(SMS_TX_BACKUP_PATH_DIR,
+                                               imsi, id++, flags, uuid);
+
+               /* rename directory to reflect new position in queue */
+               rename(oldpath, newpath);
+
+               g_free(newpath);
+               g_free(oldpath);
+       }
+
+       for (i = 0; i < len; i++)
+               g_free(entries[i]);
+
+       g_free(entries);
+
+nodir_exit:
+       g_free(path);
+       return retq;
+}
+
+gboolean sms_tx_backup_store(const char *imsi, unsigned long id,
+                               unsigned long flags, const char *uuid,
+                               guint8 seq, const unsigned char *pdu,
+                               int pdu_len, int tpdu_len)
+{
+       unsigned char buf[177];
+       int len;
+
+       if (!imsi)
+               return FALSE;
+
+       memcpy(buf + 1, pdu, pdu_len);
+       buf[0] = tpdu_len;
+       len = pdu_len + 1;
+
+       /*
+        * file name is: imsi/tx_queue/order-flags-uuid/pdu
+        */
+       if (write_file(buf, len, SMS_BACKUP_MODE, SMS_TX_BACKUP_PATH_FILE,
+                                       imsi, id, flags, uuid, seq) != len)
+               return FALSE;
+
+       return TRUE;
+}
+
+void sms_tx_backup_free(const char *imsi, unsigned long id,
+                               unsigned long flags, const char *uuid)
+{
+       char *path;
+       struct dirent **entries;
+       int len;
+
+       path = g_strdup_printf(SMS_TX_BACKUP_PATH_DIR,
+                                       imsi, id, flags, uuid);
+
+       len = scandir(path, &entries, NULL, versionsort);
+
+       if (len < 0)
+               goto nodir_exit;
+
+       /* skip '..' and '.' entries */
+       while (len-- > 2) {
+               struct dirent *dir = entries[len];
+               char *file = g_strdup_printf("%s/%s", path, dir->d_name);
+
+               unlink(file);
+               g_free(file);
+
+               g_free(entries[len]);
+       }
+
+       g_free(entries[1]);
+       g_free(entries[0]);
+       g_free(entries);
+
+       rmdir(path);
+
+nodir_exit:
+       g_free(path);
+}
+
+void sms_tx_backup_remove(const char *imsi, unsigned long id,
+                               unsigned long flags, const char *uuid,
+                               guint8 seq)
+{
+       char *path;
+
+       path = g_strdup_printf(SMS_TX_BACKUP_PATH_FILE,
+                                       imsi, id, flags, uuid, seq);
+       unlink(path);
+
+       g_free(path);
+}
+
+static inline GSList *sms_list_append(GSList *l, const struct sms *in)
+{
+       struct sms *sms;
+
+       sms = g_new(struct sms, 1);
+       memcpy(sms, in, sizeof(struct sms));
+       l = g_slist_prepend(l, sms);
+
+       return l;
+}
+
+/*
+ * Prepares a datagram for transmission.  Breaks up into fragments if
+ * necessary using ref as the concatenated message reference number.
+ * Returns a list of sms messages in order.
+ *
+ * @use_delivery_reports: value for the Status-Report-Request field
+ *     (23.040 3.2.9, 9.2.2.2)
+ */
+GSList *sms_datagram_prepare(const char *to,
+                               const unsigned char *data, unsigned int len,
+                               guint16 ref, gboolean use_16bit_ref,
+                               unsigned short src, unsigned short dst,
+                               gboolean use_16bit_port,
+                               gboolean use_delivery_reports)
+{
+       struct sms template;
+       unsigned int offset;
+       unsigned int written;
+       unsigned int left;
+       guint8 seq;
+       GSList *r = NULL;
+
+       memset(&template, 0, sizeof(struct sms));
+       template.type = SMS_TYPE_SUBMIT;
+       template.submit.rd = FALSE;
+       template.submit.vpf = SMS_VALIDITY_PERIOD_FORMAT_RELATIVE;
+       template.submit.rp = FALSE;
+       template.submit.srr = use_delivery_reports;
+       template.submit.mr = 0;
+       template.submit.vp.relative = 0xA7; /* 24 Hours */
+       template.submit.dcs = 0x04; /* Class Unspecified, 8 Bit */
+       template.submit.udhi = TRUE;
+       sms_address_from_string(&template.submit.daddr, to);
+
+       offset = 1;
+
+       if (use_16bit_port) {
+               template.submit.ud[0] += 6;
+               template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_16BIT;
+               template.submit.ud[offset + 1] = 4;
+               template.submit.ud[offset + 2] = (dst & 0xff00) >> 8;
+               template.submit.ud[offset + 3] = dst & 0xff;
+               template.submit.ud[offset + 4] = (src & 0xff00) >> 8;
+               template.submit.ud[offset + 5] = src & 0xff;
+
+               offset += 6;
+       } else {
+               template.submit.ud[0] += 4;
+               template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_8BIT;
+               template.submit.ud[offset + 1] = 2;
+               template.submit.ud[offset + 2] = dst & 0xff;
+               template.submit.ud[offset + 3] = src & 0xff;
+
+               offset += 4;
+       }
+
+       if (len <= (140 - offset)) {
+               template.submit.udl = len + offset;
+               memcpy(template.submit.ud + offset, data, len);
+
+               return sms_list_append(NULL, &template);
+       }
+
+       if (use_16bit_ref) {
+               template.submit.ud[0] += 6;
+               template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT;
+               template.submit.ud[offset + 1] = 4;
+               template.submit.ud[offset + 2] = (ref & 0xff00) >> 8;
+               template.submit.ud[offset + 3] = ref & 0xff;
+
+               offset += 6;
+       } else {
+               template.submit.ud[0] += 5;
+               template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT;
+               template.submit.ud[offset + 1] = 3;
+               template.submit.ud[offset + 2] = ref & 0xff;
+
+               offset += 5;
+       }
+
+       seq = 0;
+       left = len;
+       written = 0;
+
+       while (left > 0) {
+               unsigned int chunk;
+
+               seq += 1;
+
+               chunk = 140 - offset;
+               if (left < chunk)
+                       chunk = left;
+
+               template.submit.udl = chunk + offset;
+               memcpy(template.submit.ud + offset, data + written, chunk);
+
+               written += chunk;
+               left -= chunk;
+
+               template.submit.ud[offset - 1] = seq;
+
+               r = sms_list_append(r, &template);
+
+               if (seq == 255)
+                       break;
+       }
+
+       if (left > 0) {
+               g_slist_foreach(r, (GFunc) g_free, NULL);
+               g_slist_free(r);
+
+               return NULL;
+       } else {
+               GSList *l;
+
+               for (l = r; l; l = l->next) {
+                       struct sms *sms = l->data;
+
+                       sms->submit.ud[offset - 2] = seq;
+               }
+       }
+
+       r = g_slist_reverse(r);
+
+       return r;
+}
+
+/*
+ * Prepares the text for transmission.  Breaks up into fragments if
+ * necessary using ref as the concatenated message reference number.
+ * Returns a list of sms messages in order.
+ *
+ * @use_delivery_reports: value for the Status-Report-Request field
+ *     (23.040 3.2.9, 9.2.2.2)
+ */
+GSList *sms_text_prepare_with_alphabet(const char *to, const char *utf8,
+                                       guint16 ref, gboolean use_16bit,
+                                       gboolean use_delivery_reports,
+                                       enum sms_alphabet alphabet)
+{
+       struct sms template;
+       int offset = 0;
+       unsigned char *gsm_encoded = NULL;
+       char *ucs2_encoded = NULL;
+       long written;
+       long left;
+       guint8 seq;
+       GSList *r = NULL;
+       enum gsm_dialect used_locking;
+       enum gsm_dialect used_single;
+
+       memset(&template, 0, sizeof(struct sms));
+       template.type = SMS_TYPE_SUBMIT;
+       template.submit.rd = FALSE;
+       template.submit.vpf = SMS_VALIDITY_PERIOD_FORMAT_RELATIVE;
+       template.submit.rp = FALSE;
+       template.submit.srr = use_delivery_reports;
+       template.submit.mr = 0;
+       template.submit.vp.relative = 0xA7; /* 24 Hours */
+       sms_address_from_string(&template.submit.daddr, to);
+
+       /*
+        * UDHI, UDL, UD and DCS actually depend on the contents of
+        * the text, and also on the GSM dialect we use to encode it.
+        */
+       gsm_encoded = convert_utf8_to_gsm_best_lang(utf8, -1, NULL, &written, 0,
+                                                       alphabet, &used_locking,
+                                                       &used_single);
+       if (gsm_encoded == NULL) {
+               gsize converted;
+
+               ucs2_encoded = g_convert(utf8, -1, "UCS-2BE//TRANSLIT", "UTF-8",
+                                               NULL, &converted, NULL);
+               written = converted;
+       }
+
+       if (gsm_encoded == NULL && ucs2_encoded == NULL)
+               return NULL;
+
+       if (gsm_encoded != NULL)
+               template.submit.dcs = 0x00; /* Class Unspecified, 7 Bit */
+       else
+               template.submit.dcs = 0x08; /* Class Unspecified, UCS2 */
+
+       if (gsm_encoded != NULL && used_single != GSM_DIALECT_DEFAULT) {
+               if (!offset)
+                       offset = 1;
+
+               template.submit.ud[0] += 3;
+               template.submit.ud[offset] = SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT;
+               template.submit.ud[offset + 1] = 1;
+               template.submit.ud[offset + 2] = used_single;
+               offset += 3;
+       }
+
+       if (gsm_encoded != NULL && used_locking != GSM_DIALECT_DEFAULT) {
+               if (!offset)
+                       offset = 1;
+
+               template.submit.ud[0] += 3;
+               template.submit.ud[offset] = SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT;
+               template.submit.ud[offset + 1] = 1;
+               template.submit.ud[offset + 2] = used_locking;
+               offset += 3;
+       }
+
+       if (offset != 0)
+               template.submit.udhi = TRUE;
+
+       if (gsm_encoded && (written <= sms_text_capacity_gsm(160, offset))) {
+               template.submit.udl = written + (offset * 8 + 6) / 7;
+               pack_7bit_own_buf(gsm_encoded, written, offset, FALSE, NULL,
+                                       0, template.submit.ud + offset);
+
+               g_free(gsm_encoded);
+               return sms_list_append(NULL, &template);
+       }
+
+       if (ucs2_encoded && (written <= (140 - offset))) {
+               template.submit.udl = written + offset;
+               memcpy(template.submit.ud + offset, ucs2_encoded, written);
+
+               g_free(ucs2_encoded);
+               return sms_list_append(NULL, &template);
+       }
+
+       template.submit.udhi = TRUE;
+
+       if (!offset)
+               offset = 1;
+
+       if (use_16bit) {
+               template.submit.ud[0] += 6;
+               template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT;
+               template.submit.ud[offset + 1] = 4;
+               template.submit.ud[offset + 2] = (ref & 0xff00) >> 8;
+               template.submit.ud[offset + 3] = ref & 0xff;
+
+               offset += 6;
+       } else {
+               template.submit.ud[0] += 5;
+               template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT;
+               template.submit.ud[offset + 1] = 3;
+               template.submit.ud[offset + 2] = ref & 0xff;
+
+               offset += 5;
+       }
+
+       seq = 0;
+       left = written;
+       written = 0;
+
+       while (left > 0) {
+               long chunk;
+
+               seq += 1;
+
+               if (gsm_encoded) {
+                       chunk = sms_text_capacity_gsm(160, offset);
+
+                       if (left < chunk)
+                               chunk = left;
+
+                       if (gsm_encoded[written + chunk - 1] == 0x1b)
+                               chunk -= 1;
+
+                       template.submit.udl = chunk + (offset * 8 + 6) / 7;
+                       pack_7bit_own_buf(gsm_encoded + written, chunk,
+                                               offset, FALSE, NULL, 0,
+                                               template.submit.ud + offset);
+               } else {
+                       chunk = 140 - offset;
+                       chunk &= ~0x1;
+
+                       if (left < chunk)
+                               chunk = left;
+
+                       template.submit.udl = chunk + offset;
+                       memcpy(template.submit.ud + offset,
+                               ucs2_encoded + written, chunk);
+               }
+
+               written += chunk;
+               left -= chunk;
+
+               template.submit.ud[offset - 1] = seq;
+
+               r = sms_list_append(r, &template);
+
+               if (seq == 255)
+                       break;
+       }
+
+       if (gsm_encoded)
+               g_free(gsm_encoded);
+
+       if (ucs2_encoded)
+               g_free(ucs2_encoded);
+
+       if (left > 0) {
+               g_slist_foreach(r, (GFunc) g_free, NULL);
+               g_slist_free(r);
+
+               return NULL;
+       } else {
+               GSList *l;
+
+               for (l = r; l; l = l->next) {
+                       struct sms *sms = l->data;
+
+                       sms->submit.ud[offset - 2] = seq;
+               }
+       }
+
+       r = g_slist_reverse(r);
+
+       return r;
+}
+
+GSList *sms_text_prepare(const char *to, const char *utf8, guint16 ref,
+                               gboolean use_16bit,
+                               gboolean use_delivery_reports)
+{
+       return sms_text_prepare_with_alphabet(to, utf8, ref, use_16bit,
+                                               use_delivery_reports,
+                                               SMS_ALPHABET_DEFAULT);
+}
+
+gboolean cbs_dcs_decode(guint8 dcs, gboolean *udhi, enum sms_class *cls,
+                       enum sms_charset *charset, gboolean *compressed,
+                       enum cbs_language *language, gboolean *iso639)
+{
+       guint8 upper = (dcs & 0xf0) >> 4;
+       guint8 lower = dcs & 0xf;
+       enum sms_charset ch;
+       enum sms_class cl;
+       enum cbs_language lang = CBS_LANGUAGE_UNSPECIFIED;
+       gboolean iso = FALSE;
+       gboolean comp = FALSE;
+       gboolean udh = FALSE;
+
+       if (upper == 0x3 || upper == 0x8 || (upper >= 0xA && upper <= 0xE))
+               return FALSE;
+
+       switch (upper) {
+       case 0:
+               ch = SMS_CHARSET_7BIT;
+               cl = SMS_CLASS_UNSPECIFIED;
+               lang = (enum cbs_language) lower;
+               break;
+       case 1:
+               if (lower > 1)
+                       return FALSE;
+
+               if (lower == 0)
+                       ch = SMS_CHARSET_7BIT;
+               else
+                       ch = SMS_CHARSET_UCS2;
+
+               cl = SMS_CLASS_UNSPECIFIED;
+               iso = TRUE;
+
+               break;
+       case 2:
+               if (lower > 4)
+                       return FALSE;
+
+               ch = SMS_CHARSET_7BIT;
+               cl = SMS_CLASS_UNSPECIFIED;
+               lang = (enum cbs_language) dcs;
+               break;
+       case 4:
+       case 5:
+       case 6:
+       case 7:
+               comp = (dcs & 0x20) ? TRUE : FALSE;
+
+               if (dcs & 0x10)
+                       cl = (enum sms_class) (dcs & 0x03);
+               else
+                       cl = SMS_CLASS_UNSPECIFIED;
+
+               if (((dcs & 0x0c) >> 2) < 3)
+                       ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
+               else
+                       return FALSE;
+
+               break;
+       case 9:
+               udh = TRUE;
+               cl = (enum sms_class) (dcs & 0x03);
+               if (((dcs & 0x0c) >> 2) < 3)
+                       ch = (enum sms_charset) ((dcs & 0x0c) >> 2);
+               else
+                       return FALSE;
+
+               break;
+       case 15:
+               if (lower & 0x8)
+                       return FALSE;
+
+               if (lower & 0x4)
+                       ch = SMS_CHARSET_8BIT;
+               else
+                       ch = SMS_CHARSET_7BIT;
+
+               if (lower & 0x3)
+                       cl = (enum sms_class) (lower & 0x3);
+               else
+                       cl = SMS_CLASS_UNSPECIFIED;
+
+               break;
+       default:
+               return FALSE;
+       };
+
+       if (udhi)
+               *udhi = udh;
+
+       if (cls)
+               *cls = cl;
+
+       if (charset)
+               *charset = ch;
+
+       if (compressed)
+               *compressed = comp;
+
+       if (language)
+               *language = lang;
+
+       if (iso639)
+               *iso639 = iso;
+
+       return TRUE;
+}
+
+gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out)
+{
+       /* CBS is always a fixed length of 88 bytes */
+       if (len != 88)
+               return FALSE;
+
+       out->gs = (enum cbs_geo_scope) ((pdu[0] >> 6) & 0x03);
+       out->message_code = ((pdu[0] & 0x3f) << 4) | ((pdu[1] >> 4) & 0xf);
+       out->update_number = (pdu[1] & 0xf);
+       out->message_identifier = (pdu[2] << 8) | pdu[3];
+       out->dcs = pdu[4];
+       out->max_pages = pdu[5] & 0xf;
+       out->page = (pdu[5] >> 4) & 0xf;
+
+       /*
+        * If a mobile receives the code 0000 in either the first field or
+        * the second field then it shall treat the CBS message exactly the
+        * same as a CBS message with page parameter 0001 0001 (i.e. a single
+        * page message).
+        */
+       if (out->max_pages == 0 || out->page == 0) {
+               out->max_pages = 1;
+               out->page = 1;
+       }
+
+       memcpy(out->ud, pdu + 6, 82);
+
+       return TRUE;
+}
+
+gboolean cbs_encode(const struct cbs *cbs, int *len, unsigned char *pdu)
+{
+       pdu[0] = (cbs->gs << 6) | ((cbs->message_code >> 4) & 0x3f);
+       pdu[1] = ((cbs->message_code & 0xf) << 4) | cbs->update_number;
+       pdu[2] = cbs->message_identifier >> 8;
+       pdu[3] = cbs->message_identifier & 0xff;
+       pdu[4] = cbs->dcs;
+       pdu[5] = cbs->max_pages | (cbs->page << 4);
+
+       memcpy(pdu + 6, cbs->ud, 82);
+
+       if (len)
+               *len = 88;
+
+       return TRUE;
+}
+
+gboolean cbs_extract_app_port(const struct cbs *cbs, int *dst, int *src,
+                               gboolean *is_8bit)
+{
+       struct sms_udh_iter iter;
+
+       if (!sms_udh_iter_init_from_cbs(cbs, &iter))
+               return FALSE;
+
+       return extract_app_port_common(&iter, dst, src, is_8bit);
+}
+
+gboolean iso639_2_from_language(enum cbs_language lang, char *iso639)
+{
+       switch (lang) {
+       case CBS_LANGUAGE_GERMAN:
+               iso639[0] = 'd';
+               iso639[1] = 'e';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_ENGLISH:
+               iso639[0] = 'e';
+               iso639[1] = 'n';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_ITALIAN:
+               iso639[0] = 'i';
+               iso639[1] = 't';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_FRENCH:
+               iso639[0] = 'f';
+               iso639[1] = 'r';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_SPANISH:
+               iso639[0] = 'e';
+               iso639[1] = 's';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_DUTCH:
+               iso639[0] = 'n';
+               iso639[1] = 'l';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_SWEDISH:
+               iso639[0] = 's';
+               iso639[1] = 'v';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_DANISH:
+               iso639[0] = 'd';
+               iso639[1] = 'a';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_PORTUGESE:
+               iso639[0] = 'p';
+               iso639[1] = 't';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_FINNISH:
+               iso639[0] = 'f';
+               iso639[1] = 'i';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_NORWEGIAN:
+               iso639[0] = 'n';
+               iso639[1] = 'o';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_GREEK:
+               iso639[0] = 'e';
+               iso639[1] = 'l';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_TURKISH:
+               iso639[0] = 't';
+               iso639[1] = 'r';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_HUNGARIAN:
+               iso639[0] = 'h';
+               iso639[1] = 'u';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_POLISH:
+               iso639[0] = 'p';
+               iso639[1] = 'l';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_CZECH:
+               iso639[0] = 'c';
+               iso639[1] = 's';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_HEBREW:
+               iso639[0] = 'h';
+               iso639[1] = 'e';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_ARABIC:
+               iso639[0] = 'a';
+               iso639[1] = 'r';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_RUSSIAN:
+               iso639[0] = 'r';
+               iso639[1] = 'u';
+               iso639[2] = '\0';
+               return TRUE;
+       case CBS_LANGUAGE_ICELANDIC:
+               iso639[0] = 'i';
+               iso639[1] = 's';
+               iso639[2] = '\0';
+               return TRUE;
+       default:
+               iso639[0] = '\0';
+               break;
+       }
+
+       return FALSE;
+}
+
+char *cbs_decode_text(GSList *cbs_list, char *iso639_lang)
+{
+       GSList *l;
+       const struct cbs *cbs;
+       enum sms_charset uninitialized_var(charset);
+       enum cbs_language lang;
+       gboolean uninitialized_var(iso639);
+       int bufsize = 0;
+       unsigned char *buf;
+       char *utf8;
+
+       if (cbs_list == NULL)
+               return NULL;
+
+       /*
+        * CBS can only come from the network, so we're much less lenient
+        * on what we support.  Namely we require the same charset to be
+        * used across all pages.
+        */
+       for (l = cbs_list; l; l = l->next) {
+               enum sms_charset curch;
+               gboolean curiso;
+
+               cbs = l->data;
+
+               if (!cbs_dcs_decode(cbs->dcs, NULL, NULL,
+                                       &curch, NULL, &lang, &curiso))
+                       return NULL;
+
+               if (l == cbs_list) {
+                       iso639 = curiso;
+                       charset = curch;
+               }
+
+               if (curch != charset)
+                       return NULL;
+
+               if (curiso != iso639)
+                       return NULL;
+
+               if (curch == SMS_CHARSET_8BIT)
+                       return NULL;
+
+               if (curch == SMS_CHARSET_7BIT) {
+                       bufsize += CBS_MAX_GSM_CHARS;
+
+                       if (iso639)
+                               bufsize -= 3;
+               } else {
+                       bufsize += 82;
+
+                       if (iso639)
+                               bufsize -= 2;
+               }
+       }
+
+       if (lang) {
+               cbs = cbs_list->data;
+
+               if (iso639) {
+                       struct sms_udh_iter iter;
+                       int taken = 0;
+
+                       if (sms_udh_iter_init_from_cbs(cbs, &iter))
+                               taken = sms_udh_iter_get_udh_length(&iter) + 1;
+
+                       unpack_7bit_own_buf(cbs->ud + taken, 82 - taken,
+                                               taken, FALSE, 2,
+                                               NULL, 0,
+                                               (unsigned char *)iso639_lang);
+                       iso639_lang[2] = '\0';
+               } else {
+                       iso639_2_from_language(lang, iso639_lang);
+               }
+       }
+
+       buf = g_new(unsigned char, bufsize);
+       bufsize = 0;
+
+       for (l = cbs_list; l; l = l->next) {
+               const guint8 *ud;
+               struct sms_udh_iter iter;
+               int taken = 0;
+
+               cbs = l->data;
+               ud = cbs->ud;
+
+               if (sms_udh_iter_init_from_cbs(cbs, &iter))
+                       taken = sms_udh_iter_get_udh_length(&iter) + 1;
+
+               if (charset == SMS_CHARSET_7BIT) {
+                       unsigned char unpacked[CBS_MAX_GSM_CHARS];
+                       long written;
+                       int max_chars;
+                       int i;
+
+                       max_chars =
+                               sms_text_capacity_gsm(CBS_MAX_GSM_CHARS, taken);
+
+                       unpack_7bit_own_buf(ud + taken, 82 - taken,
+                                               taken, FALSE, max_chars,
+                                               &written, 0, unpacked);
+
+                       i = iso639 ? 3 : 0;
+
+                       /*
+                        * CR is a padding character, which means we can
+                        * safely discard everything afterwards
+                        */
+                       for (; i < written; i++, bufsize++) {
+                               if (unpacked[i] == '\r')
+                                       break;
+
+                               buf[bufsize] = unpacked[i];
+                       }
+
+                       /*
+                        * It isn't clear whether extension sequences
+                        * (2 septets) must be wholly present in the page
+                        * and not broken over multiple pages.  The behavior
+                        * is probably the same as SMS, but we don't make
+                        * the check here since the specification isn't clear
+                        */
+               } else {
+                       int num_ucs2_chars = (82 - taken) >> 1;
+                       int i = taken;
+                       int max_offset = taken + num_ucs2_chars * 2;
+
+                       /*
+                        * It is completely unclear how UCS2 chars are handled
+                        * especially across pages or when the UDH is present.
+                        * For now do the best we can.
+                        */
+                       if (iso639) {
+                               i += 2;
+                               num_ucs2_chars -= 1;
+                       }
+
+                       while (i < max_offset) {
+                               if (ud[i] == 0x00 && ud[i+1] == '\r')
+                                       break;
+
+                               buf[bufsize] = ud[i];
+                               buf[bufsize + 1] = ud[i+1];
+
+                               bufsize += 2;
+                               i += 2;
+                       }
+               }
+       }
+
+       if (charset == SMS_CHARSET_7BIT)
+               utf8 = convert_gsm_to_utf8(buf, bufsize, NULL, NULL, 0);
+       else
+               utf8 = g_convert((char *) buf, bufsize, "UTF-8//TRANSLIT",
+                                       "UCS-2BE", NULL, NULL, NULL);
+
+       g_free(buf);
+       return utf8;
+}
+
+static inline gboolean cbs_is_update_newer(unsigned int n, unsigned int o)
+{
+       unsigned int old_update = o & 0xf;
+       unsigned int new_update = n & 0xf;
+
+       if (new_update == old_update)
+               return FALSE;
+
+       /*
+        * Any Update Number eight or less higher (modulo 16) than the last
+        * received Update Number will be considered more recent, and shall be
+        * treated as a new CBS message, provided the mobile has not been
+        * switched off.
+        */
+       if (new_update <= ((old_update + 8) % 16))
+               return TRUE;
+
+       return FALSE;
+}
+
+struct cbs_assembly *cbs_assembly_new(void)
+{
+       return g_new0(struct cbs_assembly, 1);
+}
+
+void cbs_assembly_free(struct cbs_assembly *assembly)
+{
+       GSList *l;
+
+       for (l = assembly->assembly_list; l; l = l->next) {
+               struct cbs_assembly_node *node = l->data;
+
+               g_slist_foreach(node->pages, (GFunc) g_free, 0);
+               g_slist_free(node->pages);
+               g_free(node);
+       }
+
+       g_slist_free(assembly->assembly_list);
+       g_slist_free(assembly->recv_plmn);
+       g_slist_free(assembly->recv_loc);
+       g_slist_free(assembly->recv_cell);
+
+       g_free(assembly);
+}
+
+static gint cbs_compare_node_by_gs(gconstpointer a, gconstpointer b)
+{
+       const struct cbs_assembly_node *node = a;
+       unsigned int gs = GPOINTER_TO_UINT(b);
+
+       if (((node->serial >> 14) & 0x3) == gs)
+               return 0;
+
+       return 1;
+}
+
+static gint cbs_compare_node_by_update(gconstpointer a, gconstpointer b)
+{
+       const struct cbs_assembly_node *node = a;
+       unsigned int serial = GPOINTER_TO_UINT(b);
+
+       if ((serial & (~0xf)) != (node->serial & (~0xf)))
+               return 1;
+
+       if (cbs_is_update_newer(node->serial, serial))
+               return 1;
+
+       return 0;
+}
+
+static gint cbs_compare_recv_by_serial(gconstpointer a, gconstpointer b)
+{
+       unsigned int old_serial = GPOINTER_TO_UINT(a);
+       unsigned int new_serial = GPOINTER_TO_UINT(b);
+
+       if ((old_serial & (~0xf)) == (new_serial & (~0xf)))
+               return 0;
+
+       return 1;
+}
+
+static void cbs_assembly_expire(struct cbs_assembly *assembly,
+                               GCompareFunc func, gconstpointer *userdata)
+{
+       GSList *l;
+       GSList *prev;
+       GSList *tmp;
+
+       /*
+        * Take care of the case where several updates are being
+        * reassembled at the same time. If the newer one is assembled
+        * first, then the subsequent old update is discarded, make
+        * sure that we're also discarding the assembly node for the
+        * partially assembled ones
+        */
+       prev = NULL;
+       l = assembly->assembly_list;
+
+       while (l) {
+               struct cbs_assembly_node *node = l->data;
+
+               if (func(node, userdata) != 0) {
+                       prev = l;
+                       l = l->next;
+                       continue;
+               }
+
+               if (prev)
+                       prev->next = l->next;
+               else
+                       assembly->assembly_list = l->next;
+
+               g_slist_foreach(node->pages, (GFunc) g_free, NULL);
+               g_slist_free(node->pages);
+               g_free(node->pages);
+               tmp = l;
+               l = l->next;
+               g_slist_free_1(tmp);
+       }
+}
+
+void cbs_assembly_location_changed(struct cbs_assembly *assembly, gboolean plmn,
+                                       gboolean lac, gboolean ci)
+{
+       /*
+        * Location Area wide (in GSM) (which means that a CBS message with the
+        * same Message Code and Update Number may or may not be "new" in the
+        * next cell according to whether the next cell is in the same Location
+        * Area as the current cell), or
+        *
+        * Service Area Wide (in UMTS) (which means that a CBS message with the
+        * same Message Code and Update Number may or may not be "new" in the
+        * next cell according to whether the next cell is in the same Service
+        * Area as the current cell)
+        *
+        * NOTE 4: According to 3GPP TS 23.003 [2] a Service Area consists of
+        * one cell only.
+        */
+
+       if (plmn) {
+               lac = TRUE;
+               g_slist_free(assembly->recv_plmn);
+               assembly->recv_plmn = NULL;
+
+               cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
+                               GUINT_TO_POINTER(CBS_GEO_SCOPE_PLMN));
+       }
+
+       if (lac) {
+               /* If LAC changed, then cell id has changed */
+               ci = TRUE;
+               g_slist_free(assembly->recv_loc);
+               assembly->recv_loc = NULL;
+
+               cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
+                               GUINT_TO_POINTER(CBS_GEO_SCOPE_SERVICE_AREA));
+       }
+
+       if (ci) {
+               g_slist_free(assembly->recv_cell);
+               assembly->recv_cell = NULL;
+               cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
+                               GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_IMMEDIATE));
+               cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
+                               GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_NORMAL));
+       }
+}
+
+GSList *cbs_assembly_add_page(struct cbs_assembly *assembly,
+                               const struct cbs *cbs)
+{
+       struct cbs *newcbs;
+       struct cbs_assembly_node *node;
+       GSList *completed;
+       unsigned int new_serial;
+       GSList **recv;
+       GSList *l;
+       GSList *prev;
+       int position;
+
+       new_serial = cbs->gs << 14;
+       new_serial |= cbs->message_code << 4;
+       new_serial |= cbs->update_number;
+       new_serial |= cbs->message_identifier << 16;
+
+       if (cbs->gs == CBS_GEO_SCOPE_PLMN)
+               recv = &assembly->recv_plmn;
+       else if (cbs->gs == CBS_GEO_SCOPE_SERVICE_AREA)
+               recv = &assembly->recv_loc;
+       else
+               recv = &assembly->recv_cell;
+
+       /* Have we seen this message before? */
+       l = g_slist_find_custom(*recv, GUINT_TO_POINTER(new_serial),
+                               cbs_compare_recv_by_serial);
+
+       /* If we have, is the message newer? */
+       if (l && !cbs_is_update_newer(new_serial, GPOINTER_TO_UINT(l->data)))
+               return NULL;
+
+       /* Easy case first, page 1 of 1 */
+       if (cbs->max_pages == 1 && cbs->page == 1) {
+               if (l)
+                       l->data = GUINT_TO_POINTER(new_serial);
+               else
+                       *recv = g_slist_prepend(*recv,
+                                               GUINT_TO_POINTER(new_serial));
+
+               newcbs = g_new(struct cbs, 1);
+               memcpy(newcbs, cbs, sizeof(struct cbs));
+               completed = g_slist_append(NULL, newcbs);
+
+               return completed;
+       }
+
+       prev = NULL;
+       position = 0;
+
+       for (l = assembly->assembly_list; l; prev = l, l = l->next) {
+               int j;
+               node = l->data;
+
+               if (new_serial != node->serial)
+                       continue;
+
+               if (node->bitmap & (1 << cbs->page))
+                       return NULL;
+
+               for (j = 1; j < cbs->page; j++)
+                       if (node->bitmap & (1 << j))
+                               position += 1;
+
+               goto out;
+       }
+
+       node = g_new0(struct cbs_assembly_node, 1);
+       node->serial = new_serial;
+
+       assembly->assembly_list = g_slist_prepend(assembly->assembly_list,
+                                                       node);
+
+       prev = NULL;
+       l = assembly->assembly_list;
+       position = 0;
+
+out:
+       newcbs = g_new(struct cbs, 1);
+       memcpy(newcbs, cbs, sizeof(struct cbs));
+       node->pages = g_slist_insert(node->pages, newcbs, position);
+       node->bitmap |= 1 << cbs->page;
+
+       if (g_slist_length(node->pages) < cbs->max_pages)
+               return NULL;
+
+       completed = node->pages;
+
+       if (prev)
+               prev->next = l->next;
+       else
+               assembly->assembly_list = l->next;
+
+       g_free(node);
+       g_slist_free_1(l);
+
+       cbs_assembly_expire(assembly, cbs_compare_node_by_update,
+                               GUINT_TO_POINTER(new_serial));
+       *recv = g_slist_prepend(*recv, GUINT_TO_POINTER(new_serial));
+
+       return completed;
+}
+
+static inline int skip_to_next_field(const char *str, int pos, int len)
+{
+       if (pos < len && str[pos] == ',')
+               pos += 1;
+
+       while (pos < len && str[pos] == ' ')
+               pos += 1;
+
+       return pos;
+}
+
+static gboolean next_range(const char *str, int *offset, gint *min, gint *max)
+{
+       int pos;
+       int end;
+       int len;
+       int low = 0;
+       int high = 0;
+
+       len = strlen(str);
+
+       pos = *offset;
+
+       while (pos < len && str[pos] == ' ')
+               pos += 1;
+
+       end = pos;
+
+       while (str[end] >= '0' && str[end] <= '9') {
+               low = low * 10 + (int)(str[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+       if (str[end] != '-') {
+               high = low;
+               goto out;
+       }
+
+       pos = end = end + 1;
+
+       while (str[end] >= '0' && str[end] <= '9') {
+               high = high * 10 + (int)(str[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+out:
+       *offset = skip_to_next_field(str, end, len);
+
+       if (min)
+               *min = low;
+
+       if (max)
+               *max = high;
+
+       return TRUE;
+}
+
+GSList *cbs_optimize_ranges(GSList *ranges)
+{
+       struct cbs_topic_range *range;
+       unsigned char bitmap[125];
+       GSList *l;
+       unsigned short i;
+       GSList *ret = NULL;
+
+       memset(bitmap, 0, sizeof(bitmap));
+
+       for (l = ranges; l; l = l->next) {
+               range = l->data;
+
+               for (i = range->min; i <= range->max; i++) {
+                       int byte_offset = i / 8;
+                       int bit = i % 8;
+
+                       bitmap[byte_offset] |= 1 << bit;
+               }
+       }
+
+       range = NULL;
+
+       for (i = 0; i <= 999; i++) {
+               int byte_offset = i / 8;
+               int bit = i % 8;
+
+               if (is_bit_set(bitmap[byte_offset], bit) == FALSE) {
+                       if (range) {
+                               ret = g_slist_prepend(ret, range);
+                               range = NULL;
+                       }
+
+                       continue;
+               }
+
+               if (range) {
+                       range->max = i;
+                       continue;
+               }
+
+               range = g_new0(struct cbs_topic_range, 1);
+               range->min = i;
+               range->max = i;
+       }
+
+       if (range != NULL)
+               ret = g_slist_prepend(ret, range);
+
+       ret = g_slist_reverse(ret);
+
+       return ret;
+}
+
+GSList *cbs_extract_topic_ranges(const char *ranges)
+{
+       int min;
+       int max;
+       int offset = 0;
+       GSList *ret = NULL;
+       GSList *tmp;
+
+       while (next_range(ranges, &offset, &min, &max) == TRUE) {
+               if (min < 0 || min > 999)
+                       return NULL;
+
+               if (max < 0 || max > 999)
+                       return NULL;
+
+               if (max < min)
+                       return NULL;
+       }
+
+       if (ranges[offset] != '\0')
+               return NULL;
+
+       offset = 0;
+
+       while (next_range(ranges, &offset, &min, &max) == TRUE) {
+               struct cbs_topic_range *range = g_new0(struct cbs_topic_range, 1);
+
+               range->min = min;
+               range->max = max;
+
+               ret = g_slist_prepend(ret, range);
+       }
+
+       tmp = cbs_optimize_ranges(ret);
+       g_slist_foreach(ret, (GFunc) g_free, NULL);
+       g_slist_free(ret);
+
+       return tmp;
+}
+
+static inline int element_length(unsigned short element)
+{
+       if (element <= 9)
+               return 1;
+
+       if (element <= 99)
+               return 2;
+
+       if (element <= 999)
+               return 3;
+
+       if (element <= 9999)
+               return 4;
+
+       return 5;
+}
+
+static inline int range_length(struct cbs_topic_range *range)
+{
+       if (range->min == range->max)
+               return element_length(range->min);
+
+       return element_length(range->min) + element_length(range->max) + 1;
+}
+
+char *cbs_topic_ranges_to_string(GSList *ranges)
+{
+       int len = 0;
+       int nelem = 0;
+       struct cbs_topic_range *range;
+       GSList *l;
+       char *ret;
+
+       if (ranges == NULL)
+               return g_new0(char, 1);
+
+       for (l = ranges; l; l = l->next) {
+               range = l->data;
+
+               len += range_length(range);
+               nelem += 1;
+       }
+
+       /* Space for ranges, commas and terminator null */
+       ret = g_new(char, len + nelem);
+
+       len = 0;
+
+       for (l = ranges; l; l = l->next) {
+               range = l->data;
+
+               if (range->min != range->max)
+                       len += sprintf(ret + len, "%hu-%hu",
+                                       range->min, range->max);
+               else
+                       len += sprintf(ret + len, "%hu", range->min);
+
+               if (l->next != NULL)
+                       ret[len++] = ',';
+       }
+
+       return ret;
+}
+
+static gint cbs_topic_compare(gconstpointer a, gconstpointer b)
+{
+       const struct cbs_topic_range *range = a;
+       unsigned short topic = GPOINTER_TO_UINT(b);
+
+       if (topic >= range->min && topic <= range->max)
+               return 0;
+
+       return 1;
+}
+
+gboolean cbs_topic_in_range(unsigned int topic, GSList *ranges)
+{
+       if (ranges == NULL)
+               return FALSE;
+
+       return g_slist_find_custom(ranges, GUINT_TO_POINTER(topic),
+                                       cbs_topic_compare) != NULL;
+}
+
+char *ussd_decode(int dcs, int len, const unsigned char *data)
+{
+       gboolean udhi;
+       enum sms_charset charset;
+       gboolean compressed;
+       gboolean iso639;
+       char *utf8;
+
+       if (!cbs_dcs_decode(dcs, &udhi, NULL, &charset,
+                               &compressed, NULL, &iso639))
+               return NULL;
+
+       if (udhi || compressed || iso639)
+               return NULL;
+
+       switch (charset) {
+       case SMS_CHARSET_7BIT:
+       {
+               long written;
+               unsigned char *unpacked = unpack_7bit(data, len, 0, TRUE, 0,
+                                                       &written, 0);
+               if (unpacked == NULL)
+                       return NULL;
+
+               utf8 = convert_gsm_to_utf8(unpacked, written, NULL, NULL, 0);
+               g_free(unpacked);
+
+               break;
+       }
+       case SMS_CHARSET_8BIT:
+               utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0);
+               break;
+       case SMS_CHARSET_UCS2:
+               utf8 = g_convert((const gchar *) data, len,
+                                       "UTF-8//TRANSLIT", "UCS-2BE",
+                                       NULL, NULL, NULL);
+               break;
+       default:
+               utf8 = NULL;
+       }
+
+       return utf8;
+}
+
+gboolean ussd_encode(const char *str, long *items_written, unsigned char *pdu)
+{
+       unsigned char *converted = NULL;
+       long written;
+       long num_packed;
+
+       if (pdu == NULL)
+               return FALSE;
+
+       converted = convert_utf8_to_gsm(str, -1, NULL, &written, 0);
+       if (converted == NULL || written > 182) {
+               g_free(converted);
+               return FALSE;
+       }
+
+       pack_7bit_own_buf(converted, written, 0, TRUE, &num_packed, 0, pdu);
+       g_free(converted);
+
+       if (num_packed < 1)
+               return FALSE;
+
+       if (items_written)
+               *items_written = num_packed;
+
+       return TRUE;
+}
diff --git a/src/smsutil.h b/src/smsutil.h
new file mode 100644 (file)
index 0000000..b1001f8
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define CBS_MAX_GSM_CHARS 93
+#define SMS_MSGID_LEN 20
+
+enum sms_type {
+       SMS_TYPE_DELIVER = 0,
+       SMS_TYPE_DELIVER_REPORT_ACK,
+       SMS_TYPE_DELIVER_REPORT_ERROR,
+       SMS_TYPE_STATUS_REPORT,
+       SMS_TYPE_SUBMIT,
+       SMS_TYPE_SUBMIT_REPORT_ACK,
+       SMS_TYPE_SUBMIT_REPORT_ERROR,
+       SMS_TYPE_COMMAND
+};
+
+/* 23.040 Section 9.1.2.5 */
+enum sms_number_type {
+       SMS_NUMBER_TYPE_UNKNOWN = 0,
+       SMS_NUMBER_TYPE_INTERNATIONAL = 1,
+       SMS_NUMBER_TYPE_NATIONAL = 2,
+       SMS_NUMBER_TYPE_NETWORK_SPECIFIC = 3,
+       SMS_NUMBER_TYPE_SUBSCRIBER = 4,
+       SMS_NUMBER_TYPE_ALPHANUMERIC = 5,
+       SMS_NUMBER_TYPE_ABBREVIATED = 6,
+       SMS_NUMBER_TYPE_RESERVED = 7
+};
+
+/* 23.040 Section 9.1.2.5 */
+enum sms_numbering_plan {
+       SMS_NUMBERING_PLAN_UNKNOWN = 0,
+       SMS_NUMBERING_PLAN_ISDN = 1,
+       SMS_NUMBERING_PLAN_DATA = 3,
+       SMS_NUMBERING_PLAN_TELEX = 4,
+       SMS_NUMBERING_PLAN_SC1 = 5,
+       SMS_NUMBERING_PLAN_SC2 = 6,
+       SMS_NUMBERING_PLAN_NATIONAL = 8,
+       SMS_NUMBERING_PLAN_PRIVATE = 9,
+       SMS_NUMBERING_PLAN_ERMES = 10,
+       SMS_NUMBERING_PLAN_RESERVED = 15
+};
+
+enum sms_validity_period_format {
+       SMS_VALIDITY_PERIOD_FORMAT_ABSENT = 0,
+       SMS_VALIDITY_PERIOD_FORMAT_ENHANCED = 1,
+       SMS_VALIDITY_PERIOD_FORMAT_RELATIVE = 2,
+       SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE = 3,
+};
+
+enum sms_st {
+       SMS_ST_COMPLETED_RECEIVED = 0x0,
+       SMS_ST_COMPLETED_UNABLE_TO_CONFIRM = 0x1,
+       SMS_ST_COMPLETED_REPLACED = 0x2,
+       SMS_ST_COMPLETED_LAST = 0x1F,
+       SMS_ST_TEMPORARY_CONGESTION = 0x20,
+       SMS_ST_TEMPORARY_SME_BUSY = 0x21,
+       SMS_ST_TEMPORARY_NO_RESPONSE = 0x22,
+       SMS_ST_TEMPORARY_SERVICE_REJECTED = 0x23,
+       SMS_ST_TEMPORARY_QOS_UNAVAILABLE = 0x24,
+       SMS_ST_TEMPORARY_SME_ERROR = 0x25,
+       SMS_ST_TEMPORARY_LAST = 0x2F,
+       SMS_ST_PERMANENT_RP_ERROR = 0x40,
+       SMS_ST_PERMANENT_INVALID_DESTINATION = 0x41,
+       SMS_ST_PERMANENT_CONNECTION_REJECTED = 0x42,
+       SMS_ST_PERMANENT_NOT_OBTAINABLE = 0x43,
+       SMS_ST_PERMANENT_QOS_UNAVAILABLE = 0x44,
+       SMS_ST_PERMANENT_INTERWORKING_UNAVAILABLE = 0x45,
+       SMS_ST_PERMANENT_VALIDITY_PERIOD_EXPIRED = 0x46,
+       SMS_ST_PERMANENT_DELETED = 0x47,
+       SMS_ST_PERMANENT_SC_ADMIN_DELETED = 0x48,
+       SMS_ST_PERMANENT_SM_DOES_NOT_EXIST = 0x49,
+       SMS_ST_PERMANENT_LAST = 0x4F,
+       SMS_ST_TEMPFINAL_CONGESTION = 0x60,
+       SMS_ST_TEMPFINAL_SME_BUSY = 0x61,
+       SMS_ST_TEMPFINAL_NO_RESPONSE = 0x62,
+       SMS_ST_TEMPFINAL_SERVICE_REJECTED = 0x63,
+       SMS_ST_TEMPFINAL_QOS_UNAVAILABLE = 0x64,
+       SMS_ST_TEMPFINAL_SME_ERROR = 0x65,
+       SMS_ST_TEMPFINAL_LAST = 0x6F,
+};
+
+enum sms_ct {
+       SMS_CT_ENQUIRY = 0,
+       SMS_CT_CANCEL_SRR = 1,
+       SMS_CT_DELETE_SM = 2,
+       SMS_CT_ENABLE_SRR = 3
+};
+
+enum sms_iei {
+       SMS_IEI_CONCATENATED_8BIT = 0x00,
+       SMS_IEI_SPECIAL_MESSAGE_INDICATION = 0x01,
+       SMS_IEI_APPLICATION_ADDRESS_8BIT = 0x04,
+       SMS_IEI_APPLICATION_ADDRESS_16BIT = 0x05,
+       SMS_IEI_SMSC_CONTROL_PARAMETERS = 0x06,
+       SMS_IEI_UDH_SOURCE_INDICATOR = 0x07,
+       SMS_IEI_CONCATENATED_16BIT = 0x08,
+       SMS_IEI_WCMP = 0x09,
+       SMS_IEI_TEXT_FORMAT = 0x0A,
+       SMS_IEI_PREDEFINED_SOUND = 0x0B,
+       SMS_IEI_USER_DEFINED_SOUND = 0x0C,
+       SMS_IEI_PREDEFINED_ANIMATION = 0x0D,
+       SMS_IEI_LARGE_ANIMATION = 0x0E,
+       SMS_IEI_SMALL_ANIMATION = 0x0F,
+       SMS_IEI_LARGE_PICTURE = 0x10,
+       SMS_IEI_SMALL_PICTURE = 0x11,
+       SMS_IEI_VARIABLE_PICTURE = 0x12,
+       SMS_IEI_USER_PROMPT_INDICATOR = 0x13,
+       SMS_IEI_EXTENDED_OBJECT = 0x14,
+       SMS_IEI_REUSED_EXTENDED_OBJECT = 0x15,
+       SMS_IEI_COMPRESSION_CONTROL = 0x16,
+       SMS_IEI_OBJECT_DISTRIBUTION_INDICATOR = 0x17,
+       SMS_IEI_STANDARD_WVG_OBJECT = 0x18,
+       SMS_IEI_CHARACTER_SIZE_WVG_OBJECT = 0x19,
+       SMS_IEI_EXTENDED_OBJECT_DATA_REQUEST_COMMAND = 0x1A,
+       SMS_IEI_RFC822_EMAIL_HEADER = 0x20,
+       SMS_IEI_HYPERLINK_ELEMENT = 0x21,
+       SMS_IEI_REPLY_ADDRESS_ELEMENT = 0x22,
+       SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION = 0x23,
+       SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT = 0x24,
+       SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT = 0x25,
+       SMS_IEI_INVALID = 0xFFF
+};
+
+enum sms_class {
+       SMS_CLASS_0 = 0,
+       SMS_CLASS_1 = 1,
+       SMS_CLASS_2 = 2,
+       SMS_CLASS_3 = 3,
+       SMS_CLASS_UNSPECIFIED = 4,
+};
+
+enum sms_charset {
+       SMS_CHARSET_7BIT = 0,
+       SMS_CHARSET_8BIT = 1,
+       SMS_CHARSET_UCS2 = 2,
+};
+
+enum sms_alphabet {
+       SMS_ALPHABET_DEFAULT = 0,
+       SMS_ALPHABET_TURKISH,
+       SMS_ALPHABET_SPANISH,
+       SMS_ALPHABET_PORTUGUESE,
+};
+
+enum sms_mwi_type {
+       SMS_MWI_TYPE_VOICE = 0,
+       SMS_MWI_TYPE_FAX = 1,
+       SMS_MWI_TYPE_EMAIL = 2,
+       SMS_MWI_TYPE_OTHER = 3,
+       SMS_MWI_TYPE_VIDEO = 4,
+};
+
+enum sms_pid_type {
+       SMS_PID_TYPE_SM_TYPE_0 = 0x40,
+       SMS_PID_TYPE_REPLACE_SM_TYPE_1 = 0x41,
+       SMS_PID_TYPE_REPLACE_SM_TYPE_2 = 0x42,
+       SMS_PID_TYPE_REPLACE_SM_TYPE_3 = 0x43,
+       SMS_PID_TYPE_REPLACE_SM_TYPE_4 = 0x44,
+       SMS_PID_TYPE_REPLACE_SM_TYPE_5 = 0x45,
+       SMS_PID_TYPE_REPLACE_SM_TYPE_6 = 0x46,
+       SMS_PID_TYPE_REPLACE_SM_TYPE_7 = 0x47,
+       SMS_PID_TYPE_ENHANCED_MESSAGE_SERVICE = 0x5e,
+       SMS_PID_TYPE_RETURN_CALL = 0x5f,
+       SMS_PID_TYPE_ANSI136 = 0x7c,
+       SMS_PID_TYPE_ME_DOWNLOAD = 0x7d,
+       SMS_PID_TYPE_ME_DEPERSONALIZATION = 0x7e,
+       SMS_PID_TYPE_USIM_DOWNLOAD = 0x7f,
+};
+
+enum cbs_language {
+       CBS_LANGUAGE_GERMAN = 0x0,
+       CBS_LANGUAGE_ENGLISH = 0x1,
+       CBS_LANGUAGE_ITALIAN = 0x2,
+       CBS_LANGUAGE_FRENCH = 0x3,
+       CBS_LANGUAGE_SPANISH = 0x4,
+       CBS_LANGUAGE_DUTCH = 0x5,
+       CBS_LANGUAGE_SWEDISH = 0x6,
+       CBS_LANGUAGE_DANISH = 0x7,
+       CBS_LANGUAGE_PORTUGESE = 0x8,
+       CBS_LANGUAGE_FINNISH = 0x9,
+       CBS_LANGUAGE_NORWEGIAN = 0xA,
+       CBS_LANGUAGE_GREEK = 0xB,
+       CBS_LANGUAGE_TURKISH = 0xC,
+       CBS_LANGUAGE_HUNGARIAN = 0xD,
+       CBS_LANGUAGE_POLISH = 0xE,
+       CBS_LANGUAGE_UNSPECIFIED = 0xF,
+       CBS_LANGUAGE_CZECH = 0x20,
+       CBS_LANGUAGE_HEBREW = 0x21,
+       CBS_LANGUAGE_ARABIC = 0x22,
+       CBS_LANGUAGE_RUSSIAN = 0x23,
+       CBS_LANGUAGE_ICELANDIC = 0x24
+};
+
+enum cbs_geo_scope {
+       CBS_GEO_SCOPE_CELL_IMMEDIATE,
+       CBS_GEO_SCOPE_PLMN,
+       CBS_GEO_SCOPE_SERVICE_AREA,
+       CBS_GEO_SCOPE_CELL_NORMAL
+};
+
+struct sms_address {
+       enum sms_number_type number_type;
+       enum sms_numbering_plan numbering_plan;
+       char address[21]; /* Max 20 in semi-octet, 11 in alnum */
+};
+
+struct sms_scts {
+       guint8 year;
+       guint8 month;
+       guint8 day;
+       guint8 hour;
+       guint8 minute;
+       guint8 second;
+       gboolean has_timezone;
+       gint8 timezone;
+};
+
+struct sms_validity_period {
+       union {
+               guint8 relative;
+               struct sms_scts absolute;
+               guint8 enhanced[7];
+       };
+};
+
+struct sms_deliver {
+       gboolean mms;
+       gboolean sri;
+       gboolean udhi;
+       gboolean rp;
+       struct sms_address oaddr;
+       guint8 pid;
+       guint8 dcs;
+       struct sms_scts scts;
+       guint8 udl;
+       guint8 ud[140];
+};
+
+struct sms_deliver_err_report {
+       gboolean udhi;
+       guint8 fcs;
+       guint8 pi;
+       guint8 pid;
+       guint8 dcs;
+       guint8 udl;
+       guint8 ud[158];
+};
+
+struct sms_deliver_ack_report {
+       gboolean udhi;
+       guint8 pi;
+       guint8 pid;
+       guint8 dcs;
+       guint8 udl;
+       guint8 ud[159];
+};
+
+struct sms_command {
+       gboolean udhi;
+       gboolean srr;
+       guint8 mr;
+       guint8 pid;
+       enum sms_ct ct;
+       guint8 mn;
+       struct sms_address daddr;
+       guint8 cdl;
+       guint8 cd[156];
+};
+
+struct sms_status_report {
+       gboolean udhi;
+       gboolean mms;
+       gboolean srq;
+       guint8 mr;
+       struct sms_address raddr;
+       struct sms_scts scts;
+       struct sms_scts dt;
+       enum sms_st st;
+       guint8 pi;
+       guint8 pid;
+       guint8 dcs;
+       guint8 udl;
+       guint8 ud[143];
+};
+
+struct sms_submit {
+       gboolean rd;
+       enum sms_validity_period_format vpf;
+       gboolean rp;
+       gboolean udhi;
+       gboolean srr;
+       guint8 mr;
+       struct sms_address daddr;
+       guint8 pid;
+       guint8 dcs;
+       struct sms_validity_period vp;
+       guint8 udl;
+       guint8 ud[140];
+};
+
+struct sms_submit_ack_report {
+       gboolean udhi;
+       guint8 pi;
+       struct sms_scts scts;
+       guint8 pid;
+       guint8 dcs;
+       guint8 udl;
+       guint8 ud[152];
+};
+
+struct sms_submit_err_report {
+       gboolean udhi;
+       guint8 fcs;
+       guint8 pi;
+       struct sms_scts scts;
+       guint8 pid;
+       guint8 dcs;
+       guint8 udl;
+       guint8 ud[151];
+};
+
+struct sms {
+       struct sms_address sc_addr;
+       enum sms_type type;
+       union {
+               struct sms_deliver deliver;
+               struct sms_deliver_ack_report deliver_ack_report;
+               struct sms_deliver_err_report deliver_err_report;
+               struct sms_submit submit;
+               struct sms_submit_ack_report submit_ack_report;
+               struct sms_submit_err_report submit_err_report;
+               struct sms_command command;
+               struct sms_status_report status_report;
+       };
+};
+
+struct sms_udh_iter {
+       const guint8 *data;
+       guint8 offset;
+};
+
+struct sms_assembly_node {
+       struct sms_address addr;
+       time_t ts;
+       GSList *fragment_list;
+       guint16 ref;
+       guint8 max_fragments;
+       guint8 num_fragments;
+       unsigned int bitmap[8];
+};
+
+struct sms_assembly {
+       const char *imsi;
+       GSList *assembly_list;
+};
+
+struct id_table_node {
+       unsigned int mrs[8];
+       time_t expiration;
+       unsigned char total_mrs;
+       unsigned char sent_mrs;
+       gboolean deliverable;
+} __attribute__((packed));
+
+struct status_report_assembly {
+       const char *imsi;
+       GHashTable *assembly_table;
+};
+
+struct cbs {
+       enum cbs_geo_scope gs;                  /* 2 bits */
+       guint16 message_code;                   /* 10 bits */
+       guint8 update_number;                   /* 4 bits */
+       guint16 message_identifier;             /* 16 bits */
+       guint8 dcs;                             /* 8 bits */
+       guint8 max_pages;                       /* 4 bits */
+       guint8 page;                            /* 4 bits */
+       guint8 ud[82];
+};
+
+struct cbs_assembly_node {
+       guint32 serial;
+       guint16 bitmap;
+       GSList *pages;
+};
+
+struct cbs_assembly {
+       GSList *assembly_list;
+       GSList *recv_plmn;
+       GSList *recv_loc;
+       GSList *recv_cell;
+};
+
+struct cbs_topic_range {
+       unsigned short min;
+       unsigned short max;
+};
+
+struct txq_backup_entry {
+       GSList *msg_list;
+       unsigned char uuid[SMS_MSGID_LEN];
+       unsigned long flags;
+};
+
+static inline gboolean is_bit_set(unsigned char oct, int bit)
+{
+       int mask = 1 << bit;
+       return oct & mask ? TRUE : FALSE;
+}
+
+static inline unsigned char bit_field(unsigned char oct, int start, int num)
+{
+       unsigned char mask = (1 << num) - 1;
+
+       return (oct >> start) & mask;
+}
+
+void extract_bcd_number(const unsigned char *buf, int len, char *out);
+void encode_bcd_number(const char *number, unsigned char *out);
+
+gboolean sms_decode(const unsigned char *pdu, int len, gboolean outgoing,
+                       int tpdu_len, struct sms *out);
+
+gboolean sms_decode_unpacked_stk_pdu(const unsigned char *pdu, int len,
+                                       struct sms *out);
+
+gboolean sms_encode(const struct sms *in, int *len, int *tpdu_len,
+                       unsigned char *pdu);
+
+/*
+ * Length is based on the address being 12 hex characters plus a
+ * terminating NUL char. See sms_assembly_extract_address().
+ */
+#define DECLARE_SMS_ADDR_STR(a) char a[25]
+
+gboolean sms_decode_address_field(const unsigned char *pdu, int len,
+                                       int *offset, gboolean sc,
+                                       struct sms_address *out);
+
+gboolean sms_encode_address_field(const struct sms_address *in, gboolean sc,
+                                       unsigned char *pdu, int *offset);
+
+guint8 sms_decode_semi_octet(guint8 in);
+
+gboolean sms_decode_scts(const unsigned char *pdu, int len,
+                               int *offset, struct sms_scts *out);
+
+gboolean sms_encode_scts(const struct sms_scts *in, unsigned char *pdu,
+                               int *offset);
+
+int sms_udl_in_bytes(guint8 ud_len, guint8 dcs);
+
+time_t sms_scts_to_time(const struct sms_scts *scts, struct tm *remote);
+
+const char *sms_address_to_string(const struct sms_address *addr);
+void sms_address_from_string(struct sms_address *addr, const char *str);
+
+const guint8 *sms_extract_common(const struct sms *sms, gboolean *out_udhi,
+                                       guint8 *out_dcs, guint8 *out_udl,
+                                       guint8 *out_max);
+
+gboolean sms_udh_iter_init(const struct sms *sms, struct sms_udh_iter *iter);
+gboolean sms_udh_iter_init_from_cbs(const struct cbs *cbs,
+                                       struct sms_udh_iter *iter);
+guint8 sms_udh_iter_get_udh_length(struct sms_udh_iter *iter);
+const guint8 *sms_udh_iter_get_ud_after_header(struct sms_udh_iter *iter);
+enum sms_iei sms_udh_iter_get_ie_type(struct sms_udh_iter *iter);
+guint8 sms_udh_iter_get_ie_length(struct sms_udh_iter *iter);
+void sms_udh_iter_get_ie_data(struct sms_udh_iter *iter, guint8 *data);
+gboolean sms_udh_iter_has_next(struct sms_udh_iter *iter);
+gboolean sms_udh_iter_next(struct sms_udh_iter *iter);
+
+gboolean sms_dcs_decode(guint8 dcs, enum sms_class *cls,
+                       enum sms_charset *charset,
+                       gboolean *compressed, gboolean *autodelete);
+
+gboolean sms_mwi_dcs_decode(guint8 dcs, enum sms_mwi_type *type,
+                               enum sms_charset *charset,
+                               gboolean *active, gboolean *discard);
+
+gboolean sms_extract_app_port(const struct sms *sms, int *dst, int *src,
+                               gboolean *is_8bit);
+gboolean sms_extract_concatenation(const struct sms *sms, guint16 *ref_num,
+                                       guint8 *max_msgs, guint8 *seq_num);
+gboolean sms_extract_language_variant(const struct sms *sms, guint8 *locking,
+                                       guint8 *single);
+
+unsigned char *sms_decode_datagram(GSList *sms_list, long *out_len);
+char *sms_decode_text(GSList *sms_list);
+
+struct sms_assembly *sms_assembly_new(const char *imsi);
+void sms_assembly_free(struct sms_assembly *assembly);
+GSList *sms_assembly_add_fragment(struct sms_assembly *assembly,
+                                       const struct sms *sms, time_t ts,
+                                       const struct sms_address *addr,
+                                       guint16 ref, guint8 max, guint8 seq);
+void sms_assembly_expire(struct sms_assembly *assembly, time_t before);
+gboolean sms_address_to_hex_string(const struct sms_address *in, char *straddr);
+
+struct status_report_assembly *status_report_assembly_new(const char *imsi);
+void status_report_assembly_free(struct status_report_assembly *assembly);
+gboolean status_report_assembly_report(struct status_report_assembly *assembly,
+                                       const struct sms *status_report,
+                                       unsigned char *out_msgid,
+                                       gboolean *msg_delivered);
+void status_report_assembly_add_fragment(struct status_report_assembly
+                                       *assembly, const unsigned char *msgid,
+                                       const struct sms_address *to,
+                                       unsigned char mr, time_t expiration,
+                                       unsigned char total_mrs);
+void status_report_assembly_expire(struct status_report_assembly *assembly,
+                                       time_t before);
+
+gboolean sms_tx_backup_store(const char *imsi, unsigned long id,
+                               unsigned long flags, const char *uuid,
+                               guint8 seq, const unsigned char *pdu,
+                               int pdu_len, int tpdu_len);
+void sms_tx_backup_remove(const char *imsi, unsigned long id,
+                               unsigned long flags, const char *uuid,
+                               guint8 seq);
+void sms_tx_backup_free(const char *imsi, unsigned long id,
+                               unsigned long flags, const char *uuid);
+GQueue *sms_tx_queue_load(const char *imsi);
+
+GSList *sms_text_prepare(const char *to, const char *utf8, guint16 ref,
+                               gboolean use_16bit,
+                               gboolean use_delivery_reports);
+
+GSList *sms_text_prepare_with_alphabet(const char *to, const char *utf8,
+                               guint16 ref, gboolean use_16bit,
+                               gboolean use_delivery_reports,
+                               enum sms_alphabet alphabet);
+
+GSList *sms_datagram_prepare(const char *to,
+                               const unsigned char *data, unsigned int len,
+                               guint16 ref, gboolean use_16bit_ref,
+                               unsigned short src, unsigned short dst,
+                               gboolean use_16bit_port,
+                               gboolean use_delivery_reports);
+
+gboolean cbs_dcs_decode(guint8 dcs, gboolean *udhi, enum sms_class *cls,
+                       enum sms_charset *charset, gboolean *compressed,
+                       enum cbs_language *language, gboolean *iso639);
+
+gboolean iso639_2_from_language(enum cbs_language lang, char *iso639);
+gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out);
+gboolean cbs_encode(const struct cbs *cbs, int *len, unsigned char *pdu);
+gboolean cbs_extract_app_port(const struct cbs *cbs, int *dst, int *src,
+                               gboolean *is_8bit);
+
+char *cbs_decode_text(GSList *cbs_list, char *iso639_lang);
+
+struct cbs_assembly *cbs_assembly_new(void);
+void cbs_assembly_free(struct cbs_assembly *assembly);
+GSList *cbs_assembly_add_page(struct cbs_assembly *assembly,
+                               const struct cbs *cbs);
+void cbs_assembly_location_changed(struct cbs_assembly *assembly, gboolean plmn,
+                                       gboolean lac, gboolean ci);
+
+char *cbs_topic_ranges_to_string(GSList *ranges);
+GSList *cbs_extract_topic_ranges(const char *ranges);
+GSList *cbs_optimize_ranges(GSList *ranges);
+gboolean cbs_topic_in_range(unsigned int topic, GSList *ranges);
+
+char *ussd_decode(int dcs, int len, const unsigned char *data);
+gboolean ussd_encode(const char *str, long *items_written, unsigned char *pdu);
diff --git a/src/stk.c b/src/stk.c
new file mode 100644 (file)
index 0000000..e557e90
--- /dev/null
+++ b/src/stk.c
@@ -0,0 +1,3095 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <gdbus.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "smsutil.h"
+#include "stkutil.h"
+#include "stkagent.h"
+#include "util.h"
+
+static GSList *g_drivers = NULL;
+
+struct stk_timer {
+       time_t expiry;
+       time_t start;
+};
+
+struct ofono_stk {
+       const struct ofono_stk_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       struct stk_command *pending_cmd;
+       void (*cancel_cmd)(struct ofono_stk *stk);
+       GQueue *envelope_q;
+       DBusMessage *pending;
+
+       struct stk_timer timers[8];
+       guint timers_source;
+
+       int timeout;
+       int short_timeout;
+       struct stk_agent *session_agent;
+       struct stk_agent *default_agent;
+       struct stk_agent *current_agent; /* Always equals one of the above */
+       struct stk_menu *main_menu, *select_item_menu;
+       gboolean respond_on_exit;
+       ofono_bool_t immediate_response;
+       guint remove_agent_source;
+       struct extern_req *extern_req;
+       char *idle_mode_text;
+       struct stk_icon_id idle_mode_icon;
+       struct timeval get_inkey_start_ts;
+       int dtmf_id;
+
+       __ofono_sms_sim_download_cb_t sms_pp_cb;
+       void *sms_pp_userdata;
+};
+
+struct envelope_op {
+       uint8_t tlv[256];
+       unsigned int tlv_len;
+       int retries;
+       void (*cb)(struct ofono_stk *stk, gboolean ok,
+                       const unsigned char *data, int length);
+};
+
+struct extern_req {
+       struct ofono_stk *stk;
+       gboolean cancelled;
+};
+
+#define ENVELOPE_RETRIES_DEFAULT 5
+
+static void envelope_queue_run(struct ofono_stk *stk);
+static void timers_update(struct ofono_stk *stk);
+
+#define ADD_ERROR_RESULT(result, error, addn_info)             \
+               result.type = error;                            \
+               result.additional_len = sizeof(addn_info);      \
+               result.additional = addn_info;                  \
+
+static int stk_respond(struct ofono_stk *stk, struct stk_response *rsp,
+                       ofono_stk_generic_cb_t cb)
+{
+       const guint8 *tlv;
+       unsigned int tlv_len;
+
+       DBG("");
+
+       if (stk->driver->terminal_response == NULL)
+               return -ENOSYS;
+
+       rsp->src = STK_DEVICE_IDENTITY_TYPE_TERMINAL;
+       rsp->dst = STK_DEVICE_IDENTITY_TYPE_UICC;
+       rsp->number = stk->pending_cmd->number;
+       rsp->type = stk->pending_cmd->type;
+       rsp->qualifier = stk->pending_cmd->qualifier;
+
+       tlv = stk_pdu_from_response(rsp, &tlv_len);
+       if (tlv == NULL)
+               return -EINVAL;
+
+       stk_command_free(stk->pending_cmd);
+       stk->pending_cmd = NULL;
+       stk->cancel_cmd = NULL;
+       stk->respond_on_exit = FALSE;
+
+       stk->driver->terminal_response(stk, tlv_len, tlv, cb, stk);
+
+       return 0;
+}
+
+static void stk_command_cb(const struct ofono_error *error, void *data)
+{
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_error("TERMINAL RESPONSE to a UICC command failed");
+               return;
+       }
+
+       DBG("TERMINAL RESPONSE to a command reported no errors");
+}
+
+static void send_simple_response(struct ofono_stk *stk,
+                                       enum stk_result_type result)
+{
+       struct stk_response rsp;
+       static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+
+       DBG("result %d", result);
+
+       memset(&rsp, 0, sizeof(rsp));
+       rsp.result.type = result;
+
+       if (stk_respond(stk, &rsp, stk_command_cb))
+               stk_command_cb(&error, stk);
+}
+
+static void envelope_cb(const struct ofono_error *error, const uint8_t *data,
+                       int length, void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       struct envelope_op *op = g_queue_peek_head(stk->envelope_q);
+       gboolean result = TRUE;
+
+       DBG("length %d", length);
+
+       if (op->retries > 0 && error->type == OFONO_ERROR_TYPE_SIM &&
+                       error->error == 0x9300) {
+               op->retries--;
+               goto out;
+       }
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               result = FALSE;
+
+       g_queue_pop_head(stk->envelope_q);
+
+       if (op->cb)
+               op->cb(stk, result, data, length);
+
+       g_free(op);
+
+out:
+       envelope_queue_run(stk);
+}
+
+static void envelope_queue_run(struct ofono_stk *stk)
+{
+       if (g_queue_get_length(stk->envelope_q) > 0) {
+               struct envelope_op *op = g_queue_peek_head(stk->envelope_q);
+
+               stk->driver->envelope(stk, op->tlv_len, op->tlv,
+                                       envelope_cb, stk);
+       }
+}
+
+static int stk_send_envelope(struct ofono_stk *stk, struct stk_envelope *e,
+                               void (*cb)(struct ofono_stk *stk, gboolean ok,
+                                               const uint8_t *data,
+                                               int length), int retries)
+{
+       const uint8_t *tlv;
+       unsigned int tlv_len;
+       struct envelope_op *op;
+
+       DBG("");
+
+       if (stk->driver->envelope == NULL)
+               return -ENOSYS;
+
+       e->dst = STK_DEVICE_IDENTITY_TYPE_UICC;
+       tlv = stk_pdu_from_envelope(e, &tlv_len);
+       if (tlv == NULL)
+               return -EINVAL;
+
+       op = g_new0(struct envelope_op, 1);
+
+       op->cb = cb;
+       op->retries = retries;
+       memcpy(op->tlv, tlv, tlv_len);
+       op->tlv_len = tlv_len;
+
+       g_queue_push_tail(stk->envelope_q, op);
+
+       if (g_queue_get_length(stk->envelope_q) == 1)
+               envelope_queue_run(stk);
+
+       return 0;
+}
+
+static void stk_cbs_download_cb(struct ofono_stk *stk, gboolean ok,
+                               const unsigned char *data, int len)
+{
+       if (!ok) {
+               ofono_error("CellBroadcast download to UICC failed");
+               return;
+       }
+
+       if (len)
+               ofono_error("CellBroadcast download returned %i bytes of data",
+                               len);
+
+       DBG("CellBroadcast download to UICC reported no error");
+}
+
+void __ofono_cbs_sim_download(struct ofono_stk *stk, const struct cbs *msg)
+{
+       struct stk_envelope e;
+       int err;
+
+       DBG("");
+
+       memset(&e, 0, sizeof(e));
+
+       e.type = STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD;
+       e.src = STK_DEVICE_IDENTITY_TYPE_NETWORK;
+       memcpy(&e.cbs_pp_download.page, msg, sizeof(msg));
+
+       err = stk_send_envelope(stk, &e, stk_cbs_download_cb,
+                               ENVELOPE_RETRIES_DEFAULT);
+       if (err)
+               stk_cbs_download_cb(stk, FALSE, NULL, -1);
+}
+
+static void stk_sms_download_cb(struct ofono_stk *stk, gboolean ok,
+                               const unsigned char *data, int len)
+{
+       DBG("SMS-PP download to UICC reported %s", ok ? "success" : "error");
+
+       if (stk->sms_pp_cb)
+               stk->sms_pp_cb(ok, data, len, stk->sms_pp_userdata);
+}
+
+int __ofono_sms_sim_download(struct ofono_stk *stk, const struct sms *msg,
+                               __ofono_sms_sim_download_cb_t cb, void *data)
+{
+       struct stk_envelope e;
+
+       if (msg->type != SMS_TYPE_DELIVER)
+               return -EINVAL;
+
+       DBG("");
+
+       memset(&e, 0, sizeof(e));
+
+       e.type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD;
+       e.src = STK_DEVICE_IDENTITY_TYPE_NETWORK;
+
+       e.sms_pp_download.address.number = (char *) msg->sc_addr.address;
+       e.sms_pp_download.address.ton_npi = msg->sc_addr.numbering_plan |
+               (msg->sc_addr.number_type << 4);
+       memcpy(&e.sms_pp_download.message, &msg->deliver, sizeof(msg->deliver));
+
+       stk->sms_pp_cb = cb;
+       stk->sms_pp_userdata = data;
+
+       return stk_send_envelope(stk, &e, stk_sms_download_cb,
+                                       ENVELOPE_RETRIES_DEFAULT);
+}
+
+static char *dbus_apply_text_attributes(const char *text,
+                                       const struct stk_text_attribute *attr)
+{
+       uint16_t buf[256], *i = buf;
+       const uint8_t *j = attr->attributes;
+       const uint8_t *end = j + attr->len;
+
+       if (text == NULL)
+               return NULL;
+
+       if (attr->len & 3)
+               return NULL;
+
+       while (j < end)
+               *i++ = *j++;
+
+       return stk_text_to_html(text, buf, attr->len / 4);
+}
+
+static struct stk_menu *stk_menu_create(const char *title,
+               const struct stk_text_attribute *title_attr,
+               const struct stk_icon_id *icon, GSList *items,
+               const struct stk_item_text_attribute_list *item_attrs,
+               const struct stk_item_icon_id_list *item_icon_ids,
+               int default_id, gboolean soft_key, gboolean has_help)
+{
+       unsigned int len = g_slist_length(items);
+       struct stk_menu *ret;
+       GSList *l;
+       int i;
+       struct stk_text_attribute attr;
+
+       DBG("");
+
+       if (item_attrs && item_attrs->len && item_attrs->len != len * 4)
+               return NULL;
+
+       if (item_icon_ids && item_icon_ids->len && item_icon_ids->len != len)
+               return NULL;
+
+       ret = g_try_new(struct stk_menu, 1);
+       if (ret == NULL)
+               return NULL;
+
+       ret->title = dbus_apply_text_attributes(title ? title : "",
+                                               title_attr);
+       if (ret->title == NULL)
+               ret->title = g_strdup(title ? title : "");
+
+       memcpy(&ret->icon, icon, sizeof(ret->icon));
+       ret->items = g_new0(struct stk_menu_item, len + 1);
+       ret->default_item = -1;
+       ret->soft_key = soft_key;
+       ret->has_help = has_help;
+
+       for (l = items, i = 0; l; l = l->next, i++) {
+               struct stk_item *item = l->data;
+               char *text;
+
+               ret->items[i].item_id = item->id;
+
+               text = NULL;
+
+               if (item_attrs && item_attrs->len) {
+                       memcpy(attr.attributes, &item_attrs->list[i * 4], 4);
+                       attr.len = 4;
+
+                       text = dbus_apply_text_attributes(item->text, &attr);
+               }
+
+               if (text == NULL)
+                       text = strdup(item->text);
+
+               ret->items[i].text = text;
+
+               if (item_icon_ids && item_icon_ids->len)
+                       ret->items[i].icon_id = item_icon_ids->list[i];
+
+               if (item->id == default_id)
+                       ret->default_item = i;
+       }
+
+       return ret;
+}
+
+static struct stk_menu *stk_menu_create_from_set_up_menu(
+                                               const struct stk_command *cmd)
+{
+       gboolean soft_key = (cmd->qualifier & (1 << 0)) != 0;
+       gboolean has_help = (cmd->qualifier & (1 << 7)) != 0;
+
+       return stk_menu_create(cmd->setup_menu.alpha_id,
+                               &cmd->setup_menu.text_attr,
+                               &cmd->setup_menu.icon_id,
+                               cmd->setup_menu.items,
+                               &cmd->setup_menu.item_text_attr_list,
+                               &cmd->setup_menu.item_icon_id_list,
+                               0, soft_key, has_help);
+}
+
+static struct stk_menu *stk_menu_create_from_select_item(
+                                               const struct stk_command *cmd)
+{
+       gboolean soft_key = (cmd->qualifier & (1 << 2)) != 0;
+       gboolean has_help = (cmd->qualifier & (1 << 7)) != 0;
+
+       return stk_menu_create(cmd->select_item.alpha_id,
+                               &cmd->select_item.text_attr,
+                               &cmd->select_item.icon_id,
+                               cmd->select_item.items,
+                               &cmd->select_item.item_text_attr_list,
+                               &cmd->select_item.item_icon_id_list,
+                               cmd->select_item.item_id, soft_key, has_help);
+}
+
+static void stk_menu_free(struct stk_menu *menu)
+{
+       struct stk_menu_item *i;
+
+       for (i = menu->items; i->text; i++)
+               g_free(i->text);
+
+       g_free(menu->items);
+       g_free(menu->title);
+       g_free(menu);
+}
+
+static void emit_menu_changed(struct ofono_stk *stk)
+{
+       static struct stk_menu_item end_item = {};
+       static struct stk_menu no_menu = {
+               .title = "",
+               .items = &end_item,
+               .has_help = FALSE,
+               .default_item = -1,
+       };
+       static char *name = "MainMenu";
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(stk->atom);
+       struct stk_menu *menu = stk->main_menu ? stk->main_menu : &no_menu;
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_STK_INTERFACE,
+                                               "MainMenuTitle",
+                                               DBUS_TYPE_STRING, &menu->title);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_STK_INTERFACE,
+                                               "MainMenuIcon",
+                                               DBUS_TYPE_BYTE, &menu->icon.id);
+
+       signal = dbus_message_new_signal(path, OFONO_STK_INTERFACE,
+                                               "PropertyChanged");
+       if (signal == NULL) {
+               ofono_error("Unable to allocate new %s.PropertyChanged signal",
+                               OFONO_SIM_APP_INTERFACE);
+
+               return;
+       }
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       append_menu_items_variant(&iter, menu->items);
+
+       g_dbus_send_message(conn, signal);
+}
+
+static void cancel_pending_dtmf(struct ofono_stk *stk)
+{
+       struct ofono_voicecall *vc;
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL,
+                               __ofono_atom_get_modem(stk->atom));
+
+       if (vc) /* Should be always true here */
+               __ofono_voicecall_tone_cancel(vc, stk->dtmf_id);
+}
+
+static void user_termination_cb(enum stk_agent_result result, void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       if (result != STK_AGENT_RESULT_TERMINATE)
+               return;
+
+       switch (stk->pending_cmd->type) {
+       case STK_COMMAND_TYPE_SEND_DTMF:
+               cancel_pending_dtmf(stk);
+               break;
+       }
+
+       send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+}
+
+static gboolean stk_alpha_id_set(struct ofono_stk *stk,
+                       const char *text, const struct stk_text_attribute *attr,
+                       const struct stk_icon_id *icon)
+{
+       char *alpha = dbus_apply_text_attributes(text, attr);
+
+       /*
+        * Currently, we are treating null data object(len = 0, no value part)
+        * and no alpha identifier cases equally. This may be changed once
+        * better idea is found out.
+        */
+       if (alpha == NULL)
+               return FALSE;
+
+       if (stk->respond_on_exit)
+               stk_agent_display_action(stk->current_agent, alpha, icon,
+                                               user_termination_cb, stk, NULL);
+       else
+               stk_agent_display_action_info(stk->current_agent, alpha, icon);
+
+       g_free(alpha);
+
+       return TRUE;
+}
+
+static void stk_alpha_id_unset(struct ofono_stk *stk)
+{
+       /*
+        * If there is no default agent, then current agent also will be NULL.
+        * So, call request cancel only when there is a valid current agent.
+        */
+       if (stk->current_agent)
+               stk_agent_request_cancel(stk->current_agent);
+}
+
+static int duration_to_msecs(const struct stk_duration *duration)
+{
+       int msecs = duration->interval;
+
+       switch (duration->unit) {
+       case STK_DURATION_TYPE_MINUTES:
+               msecs *= 60;
+               /* Fall through.  */
+       case STK_DURATION_TYPE_SECONDS:
+               msecs *= 10;
+               /* Fall through.  */
+       case STK_DURATION_TYPE_SECOND_TENTHS:
+               msecs *= 100;
+       }
+
+       return msecs;
+}
+
+static DBusMessage *stk_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_stk *stk = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessageIter entry;
+       const char *key = "MainMenu";
+       const char *str;
+       unsigned char icon;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       str = stk->idle_mode_text;
+       ofono_dbus_dict_append(&dict, "IdleModeText", DBUS_TYPE_STRING, &str);
+
+       icon = stk->idle_mode_icon.id;
+       ofono_dbus_dict_append(&dict, "IdleModeIcon", DBUS_TYPE_BYTE, &icon);
+
+       str = stk->main_menu ? stk->main_menu->title : "";
+       ofono_dbus_dict_append(&dict, "MainMenuTitle", DBUS_TYPE_STRING, &str);
+
+       icon = stk->main_menu ? stk->main_menu->icon.id : 0;
+       ofono_dbus_dict_append(&dict, "MainMenuIcon", DBUS_TYPE_BYTE, &icon);
+
+       dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       append_menu_items_variant(&entry,
+                               stk->main_menu ? stk->main_menu->items : NULL);
+
+       dbus_message_iter_close_container(&dict, &entry);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void stk_request_cancel(struct ofono_stk *stk)
+{
+       if (stk->session_agent)
+               stk_agent_request_cancel(stk->session_agent);
+
+       if (stk->default_agent)
+               stk_agent_request_cancel(stk->default_agent);
+}
+
+static void default_agent_notify(gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       if (stk->current_agent == stk->default_agent && stk->respond_on_exit) {
+               if (stk->pending_cmd)
+                       stk->cancel_cmd(stk);
+
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+       }
+
+       stk->default_agent = NULL;
+       stk->current_agent = stk->session_agent;
+}
+
+static void session_agent_notify(gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       DBG("Session Agent removed");
+
+       if (stk->current_agent == stk->session_agent && stk->respond_on_exit) {
+               if (stk->pending_cmd)
+                       stk->cancel_cmd(stk);
+
+               DBG("Sending Terminate response for session agent");
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+       }
+
+       stk->session_agent = NULL;
+       stk->current_agent = stk->default_agent;
+
+       if (stk->remove_agent_source) {
+               g_source_remove(stk->remove_agent_source);
+               stk->remove_agent_source = 0;
+       }
+}
+
+static gboolean session_agent_remove_cb(gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       stk->remove_agent_source = 0;
+
+       stk_agent_free(stk->session_agent);
+
+       return FALSE;
+}
+
+/* Safely remove the agent even inside a callback */
+static void session_agent_remove(struct ofono_stk *stk)
+{
+       if (!stk->remove_agent_source)
+               stk->remove_agent_source =
+                       g_timeout_add(0, session_agent_remove_cb, stk);
+}
+
+static DBusMessage *stk_register_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_stk *stk = data;
+       const char *agent_path;
+
+       if (stk->default_agent)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_dbus_valid_object_path(agent_path))
+               return __ofono_error_invalid_format(msg);
+
+       stk->default_agent = stk_agent_new(agent_path,
+                                               dbus_message_get_sender(msg),
+                                               FALSE);
+       if (stk->default_agent == NULL)
+               return __ofono_error_failed(msg);
+
+       stk_agent_set_removed_notify(stk->default_agent,
+                                       default_agent_notify, stk);
+
+       if (stk->session_agent == NULL)
+               stk->current_agent = stk->default_agent;
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *stk_unregister_agent(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_stk *stk = data;
+       const char *agent_path;
+       const char *agent_bus = dbus_message_get_sender(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (stk->default_agent == NULL)
+               return __ofono_error_failed(msg);
+
+       if (!stk_agent_matches(stk->default_agent, agent_path, agent_bus))
+               return __ofono_error_failed(msg);
+
+       stk_agent_free(stk->default_agent);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void menu_selection_envelope_cb(struct ofono_stk *stk, gboolean ok,
+                                       const unsigned char *data, int len)
+{
+       unsigned char selection;
+       const char *agent_path;
+       DBusMessage *reply;
+
+       DBG("");
+
+       if (!ok) {
+               ofono_error("Sending Menu Selection to UICC failed");
+
+               reply = __ofono_error_failed(stk->pending);
+
+               goto out;
+       }
+
+       if (len)
+               ofono_error("Menu Selection returned %i bytes of unwanted data",
+                               len);
+
+       DBG("Menu Selection envelope submission gave no error");
+
+       dbus_message_get_args(stk->pending, NULL,
+                               DBUS_TYPE_BYTE, &selection,
+                               DBUS_TYPE_OBJECT_PATH, &agent_path,
+                               DBUS_TYPE_INVALID);
+
+       stk->session_agent = stk_agent_new(agent_path,
+                                       dbus_message_get_sender(stk->pending),
+                                       TRUE);
+       if (stk->session_agent == NULL) {
+               reply = __ofono_error_failed(stk->pending);
+
+               goto out;
+       }
+
+       stk_agent_set_removed_notify(stk->session_agent,
+                                       session_agent_notify, stk);
+
+       stk->current_agent = stk->session_agent;
+
+       reply = dbus_message_new_method_return(stk->pending);
+
+out:
+       __ofono_dbus_pending_reply(&stk->pending, reply);
+}
+
+static DBusMessage *stk_select_item(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_stk *stk = data;
+       const char *agent_path;
+       unsigned char selection, i;
+       struct stk_envelope e;
+       struct stk_menu *menu = stk->main_menu;
+
+       DBG("");
+
+       if (stk->pending || stk->session_agent)
+               return __ofono_error_busy(msg);
+
+       if (menu == NULL)
+               return __ofono_error_not_supported(msg);
+
+       if (dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_BYTE, &selection,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!__ofono_dbus_valid_object_path(agent_path))
+               return __ofono_error_invalid_format(msg);
+
+       for (i = 0; i < selection && menu->items[i].text; i++);
+
+       if (i != selection)
+               return __ofono_error_invalid_format(msg);
+
+       memset(&e, 0, sizeof(e));
+       e.type = STK_ENVELOPE_TYPE_MENU_SELECTION;
+       e.src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+       e.menu_selection.item_id = menu->items[selection].item_id;
+       e.menu_selection.help_request = FALSE;
+
+       DBG("");
+
+       if (stk_send_envelope(stk, &e, menu_selection_envelope_cb, 0))
+               return __ofono_error_failed(msg);
+
+       stk->pending = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static GDBusMethodTable stk_methods[] = {
+       { "GetProperties",              "",     "a{sv}",stk_get_properties },
+       { "SelectItem",                 "yo",   "",     stk_select_item,
+                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "RegisterAgent",              "o",    "",     stk_register_agent },
+       { "UnregisterAgent",            "o",    "",     stk_unregister_agent },
+
+       { }
+};
+
+static GDBusSignalTable stk_signals[] = {
+       { "PropertyChanged",    "sv" },
+
+       { }
+};
+
+static gboolean handle_command_more_time(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       /* Do nothing */
+
+       return TRUE;
+}
+
+static void send_sms_cancel(struct ofono_stk *stk)
+{
+       stk->extern_req->cancelled = TRUE;
+
+       stk_alpha_id_unset(stk);
+}
+
+static void send_sms_submit_cb(gboolean ok, void *data)
+{
+       struct extern_req *req = data;
+       struct ofono_stk *stk = req->stk;
+       struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE };
+       struct stk_response rsp;
+
+       DBG("SMS submission %s", ok ? "successful" : "failed");
+
+       if (req->cancelled) {
+               DBG("Received an SMS submitted callback after the "
+                               "proactive command was cancelled");
+               return;
+       }
+
+       stk_alpha_id_unset(stk);
+
+       memset(&rsp, 0, sizeof(rsp));
+
+       if (ok == FALSE)
+               rsp.result.type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE;
+
+       if (stk_respond(stk, &rsp, stk_command_cb))
+               stk_command_cb(&failure, stk);
+}
+
+static void extern_req_start(struct ofono_stk *stk)
+{
+       stk->extern_req = g_new0(struct extern_req, 1);
+       stk->extern_req->stk = stk;
+}
+
+static gboolean handle_command_send_sms(const struct stk_command *cmd,
+                                       struct stk_response *rsp,
+                                       struct ofono_stk *stk)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom);
+       struct ofono_sms *sms;
+       GSList msg_list;
+       struct ofono_uuid uuid;
+
+       sms = __ofono_atom_find(OFONO_ATOM_TYPE_SMS, modem);
+
+       if (sms == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+
+       extern_req_start(stk);
+
+       msg_list.data = (void *) &cmd->send_sms.gsm_sms;
+       msg_list.next = NULL;
+
+       if (__ofono_sms_txq_submit(sms, &msg_list, 0, &uuid, NULL, NULL) < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       __ofono_sms_txq_set_submit_notify(sms, &uuid, send_sms_submit_cb,
+                                               stk->extern_req, g_free);
+       stk->cancel_cmd = send_sms_cancel;
+
+       stk_alpha_id_set(stk, cmd->send_sms.alpha_id, &cmd->send_sms.text_attr,
+                               &cmd->send_sms.icon_id);
+
+       return FALSE;
+}
+
+/* Note: may be called from ofono_stk_proactive_command_handled_notify */
+static gboolean handle_command_set_idle_text(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(stk->atom);
+       char *idle_mode_text;
+
+       idle_mode_text = dbus_apply_text_attributes(
+                                       cmd->setup_idle_mode_text.text,
+                                       &cmd->setup_idle_mode_text.text_attr);
+
+       if (idle_mode_text == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       if (stk->idle_mode_text)
+               g_free(stk->idle_mode_text);
+
+       stk->idle_mode_text = idle_mode_text;
+
+       ofono_dbus_signal_property_changed(conn, path, OFONO_STK_INTERFACE,
+                                               "IdleModeText",
+                                               DBUS_TYPE_STRING,
+                                               &idle_mode_text);
+
+       if (stk->idle_mode_icon.id != cmd->setup_idle_mode_text.icon_id.id) {
+               memcpy(&stk->idle_mode_icon, &cmd->setup_idle_mode_text.icon_id,
+                               sizeof(stk->idle_mode_icon));
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_STK_INTERFACE,
+                                               "IdleModeIcon", DBUS_TYPE_BYTE,
+                                               &stk->idle_mode_icon.id);
+       }
+
+       return TRUE;
+}
+
+static void timer_expiration_cb(struct ofono_stk *stk, gboolean ok,
+                               const unsigned char *data, int len)
+{
+       if (!ok) {
+               ofono_error("Timer Expiration reporting failed");
+               return;
+       }
+
+       if (len)
+               ofono_error("Timer Expiration returned %i bytes of data",
+                               len);
+
+       DBG("Timer Expiration reporting to UICC reported no error");
+}
+
+static gboolean timers_cb(gpointer user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       stk->timers_source = 0;
+
+       timers_update(stk);
+
+       return FALSE;
+}
+
+static void timer_value_from_seconds(struct stk_timer_value *val, int seconds)
+{
+       val->has_value = TRUE;
+       val->hour = seconds / 3600;
+       seconds -= val->hour * 3600;
+       val->minute = seconds / 60;
+       seconds -= val->minute * 60;
+       val->second = seconds;
+}
+
+static void timers_update(struct ofono_stk *stk)
+{
+       time_t min = 0, now = time(NULL);
+       int i;
+
+       if (stk->timers_source) {
+               g_source_remove(stk->timers_source);
+               stk->timers_source = 0;
+       }
+
+       for (i = 0; i < 8; i++) {
+               if (!stk->timers[i].expiry)
+                       continue;
+
+               if (stk->timers[i].expiry <= now) {
+                       struct stk_envelope e;
+                       int seconds = now - stk->timers[i].start;
+
+                       stk->timers[i].expiry = 0;
+
+                       memset(&e, 0, sizeof(e));
+
+                       e.type = STK_ENVELOPE_TYPE_TIMER_EXPIRATION;
+                       e.src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+                       e.timer_expiration.id = i + 1;
+                       timer_value_from_seconds(&e.timer_expiration.value,
+                                                       seconds);
+
+                       /*
+                        * TODO: resubmit until success, providing current
+                        * time difference every time we re-send.
+                        */
+                       if (stk_send_envelope(stk, &e, timer_expiration_cb, 0))
+                               timer_expiration_cb(stk, FALSE, NULL, -1);
+
+                       continue;
+               }
+
+               if (stk->timers[i].expiry < now + min || min == 0)
+                       min = stk->timers[i].expiry - now;
+       }
+
+       if (min)
+               stk->timers_source = g_timeout_add_seconds(min, timers_cb, stk);
+}
+
+static gboolean handle_command_timer_mgmt(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       int op = cmd->qualifier & 3;
+       time_t seconds, now = time(NULL);
+       struct stk_timer *tmr;
+
+       if (cmd->timer_mgmt.timer_id < 1 || cmd->timer_mgmt.timer_id > 8) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       tmr = &stk->timers[cmd->timer_mgmt.timer_id - 1];
+
+       switch (op) {
+       case 0: /* Start */
+               seconds = cmd->timer_mgmt.timer_value.second +
+                       cmd->timer_mgmt.timer_value.minute * 60 +
+                       cmd->timer_mgmt.timer_value.hour * 3600;
+
+               tmr->expiry = now + seconds;
+               tmr->start = now;
+
+               timers_update(stk);
+               break;
+
+       case 1: /* Deactivate */
+               if (!tmr->expiry) {
+                       rsp->result.type = STK_RESULT_TYPE_TIMER_CONFLICT;
+
+                       return TRUE;
+               }
+
+               seconds = MAX(0, tmr->expiry - now);
+               tmr->expiry = 0;
+
+               timers_update(stk);
+
+               timer_value_from_seconds(&rsp->timer_mgmt.value, seconds);
+               break;
+
+       case 2: /* Get current value */
+               if (!tmr->expiry) {
+                       rsp->result.type = STK_RESULT_TYPE_TIMER_CONFLICT;
+
+                       return TRUE;
+               }
+
+               seconds = MAX(0, tmr->expiry - now);
+               timer_value_from_seconds(&rsp->timer_mgmt.value, seconds);
+               break;
+
+       default:
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+               return TRUE;
+       }
+
+       rsp->timer_mgmt.id = cmd->timer_mgmt.timer_id;
+
+       return TRUE;
+}
+
+static gboolean handle_command_poll_interval(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom);
+       int seconds;
+
+       if (!cmd->poll_interval.duration.interval) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       seconds = MAX(duration_to_msecs(&cmd->poll_interval.duration) / 1000,
+                       1);
+
+       ofono_modem_set_integer(modem, "status-poll-interval", seconds);
+
+       if (seconds > 255) {
+               rsp->poll_interval.max_interval.unit =
+                       STK_DURATION_TYPE_MINUTES;
+               rsp->poll_interval.max_interval.interval = seconds / 60;
+       } else {
+               rsp->poll_interval.max_interval.unit =
+                       STK_DURATION_TYPE_SECONDS;
+               rsp->poll_interval.max_interval.interval = seconds;
+       }
+
+       return TRUE;
+}
+
+/* Note: may be called from ofono_stk_proactive_command_handled_notify */
+static gboolean handle_command_set_up_menu(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       struct stk_menu *menu = NULL;
+
+       if (cmd->setup_menu.items) {
+               menu = stk_menu_create_from_set_up_menu(cmd);
+
+               if (menu == NULL) {
+                       rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+                       return TRUE;
+               }
+       }
+
+       if (menu == NULL && stk->main_menu == NULL)
+               return TRUE;
+
+       if (stk->main_menu)
+               stk_menu_free(stk->main_menu);
+
+       stk->main_menu = menu;
+
+       emit_menu_changed(stk);
+
+       return TRUE;
+}
+
+static void request_selection_destroy(void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       stk_menu_free(stk->select_item_menu);
+       stk->select_item_menu = NULL;
+}
+
+static void request_selection_cb(enum stk_agent_result result, uint8_t id,
+                                       void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       switch (result) {
+       case STK_AGENT_RESULT_OK:
+       {
+               static struct ofono_error error = {
+                       .type = OFONO_ERROR_TYPE_FAILURE
+               };
+               struct stk_response rsp;
+
+               memset(&rsp, 0, sizeof(rsp));
+
+               rsp.result.type = STK_RESULT_TYPE_SUCCESS;
+               rsp.select_item.item_id = id;
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               break;
+       }
+
+       case STK_AGENT_RESULT_BACK:
+               send_simple_response(stk, STK_RESULT_TYPE_GO_BACK);
+               break;
+
+       case STK_AGENT_RESULT_TIMEOUT:
+               send_simple_response(stk, STK_RESULT_TYPE_NO_RESPONSE);
+               break;
+
+       case STK_AGENT_RESULT_TERMINATE:
+       default:
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+               break;
+       }
+}
+
+static gboolean handle_command_select_item(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       stk->select_item_menu = stk_menu_create_from_select_item(cmd);
+
+       if (stk->select_item_menu == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+               return TRUE;
+       }
+
+       /* We most likely got an out of memory error, tell SIM to retry */
+       if (stk_agent_request_selection(stk->current_agent,
+                                       stk->select_item_menu,
+                                       request_selection_cb, stk,
+                                       request_selection_destroy,
+                                       stk->timeout * 1000) < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               request_selection_destroy(stk);
+
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       stk->cancel_cmd = stk_request_cancel;
+       stk->respond_on_exit = TRUE;
+
+       return FALSE;
+}
+
+static void display_text_destroy(void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       stk->immediate_response = FALSE;
+}
+
+static void display_text_cb(enum stk_agent_result result, void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       gboolean confirm;
+       struct stk_response rsp;
+       static unsigned char screen_busy_result[] = { 0x01 };
+       static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+
+       /*
+        * There are four possible paths for DisplayText with immediate
+        * response flag set:
+        *      1. Agent drops off the bus.  In that case regular removal
+        *      semantics apply and the agent is removed.
+        *
+        *      2. A new SIM command arrives.  In this case the agent is
+        *      canceled and a new command is processed.  This function is
+        *      not called in this case.
+        *
+        *      3. The session is ended by the SIM.  This case is ignored,
+        *      and will result in either case 1, 2 or 4 occurring.
+        *
+        *      4. Agent reports an error or success.  This function is called
+        *      with the result.
+        *
+        *      NOTE: If the agent reports a TERMINATE result, the agent will
+        *      be removed.  Since the response has been already sent, there
+        *      is no way to signal the end of session to the SIM.  Hence
+        *      it is assumed that immediate response flagged commands will
+        *      only occur at the end of session.
+        */
+       if (stk->immediate_response) {
+               if (stk->session_agent)
+                       session_agent_remove(stk);
+
+               return;
+       }
+
+       switch (result) {
+       case STK_AGENT_RESULT_OK:
+               send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+               break;
+
+       case STK_AGENT_RESULT_BACK:
+               send_simple_response(stk, STK_RESULT_TYPE_GO_BACK);
+               break;
+
+       case STK_AGENT_RESULT_TIMEOUT:
+               confirm = (stk->pending_cmd->qualifier & (1 << 7)) != 0;
+               send_simple_response(stk, confirm ?
+                       STK_RESULT_TYPE_NO_RESPONSE : STK_RESULT_TYPE_SUCCESS);
+               break;
+
+       case STK_AGENT_RESULT_BUSY:
+               memset(&rsp, 0, sizeof(rsp));
+               ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       screen_busy_result);
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+               break;
+
+       case STK_AGENT_RESULT_TERMINATE:
+       default:
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+               break;
+       }
+}
+
+static gboolean handle_command_display_text(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       int timeout = stk->short_timeout * 1000;
+       struct stk_command_display_text *dt = &stk->pending_cmd->display_text;
+       uint8_t qualifier = stk->pending_cmd->qualifier;
+       ofono_bool_t priority = (qualifier & (1 << 0)) != 0;
+       char *text = dbus_apply_text_attributes(dt->text, &dt->text_attr);
+       int err;
+
+       if (text == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       if (qualifier & (1 << 7))
+               timeout = stk->timeout * 1000;
+
+       if (dt->duration.interval)
+               timeout = duration_to_msecs(&dt->duration);
+
+       err = stk_agent_display_text(stk->current_agent, text, &dt->icon_id,
+                                       priority, display_text_cb, stk,
+                                       display_text_destroy, timeout);
+       g_free(text);
+
+       /* We most likely got an out of memory error, tell SIM to retry */
+       if (err < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       if (cmd->display_text.immediate_response)
+               stk->immediate_response = TRUE;
+
+       DBG("Immediate Response: %d", stk->immediate_response);
+
+       if (stk->immediate_response == FALSE) {
+               stk->respond_on_exit = TRUE;
+               stk->cancel_cmd = stk_request_cancel;
+       }
+
+       return stk->immediate_response;
+}
+
+static void set_get_inkey_duration(struct stk_duration *duration,
+                                       struct timeval *start_ts)
+{
+       struct timeval end_ts;
+       int interval;
+
+       gettimeofday(&end_ts, NULL);
+
+       interval = (end_ts.tv_usec + 1099999 - start_ts->tv_usec) / 100000;
+       interval += (end_ts.tv_sec - start_ts->tv_sec) * 10;
+       interval -= 10;
+
+       switch (duration->unit) {
+       case STK_DURATION_TYPE_MINUTES:
+               interval = (interval + 59) / 60;
+       case STK_DURATION_TYPE_SECONDS:
+               interval = (interval + 9) / 10;
+       case STK_DURATION_TYPE_SECOND_TENTHS:
+               break;
+       }
+
+       duration->interval = interval;
+}
+
+static void request_confirmation_cb(enum stk_agent_result result,
+                                       gboolean confirm,
+                                       void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+       struct stk_command_get_inkey *cmd = &stk->pending_cmd->get_inkey;
+       struct stk_response rsp;
+
+       switch (result) {
+       case STK_AGENT_RESULT_OK:
+               memset(&rsp, 0, sizeof(rsp));
+
+               rsp.result.type = STK_RESULT_TYPE_SUCCESS;
+               rsp.get_inkey.text.text = confirm ? "" : NULL;
+               rsp.get_inkey.text.yesno = TRUE;
+
+               if (cmd->duration.interval) {
+                       rsp.get_inkey.duration.unit = cmd->duration.unit;
+                       set_get_inkey_duration(&rsp.get_inkey.duration,
+                                               &stk->get_inkey_start_ts);
+               }
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               break;
+
+       case STK_AGENT_RESULT_BACK:
+               send_simple_response(stk, STK_RESULT_TYPE_GO_BACK);
+               break;
+
+       case STK_AGENT_RESULT_TIMEOUT:
+               memset(&rsp, 0, sizeof(rsp));
+
+               rsp.result.type = STK_RESULT_TYPE_NO_RESPONSE;
+
+               if (cmd->duration.interval) {
+                       rsp.get_inkey.duration.unit = cmd->duration.unit;
+                       set_get_inkey_duration(&rsp.get_inkey.duration,
+                                               &stk->get_inkey_start_ts);
+               }
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               break;
+
+       case STK_AGENT_RESULT_TERMINATE:
+       default:
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+               break;
+       }
+}
+
+static void request_key_cb(enum stk_agent_result result, char *string,
+                               void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+       struct stk_command_get_inkey *cmd = &stk->pending_cmd->get_inkey;
+       struct stk_response rsp;
+
+       switch (result) {
+       case STK_AGENT_RESULT_OK:
+               memset(&rsp, 0, sizeof(rsp));
+
+               rsp.result.type = STK_RESULT_TYPE_SUCCESS;
+               rsp.get_inkey.text.text = string;
+
+               if (cmd->duration.interval) {
+                       rsp.get_inkey.duration.unit = cmd->duration.unit;
+                       set_get_inkey_duration(&rsp.get_inkey.duration,
+                                               &stk->get_inkey_start_ts);
+               }
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               break;
+
+       case STK_AGENT_RESULT_BACK:
+               send_simple_response(stk, STK_RESULT_TYPE_GO_BACK);
+               break;
+
+       case STK_AGENT_RESULT_TIMEOUT:
+               memset(&rsp, 0, sizeof(rsp));
+
+               rsp.result.type = STK_RESULT_TYPE_NO_RESPONSE;
+
+               if (cmd->duration.interval) {
+                       rsp.get_inkey.duration.unit = cmd->duration.unit;
+                       set_get_inkey_duration(&rsp.get_inkey.duration,
+                                               &stk->get_inkey_start_ts);
+               }
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               break;
+
+       case STK_AGENT_RESULT_TERMINATE:
+       default:
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+               break;
+       }
+}
+
+static gboolean handle_command_get_inkey(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       int timeout = stk->timeout * 1000;
+       const struct stk_command_get_inkey *gi = &cmd->get_inkey;
+       char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr);
+       uint8_t qualifier = stk->pending_cmd->qualifier;
+       gboolean alphabet = (qualifier & (1 << 0)) != 0;
+       gboolean ucs2 = (qualifier & (1 << 1)) != 0;
+       gboolean yesno = (qualifier & (1 << 2)) != 0;
+       /*
+        * Note: immediate response and help parameter values are not
+        * provided by current api.
+        */
+       int err;
+
+       if (text == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       if (gi->duration.interval)
+               timeout = duration_to_msecs(&gi->duration);
+
+       gettimeofday(&stk->get_inkey_start_ts, NULL);
+
+       if (yesno)
+               err = stk_agent_request_confirmation(stk->current_agent,
+                                                       text, &gi->icon_id,
+                                                       request_confirmation_cb,
+                                                       stk, NULL, timeout);
+       else if (alphabet)
+               err = stk_agent_request_key(stk->current_agent, text,
+                                               &gi->icon_id, ucs2,
+                                               request_key_cb, stk, NULL,
+                                               timeout);
+       else
+               err = stk_agent_request_digit(stk->current_agent, text,
+                                               &gi->icon_id, request_key_cb,
+                                               stk, NULL, timeout);
+
+       g_free(text);
+
+       if (err < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               /*
+                * We most likely got an out of memory error, tell SIM
+                * to retry
+                */
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       stk->respond_on_exit = TRUE;
+       stk->cancel_cmd = stk_request_cancel;
+
+       return FALSE;
+}
+
+static void request_string_cb(enum stk_agent_result result, char *string,
+                               void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+       uint8_t qualifier = stk->pending_cmd->qualifier;
+       gboolean packed = (qualifier & (1 << 3)) != 0;
+       struct stk_response rsp;
+
+       switch (result) {
+       case STK_AGENT_RESULT_OK:
+               memset(&rsp, 0, sizeof(rsp));
+
+               rsp.result.type = STK_RESULT_TYPE_SUCCESS;
+               rsp.get_input.text.text = string;
+               rsp.get_input.text.packed = packed;
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               break;
+
+       case STK_AGENT_RESULT_BACK:
+               send_simple_response(stk, STK_RESULT_TYPE_GO_BACK);
+               break;
+
+       case STK_AGENT_RESULT_TIMEOUT:
+               send_simple_response(stk, STK_RESULT_TYPE_NO_RESPONSE);
+               break;
+
+       case STK_AGENT_RESULT_TERMINATE:
+       default:
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+               break;
+       }
+}
+
+static gboolean handle_command_get_input(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       int timeout = stk->timeout * 1000;
+       const struct stk_command_get_input *gi = &cmd->get_input;
+       char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr);
+       uint8_t qualifier = stk->pending_cmd->qualifier;
+       gboolean alphabet = (qualifier & (1 << 0)) != 0;
+       gboolean ucs2 = (qualifier & (1 << 1)) != 0;
+       gboolean hidden = (qualifier & (1 << 2)) != 0;
+       int err;
+
+       if (text == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       if (alphabet)
+               err = stk_agent_request_input(stk->current_agent, text,
+                                               &gi->icon_id, gi->default_text,
+                                               ucs2, gi->resp_len.min,
+                                               gi->resp_len.max, hidden,
+                                               request_string_cb,
+                                               stk, NULL, timeout);
+       else
+               err = stk_agent_request_digits(stk->current_agent, text,
+                                               &gi->icon_id, gi->default_text,
+                                               gi->resp_len.min,
+                                               gi->resp_len.max, hidden,
+                                               request_string_cb,
+                                               stk, NULL, timeout);
+
+       g_free(text);
+
+       if (err < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               /*
+                * We most likely got an out of memory error, tell SIM
+                * to retry
+                */
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       stk->respond_on_exit = TRUE;
+       stk->cancel_cmd = stk_request_cancel;
+
+       return FALSE;
+}
+
+static void call_setup_connected(struct ofono_call *call, void *data)
+{
+       struct ofono_stk *stk = data;
+       struct stk_response rsp;
+       static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+       static unsigned char facility_rejected_result[] = { 0x9d };
+
+       if (call == NULL || call->status == CALL_STATUS_DISCONNECTED) {
+               memset(&rsp, 0, sizeof(rsp));
+
+               ADD_ERROR_RESULT(rsp.result,
+                                       STK_RESULT_TYPE_NETWORK_UNAVAILABLE,
+                                       facility_rejected_result);
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               return;
+       }
+
+       if (call->status == CALL_STATUS_ACTIVE)
+               send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+       else
+               send_simple_response(stk, STK_RESULT_TYPE_USER_CANCEL);
+}
+
+static void call_setup_cancel(struct ofono_stk *stk)
+{
+       struct ofono_voicecall *vc;
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL,
+                               __ofono_atom_get_modem(stk->atom));
+
+       if (vc)
+               __ofono_voicecall_dial_cancel(vc);
+}
+
+static void confirm_call_cb(enum stk_agent_result result, gboolean confirm,
+                               void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+       const struct stk_command_setup_call *sc = &stk->pending_cmd->setup_call;
+       uint8_t qualifier = stk->pending_cmd->qualifier;
+       static unsigned char busy_on_call_result[] = { 0x02 };
+       static unsigned char no_cause_result[] = { 0x00 };
+       char *alpha_id = NULL;
+       struct ofono_voicecall *vc;
+       struct stk_response rsp;
+       int err;
+
+       switch (result) {
+       case STK_AGENT_RESULT_TIMEOUT:
+               confirm = FALSE;
+               /* Fall through */
+
+       case STK_AGENT_RESULT_OK:
+               if (confirm)
+                       break;
+
+               send_simple_response(stk, STK_RESULT_TYPE_USER_REJECT);
+               return;
+
+       case STK_AGENT_RESULT_TERMINATE:
+       default:
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+               return;
+       }
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL,
+                               __ofono_atom_get_modem(stk->atom));
+       if (vc == NULL) {
+               send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE);
+               return;
+       }
+
+       if (sc->alpha_id_call_setup) {
+               alpha_id = dbus_apply_text_attributes(sc->alpha_id_call_setup,
+                                               &sc->text_attr_call_setup);
+               if (alpha_id == NULL) {
+                       send_simple_response(stk,
+                                       STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD);
+                       return;
+               }
+       }
+
+       err = __ofono_voicecall_dial(vc, sc->addr.number, sc->addr.ton_npi,
+                                       alpha_id, sc->icon_id_call_setup.id,
+                                       qualifier >> 1, call_setup_connected,
+                                       stk);
+       g_free(alpha_id);
+
+       if (err >= 0) {
+               stk->cancel_cmd = call_setup_cancel;
+
+               return;
+       }
+
+       if (err == -EBUSY) {
+               memset(&rsp, 0, sizeof(rsp));
+
+               ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       busy_on_call_result);
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&error, stk);
+
+               return;
+       }
+
+       if (err == -ENOSYS) {
+               send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE);
+
+               return;
+       }
+
+       memset(&rsp, 0, sizeof(rsp));
+
+       ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_NETWORK_UNAVAILABLE,
+                               no_cause_result);
+
+       if (stk_respond(stk, &rsp, stk_command_cb))
+               stk_command_cb(&error, stk);
+}
+
+static void confirm_handled_call_cb(enum stk_agent_result result,
+                                       gboolean confirm, void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       const struct stk_command_setup_call *sc =
+                                       &stk->pending_cmd->setup_call;
+       struct ofono_voicecall *vc;
+
+       if (stk->driver->user_confirmation == NULL)
+               goto out;
+
+       if (result != STK_AGENT_RESULT_OK) {
+               stk->driver->user_confirmation(stk, FALSE);
+               goto out;
+       }
+
+       stk->driver->user_confirmation(stk, confirm);
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL,
+                               __ofono_atom_get_modem(stk->atom));
+       if (vc == NULL)
+               goto out;
+
+       __ofono_voicecall_set_alpha_and_icon_id(vc, sc->addr.number,
+                                               sc->addr.ton_npi,
+                                               sc->alpha_id_call_setup,
+                                               sc->icon_id_call_setup.id);
+
+       return;
+
+out:
+       stk_command_free(stk->pending_cmd);
+       stk->pending_cmd = NULL;
+}
+
+static gboolean handle_command_set_up_call(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       const struct stk_command_setup_call *sc = &cmd->setup_call;
+       uint8_t qualifier = cmd->qualifier;
+       static unsigned char busy_on_call_result[] = { 0x02 };
+       char *alpha_id = NULL;
+       struct ofono_voicecall *vc = NULL;
+       int err;
+
+       if (qualifier > 5) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       /*
+        * Passing called party subaddress and establishing non-speech
+        * calls are not supported.
+        */
+       if (sc->ccp.len || sc->subaddr.len) {
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL,
+                               __ofono_atom_get_modem(stk->atom));
+       if (vc == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+
+       if (__ofono_voicecall_is_busy(vc, qualifier >> 1)) {
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       busy_on_call_result);
+               return TRUE;
+       }
+
+       alpha_id = dbus_apply_text_attributes(sc->alpha_id_usr_cfm ?
+                                               sc->alpha_id_usr_cfm : "",
+                                               &sc->text_attr_usr_cfm);
+       if (alpha_id == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       err = stk_agent_confirm_call(stk->current_agent, alpha_id,
+                                       &sc->icon_id_usr_cfm, confirm_call_cb,
+                                       stk, NULL, stk->timeout * 1000);
+       g_free(alpha_id);
+
+       if (err < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               /*
+                * We most likely got an out of memory error, tell SIM
+                * to retry
+                */
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       stk->respond_on_exit = TRUE;
+       stk->cancel_cmd = stk_request_cancel;
+
+       return FALSE;
+}
+
+static void send_ussd_cancel(struct ofono_stk *stk)
+{
+       struct ofono_ussd *ussd;
+
+       ussd = __ofono_atom_find(OFONO_ATOM_TYPE_USSD,
+                                       __ofono_atom_get_modem(stk->atom));
+       if (ussd)
+               __ofono_ussd_initiate_cancel(ussd);
+
+       stk_alpha_id_unset(stk);
+}
+
+static void send_ussd_callback(int error, int dcs, const unsigned char *msg,
+                               int msg_len, void *userdata)
+{
+       struct ofono_stk *stk = userdata;
+       struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE };
+       struct stk_response rsp;
+       enum sms_charset charset;
+       unsigned char no_cause[] = { 0x00 };
+
+       stk_alpha_id_unset(stk);
+
+       memset(&rsp, 0, sizeof(rsp));
+
+       switch (error) {
+       case 0:
+               if (cbs_dcs_decode(dcs, NULL, NULL, &charset,
+                                       NULL, NULL, NULL)) {
+                       if (charset == SMS_CHARSET_7BIT)
+                               rsp.send_ussd.text.dcs = 0x00;
+                       else if (charset == SMS_CHARSET_8BIT)
+                               rsp.send_ussd.text.dcs = 0x04;
+                       else if (charset == SMS_CHARSET_UCS2)
+                               rsp.send_ussd.text.dcs = 0x08;
+
+                       rsp.result.type = STK_RESULT_TYPE_SUCCESS;
+                       rsp.send_ussd.text.text = msg;
+                       rsp.send_ussd.text.len = msg_len;
+                       rsp.send_ussd.text.has_text = TRUE;
+               } else
+                       rsp.result.type = STK_RESULT_TYPE_USSD_RETURN_ERROR;
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&failure, stk);
+
+               break;
+
+       case -ECANCELED:
+               send_simple_response(stk,
+                               STK_RESULT_TYPE_USSD_OR_SS_USER_TERMINATION);
+               break;
+
+       case -ETIMEDOUT:
+               send_simple_response(stk, STK_RESULT_TYPE_NETWORK_UNAVAILABLE);
+               break;
+
+       default:
+               ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_USSD_RETURN_ERROR,
+                                       no_cause);
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&failure, stk);
+
+               break;
+       }
+}
+
+static gboolean ss_is_busy(struct ofono_modem *modem)
+{
+       struct ofono_atom *atom;
+
+       atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_CALL_FORWARDING);
+       if (atom != NULL) {
+               struct ofono_call_forwarding *cf = __ofono_atom_get_data(atom);
+
+               if (__ofono_call_forwarding_is_busy(cf))
+                       return TRUE;
+       }
+
+       atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_CALL_BARRING);
+       if (atom != NULL) {
+               struct ofono_call_barring *cb = __ofono_atom_get_data(atom);
+
+               if (__ofono_call_barring_is_busy(cb))
+                       return TRUE;
+       }
+
+       atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_CALL_SETTINGS);
+       if (atom != NULL) {
+               struct ofono_call_settings *cs = __ofono_atom_get_data(atom);
+
+               if (__ofono_call_settings_is_busy(cs))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean handle_command_send_ussd(const struct stk_command *cmd,
+                                       struct stk_response *rsp,
+                                       struct ofono_stk *stk)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom);
+       static unsigned char busy_on_ss_result[] = { 0x03 };
+       static unsigned char busy_on_ussd_result[] = { 0x08 };
+       struct ofono_ussd *ussd;
+       int err;
+
+       ussd = __ofono_atom_find(OFONO_ATOM_TYPE_USSD, modem);
+       if (ussd == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+
+       if (__ofono_ussd_is_busy(ussd)) {
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       busy_on_ussd_result);
+               return TRUE;
+       }
+
+       if (ss_is_busy(modem)) {
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       busy_on_ss_result);
+               return TRUE;
+       }
+
+       err = __ofono_ussd_initiate(ussd, cmd->send_ussd.ussd_string.dcs,
+                                       cmd->send_ussd.ussd_string.string,
+                                       cmd->send_ussd.ussd_string.len,
+                                       send_ussd_callback, stk);
+
+       if (err >= 0) {
+               stk->cancel_cmd = send_ussd_cancel;
+
+               return FALSE;
+       }
+
+       if (err == -ENOSYS) {
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+
+       if (err == -EBUSY) {
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       busy_on_ussd_result);
+               return TRUE;
+       }
+
+       stk_alpha_id_set(stk, cmd->send_ussd.alpha_id,
+                               &cmd->send_ussd.text_attr,
+                               &cmd->send_ussd.icon_id);
+
+       return FALSE;
+}
+
+static void free_idle_mode_text(struct ofono_stk *stk)
+{
+       g_free(stk->idle_mode_text);
+       stk->idle_mode_text = NULL;
+
+       memset(&stk->idle_mode_icon, 0, sizeof(stk->idle_mode_icon));
+}
+
+/* Note: may be called from ofono_stk_proactive_command_handled_notify */
+static gboolean handle_command_refresh(const struct stk_command *cmd,
+                                       struct stk_response *rsp,
+                                       struct ofono_stk *stk)
+{
+       struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE };
+       struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom);
+       struct ofono_sim *sim;
+       uint8_t addnl_info[1];
+       int err;
+       GSList *l;
+
+       DBG("");
+
+       switch (cmd->qualifier) {
+       case 0:
+               DBG("NAA Initialization and "
+                               "Full File Change Notification");
+               break;
+
+       case 1:
+               DBG("File Change Notification");
+               break;
+
+       case 2:
+               DBG("NAA Initialization and File Change Notification");
+               break;
+
+       case 3:
+               DBG("NAA Initialization");
+               break;
+
+       case 4:
+               DBG("UICC Reset");
+               break;
+
+       case 5:
+               DBG("NAA Application Reset");
+               break;
+
+       case 6:
+               DBG("NAA Session Reset");
+               break;
+
+       default:
+               ofono_info("Undefined Refresh qualifier: %d", cmd->qualifier);
+
+               if (rsp != NULL)
+                       rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+
+               return TRUE;
+       }
+
+       DBG("Files:");
+       for (l = cmd->refresh.file_list; l; l = l->next) {
+               struct stk_file *file = l->data;
+               char buf[17];
+
+               encode_hex_own_buf(file->file, file->len, 0, buf);
+               DBG("%s", buf);
+       }
+
+       DBG("Icon: %d, qualifier: %d", cmd->refresh.icon_id.id,
+                                       cmd->refresh.icon_id.qualifier);
+       DBG("Alpha ID: %s", cmd->refresh.alpha_id);
+
+       sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+       if (sim == NULL) {
+               if (rsp != NULL)
+                       rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+
+               return TRUE;
+       }
+
+       if (rsp != NULL) {
+               struct ofono_ussd *ussd;
+               struct ofono_voicecall *vc;
+
+               ussd = __ofono_atom_find(OFONO_ATOM_TYPE_USSD, modem);
+
+               if (ussd && __ofono_ussd_is_busy(ussd)) {
+                       addnl_info[0] = STK_RESULT_ADDNL_ME_PB_USSD_BUSY;
+
+                       ADD_ERROR_RESULT(rsp->result,
+                                       STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       addnl_info);
+                       return TRUE;
+               }
+
+               vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem);
+
+               if (vc && __ofono_voicecall_is_busy(vc,
+                                       OFONO_VOICECALL_INTERACTION_NONE)) {
+                       addnl_info[0] = STK_RESULT_ADDNL_ME_PB_BUSY_ON_CALL;
+
+                       ADD_ERROR_RESULT(rsp->result,
+                                       STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       addnl_info);
+                       return TRUE;
+               }
+
+               if (ss_is_busy(__ofono_atom_get_modem(stk->atom))) {
+                       addnl_info[0] = STK_RESULT_ADDNL_ME_PB_SS_BUSY;
+
+                       ADD_ERROR_RESULT(rsp->result,
+                                       STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       addnl_info);
+                       return TRUE;
+               }
+       }
+
+       /*
+        * For now we can handle the Refresh types that don't require
+        * a SIM reset except if that part of the task has been already
+        * handled by modem firmware (indicated by rsp == NULL) in which
+        * case we just restart our SIM initialisation.
+        */
+       if (cmd->qualifier < 4 || rsp == NULL) {
+               int qualifier = stk->pending_cmd->qualifier;
+               GSList *file_list = stk->pending_cmd->refresh.file_list;
+
+               /* Don't free the list yet */
+               stk->pending_cmd->refresh.file_list = NULL;
+
+               /*
+                * Queue the TERMINAL RESPONSE before triggering potential
+                * file accesses.
+                *
+                * TODO: Find out if we need to send the "Refresh performed
+                * with additional EFs read" response.
+                */
+               if (rsp != NULL) {
+                       err = stk_respond(stk, rsp, stk_command_cb);
+                       if (err)
+                               stk_command_cb(&failure, stk);
+               }
+
+               /* TODO: use the alphaId / icon */
+               /* TODO: if AID is supplied, check its value */
+               /* TODO: possibly check if a D-bus call is pending or
+                * an STK session ongoing. */
+
+               /* TODO: free some elements of the atom state */
+
+               switch (qualifier) {
+               case 0:
+                       free_idle_mode_text(stk);
+                       __ofono_sim_refresh(sim, file_list, TRUE, TRUE);
+                       break;
+               case 1:
+                       __ofono_sim_refresh(sim, file_list, FALSE, FALSE);
+                       break;
+               case 2:
+               case 3:
+               case 4:
+               case 5:
+               case 6:
+                       free_idle_mode_text(stk);
+                       __ofono_sim_refresh(sim, file_list, FALSE, TRUE);
+                       break;
+               }
+
+               g_slist_foreach(file_list, (GFunc) g_free, NULL);
+               g_slist_free(file_list);
+
+               return FALSE;
+       }
+
+       rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+       return TRUE;
+}
+
+static void get_time(struct stk_response *rsp)
+{
+       time_t now;
+       struct tm *t;
+
+       time(&now);
+       t = localtime(&now);
+
+       rsp->result.type = STK_RESULT_TYPE_SUCCESS;
+
+       if (t->tm_year > 100)
+               rsp->provide_local_info.datetime.year = t->tm_year - 100;
+       else
+               rsp->provide_local_info.datetime.year = t->tm_year;
+
+       rsp->provide_local_info.datetime.month = t->tm_mon + 1;
+       rsp->provide_local_info.datetime.day = t->tm_mday;
+       rsp->provide_local_info.datetime.hour = t->tm_hour;
+       rsp->provide_local_info.datetime.minute = t->tm_min;
+       rsp->provide_local_info.datetime.second = t->tm_sec;
+       rsp->provide_local_info.datetime.timezone = t->tm_gmtoff / 900;
+       rsp->provide_local_info.datetime.has_timezone = TRUE;
+
+       return;
+}
+
+static void get_lang(struct stk_response *rsp, struct ofono_stk *stk)
+{
+       char *l;
+       char lang[3];
+       struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE };
+
+       l = getenv("LANG");
+       if (l == NULL) {
+               l = "en";
+               ofono_warn("LANG environment variable not set"
+                               " - defaulting to en");
+       }
+
+       memcpy(lang, l, 2);
+       lang[2] = '\0';
+
+       rsp->result.type = STK_RESULT_TYPE_SUCCESS;
+       rsp->provide_local_info.language = lang;
+
+       if (stk_respond(stk, rsp, stk_command_cb))
+               stk_command_cb(&failure, stk);
+}
+
+static gboolean handle_command_provide_local_info(const struct stk_command *cmd,
+                               struct stk_response *rsp, struct ofono_stk *stk)
+{
+       switch (cmd->qualifier) {
+       case 3:
+               DBG("Date, time and time zone");
+               get_time(rsp);
+               return TRUE;
+
+       case 4:
+               DBG("Language setting");
+               get_lang(rsp, stk);
+               return FALSE;
+
+       default:
+               ofono_info("Unsupported Provide Local Info qualifier: %d",
+                               cmd->qualifier);
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+}
+
+static void send_dtmf_cancel(struct ofono_stk *stk)
+{
+       cancel_pending_dtmf(stk);
+       stk_alpha_id_unset(stk);
+}
+
+static void dtmf_sent_cb(int error, void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       stk_alpha_id_unset(stk);
+
+       if (error == ENOENT) {
+               struct stk_response rsp;
+               static unsigned char not_in_speech_call_result[] = { 0x07 };
+               static struct ofono_error failure = {
+                       .type = OFONO_ERROR_TYPE_FAILURE
+               };
+
+               memset(&rsp, 0, sizeof(rsp));
+
+               ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       not_in_speech_call_result);
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&failure, stk);
+
+               return;
+       }
+
+       if (error != 0)
+               send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE);
+       else
+               send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+}
+
+static gboolean handle_command_send_dtmf(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       static unsigned char not_in_speech_call_result[] = { 0x07 };
+       struct ofono_voicecall *vc = NULL;
+       char dtmf[256], *digit;
+       char *dtmf_from = "01234567890abcABC";
+       char *dtmf_to = "01234567890*#p*#p";
+       int err, pos;
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL,
+                               __ofono_atom_get_modem(stk->atom));
+       if (vc == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+
+       /* Convert the DTMF string to phone number format */
+       for (pos = 0; cmd->send_dtmf.dtmf[pos] != '\0'; pos++) {
+               digit = strchr(dtmf_from, cmd->send_dtmf.dtmf[pos]);
+               if (digit == NULL) {
+                       rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+                       return TRUE;
+               }
+
+               dtmf[pos] = dtmf_to[digit - dtmf_from];
+       }
+
+       dtmf[pos] = '\0';
+
+       err = __ofono_voicecall_tone_send(vc, dtmf, dtmf_sent_cb, stk);
+
+       if (err == -ENOSYS) {
+               rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+               return TRUE;
+       }
+
+       if (err == -ENOENT) {
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       not_in_speech_call_result);
+               return TRUE;
+       }
+
+       if (err == -EINVAL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       if (err < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               /*
+                * We most likely got an out of memory error, tell SIM
+                * to retry
+                */
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       /*
+        * Note that we don't strictly require an agent to be connected,
+        * but to comply with 6.4.24 we need to send a End Session when
+        * the user decides so.
+        */
+       stk->respond_on_exit = TRUE;
+       stk->cancel_cmd = send_dtmf_cancel;
+       stk->dtmf_id = err;
+
+       stk_alpha_id_set(stk, cmd->send_dtmf.alpha_id,
+                               &cmd->send_dtmf.text_attr,
+                               &cmd->send_dtmf.icon_id);
+
+       return FALSE;
+}
+
+static void play_tone_cb(enum stk_agent_result result, void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+
+       switch (result) {
+       case STK_AGENT_RESULT_OK:
+       case STK_AGENT_RESULT_TIMEOUT:
+               send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+               break;
+
+       default:
+               send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+               break;
+       }
+}
+
+static gboolean handle_command_play_tone(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       static int manufacturer_timeout = 10000; /* 10 seconds */
+       static const struct {
+               const char *name;
+               /* Continuous true/false according to 02.40 */
+               gboolean continuous;
+       } tone_infos[] = {
+               /* Default */
+               [0x00] = { "general-beep", FALSE },
+
+               /* Standard */
+               [0x01] = { "dial-tone", TRUE },
+               [0x02] = { "busy", TRUE },
+               [0x03] = { "congestion", TRUE },
+               [0x04] = { "radio-path-acknowledge", FALSE },
+               [0x05] = { "radio-path-not-available", FALSE },
+               [0x06] = { "error", TRUE },
+               [0x07] = { "call-waiting", FALSE },
+               [0x08] = { "ringing-tone", TRUE },
+
+               /* Proprietary */
+               [0x10] = { "general-beep", FALSE },
+               [0x11] = { "positive-acknowledgement", FALSE },
+               [0x12] = { "negative-acknowledgement", FALSE },
+               [0x13] = { "user-ringing-tone", TRUE },
+               [0x14] = { "user-sms-alert", FALSE },
+               [0x15] = { "critical", FALSE },
+               [0x20] = { "vibrate", TRUE },
+
+               /* Themed */
+               [0x30] = { "happy", FALSE },
+               [0x31] = { "sad", FALSE },
+               [0x32] = { "urgent-action", FALSE },
+               [0x33] = { "question", FALSE },
+               [0x34] = { "message-received", FALSE },
+
+               /* Melody */
+               [0x40] = { "melody-1", FALSE },
+               [0x41] = { "melody-2", FALSE },
+               [0x42] = { "melody-3", FALSE },
+               [0x43] = { "melody-4", FALSE },
+               [0x44] = { "melody-5", FALSE },
+               [0x45] = { "melody-6", FALSE },
+               [0x46] = { "melody-7", FALSE },
+               [0x47] = { "melody-8", FALSE },
+       };
+
+       const struct stk_command_play_tone *pt = &cmd->play_tone;
+       uint8_t qualifier = stk->pending_cmd->qualifier;
+       gboolean vibrate = (qualifier & (1 << 0)) != 0;
+       char *text;
+       int timeout;
+       int err;
+
+       if (pt->tone > sizeof(tone_infos) / sizeof(*tone_infos) ||
+                       tone_infos[pt->tone].name == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+               return TRUE;
+       }
+
+       text = dbus_apply_text_attributes(pt->alpha_id ? pt->alpha_id : "",
+                                               &pt->text_attr);
+       if (text == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+               return TRUE;
+       }
+
+       if (pt->duration.interval)
+               timeout = duration_to_msecs(&pt->duration);
+       else
+               timeout = manufacturer_timeout;
+
+       if (!tone_infos[pt->tone].continuous)
+               /* Duration ignored */
+               err = stk_agent_play_tone(stk->current_agent, text,
+                                               &pt->icon_id, vibrate,
+                                               tone_infos[pt->tone].name,
+                                               play_tone_cb, stk, NULL,
+                                               stk->timeout * 1000);
+       else
+               err = stk_agent_loop_tone(stk->current_agent, text,
+                                               &pt->icon_id, vibrate,
+                                               tone_infos[pt->tone].name,
+                                               play_tone_cb, stk, NULL,
+                                               timeout);
+
+       g_free(text);
+
+       if (err < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               /*
+                * We most likely got an out of memory error, tell SIM
+                * to retry
+                */
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       stk->respond_on_exit = TRUE;
+       stk->cancel_cmd = stk_request_cancel;
+
+       return FALSE;
+}
+
+static void confirm_launch_browser_cb(enum stk_agent_result result,
+                                       gboolean confirm,
+                                       void *user_data)
+{
+       struct ofono_stk *stk = user_data;
+       unsigned char no_cause[] = { 0x00 };
+       struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE };
+       struct stk_response rsp;
+
+       switch (result) {
+       case STK_AGENT_RESULT_TIMEOUT:
+               confirm = FALSE;
+               /* Fall through */
+
+       case STK_AGENT_RESULT_OK:
+               if (confirm)
+                       break;
+               /* Fall through */
+
+       default:
+               memset(&rsp, 0, sizeof(rsp));
+               ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause);
+
+               if (stk_respond(stk, &rsp, stk_command_cb))
+                       stk_command_cb(&failure, stk);
+
+               return;
+       }
+
+       send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+}
+
+static gboolean handle_command_launch_browser(const struct stk_command *cmd,
+                                               struct stk_response *rsp,
+                                               struct ofono_stk *stk)
+{
+       const struct stk_command_launch_browser *lb = &cmd->launch_browser;
+       char *alpha_id;
+       int err;
+
+       alpha_id = dbus_apply_text_attributes(lb->alpha_id ? lb->alpha_id : "",
+                                                       &lb->text_attr);
+       if (alpha_id == NULL) {
+               rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+               return TRUE;
+       }
+
+       err = stk_agent_confirm_launch_browser(stk->current_agent, alpha_id,
+                                               lb->icon_id.id, lb->url,
+                                               confirm_launch_browser_cb,
+                                               stk, NULL, stk->timeout * 1000);
+       g_free(alpha_id);
+
+       if (err < 0) {
+               unsigned char no_cause_result[] = { 0x00 };
+
+               /*
+                * We most likely got an out of memory error, tell SIM
+                * to retry
+                */
+               ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY,
+                                       no_cause_result);
+               return TRUE;
+       }
+
+       stk->respond_on_exit = TRUE;
+       stk->cancel_cmd = stk_request_cancel;
+
+       return FALSE;
+}
+
+static void setup_call_handled_cancel(struct ofono_stk *stk)
+{
+       struct ofono_voicecall *vc;
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL,
+                               __ofono_atom_get_modem(stk->atom));
+
+       if (vc != NULL)
+               __ofono_voicecall_clear_alpha_and_icon_id(vc);
+}
+
+static gboolean handle_setup_call_confirmation_req(struct stk_command *cmd,
+                                               struct ofono_stk *stk)
+{
+       const struct stk_command_setup_call *sc = &cmd->setup_call;
+       int err;
+       char *alpha_id = dbus_apply_text_attributes(
+                                       sc->alpha_id_usr_cfm ?
+                                       sc->alpha_id_usr_cfm : "",
+                                       &sc->text_attr_usr_cfm);
+       if (alpha_id == NULL)
+               goto out;
+
+       err = stk_agent_confirm_call(stk->current_agent, alpha_id,
+                                       &sc->icon_id_usr_cfm,
+                                       confirm_handled_call_cb,
+                                       stk, NULL,
+                                       stk->timeout * 1000);
+       g_free(alpha_id);
+
+       if (err < 0)
+               goto out;
+
+       stk->cancel_cmd = setup_call_handled_cancel;
+
+       return TRUE;
+
+out:
+       if (stk->driver->user_confirmation)
+               stk->driver->user_confirmation(stk, FALSE);
+
+       return FALSE;
+}
+
+static void stk_proactive_command_cancel(struct ofono_stk *stk)
+{
+       if (stk->immediate_response)
+               stk_request_cancel(stk);
+
+       if (stk->pending_cmd) {
+               stk->cancel_cmd(stk);
+               stk_command_free(stk->pending_cmd);
+               stk->pending_cmd = NULL;
+               stk->cancel_cmd = NULL;
+               stk->respond_on_exit = FALSE;
+       }
+}
+
+void ofono_stk_proactive_session_end_notify(struct ofono_stk *stk)
+{
+       /* Wait until we receive the next command */
+       if (stk->immediate_response)
+               return;
+
+       stk_proactive_command_cancel(stk);
+
+       if (stk->session_agent)
+               stk_agent_free(stk->session_agent);
+}
+
+void ofono_stk_proactive_command_notify(struct ofono_stk *stk,
+                                       int length, const unsigned char *pdu)
+{
+       struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE };
+       struct stk_response rsp;
+       int err;
+       gboolean respond = TRUE;
+
+       /*
+        * Depending on the hardware we may have received a new
+        * command before we managed to send a TERMINAL RESPONSE to
+        * the previous one.  3GPP says in the current revision only
+        * one command can be executing at any time, so assume that
+        * the previous one is being cancelled and the card just
+        * expects a response to the new one.
+        */
+       stk_proactive_command_cancel(stk);
+
+       stk->pending_cmd = stk_command_new_from_pdu(pdu, length);
+       if (stk->pending_cmd == NULL) {
+               ofono_error("Can't parse proactive command");
+
+               /*
+                * Nothing we can do, we'd need at least Command Details
+                * to be able to respond with an error.
+                */
+               return;
+       }
+
+       switch (stk->pending_cmd->status) {
+       case STK_PARSE_RESULT_OK:
+               break;
+
+       case STK_PARSE_RESULT_MISSING_VALUE:
+               send_simple_response(stk, STK_RESULT_TYPE_MINIMUM_NOT_MET);
+               return;
+
+       case STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD:
+               send_simple_response(stk, STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD);
+               return;
+
+       case STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD:
+       default:
+               send_simple_response(stk,
+                                       STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD);
+               return;
+       }
+
+       /*
+        * In case no agent is registered, we should reject commands destined
+        * to the Agent with a NOT_CAPABLE error.
+        */
+       if (stk->current_agent == NULL) {
+               switch (stk->pending_cmd->type) {
+               case STK_COMMAND_TYPE_SELECT_ITEM:
+               case STK_COMMAND_TYPE_DISPLAY_TEXT:
+               case STK_COMMAND_TYPE_GET_INKEY:
+               case STK_COMMAND_TYPE_GET_INPUT:
+               case STK_COMMAND_TYPE_PLAY_TONE:
+               case STK_COMMAND_TYPE_SETUP_CALL:
+                       send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE);
+                       return;
+
+               default:
+                       break;
+               }
+       }
+
+       memset(&rsp, 0, sizeof(rsp));
+
+       switch (stk->pending_cmd->type) {
+       case STK_COMMAND_TYPE_MORE_TIME:
+               respond = handle_command_more_time(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SEND_SMS:
+               respond = handle_command_send_sms(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT:
+               respond = handle_command_set_idle_text(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_TIMER_MANAGEMENT:
+               respond = handle_command_timer_mgmt(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_POLL_INTERVAL:
+               respond = handle_command_poll_interval(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SETUP_MENU:
+               respond = handle_command_set_up_menu(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SELECT_ITEM:
+               respond = handle_command_select_item(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_DISPLAY_TEXT:
+               respond = handle_command_display_text(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_GET_INKEY:
+               respond = handle_command_get_inkey(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_GET_INPUT:
+               respond = handle_command_get_input(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SETUP_CALL:
+               respond = handle_command_set_up_call(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SEND_USSD:
+               respond = handle_command_send_ussd(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION:
+               /*
+                * If any clients are interested, then the ISO639
+                * 2-letter codes has to be convered to language strings.
+                * Converted language strings has to be added to the
+                * property list.
+                */
+               ofono_info("Language Code: %s",
+                       stk->pending_cmd->language_notification.language);
+               break;
+
+       case STK_COMMAND_TYPE_REFRESH:
+               respond = handle_command_refresh(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO:
+               respond = handle_command_provide_local_info(stk->pending_cmd,
+                                                               &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SEND_DTMF:
+               respond = handle_command_send_dtmf(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_PLAY_TONE:
+               respond = handle_command_play_tone(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_LAUNCH_BROWSER:
+               respond = handle_command_launch_browser(stk->pending_cmd,
+                                                       &rsp, stk);
+               break;
+
+       default:
+               rsp.result.type = STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD;
+               break;
+       }
+
+       if (respond == FALSE)
+               return;
+
+       err = stk_respond(stk, &rsp, stk_command_cb);
+       if (err)
+               stk_command_cb(&error, stk);
+}
+
+static gboolean handled_alpha_id_set(struct ofono_stk *stk,
+                       const char *text, const struct stk_text_attribute *attr,
+                       const struct stk_icon_id *icon)
+{
+       if (stk_alpha_id_set(stk, text, attr, icon) == FALSE)
+               return FALSE;
+
+       stk->cancel_cmd = stk_alpha_id_unset;
+       return TRUE;
+}
+
+void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk,
+                                               int length,
+                                               const unsigned char *pdu)
+{
+       struct stk_response dummyrsp;
+       gboolean ok = FALSE;
+
+       /*
+        * Modems send us the proactive command details and terminal responses
+        * sent by the modem as a response to the command.  Terminal responses
+        * start with the Command Details CTLV tag (0x81).  We filter terminal
+        * responses here
+        */
+       if (length > 0 && pdu[0] == 0x81) {
+               stk_proactive_command_cancel(stk);
+               return;
+       }
+
+       stk_proactive_command_cancel(stk);
+
+       stk->pending_cmd = stk_command_new_from_pdu(pdu, length);
+       if (stk->pending_cmd == NULL)
+               return;
+
+       if (stk->pending_cmd->status != STK_PARSE_RESULT_OK) {
+               ofono_error("Can't parse modem-handled proactive command");
+               ok = FALSE;
+               goto out;
+       }
+
+       DBG("type: %d", stk->pending_cmd->type);
+
+       switch (stk->pending_cmd->type) {
+       case STK_COMMAND_TYPE_SEND_SMS:
+               ok = handled_alpha_id_set(stk,
+                                       stk->pending_cmd->send_sms.alpha_id,
+                                       &stk->pending_cmd->send_sms.text_attr,
+                                       &stk->pending_cmd->send_sms.icon_id);
+               break;
+
+       case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT:
+               handle_command_set_idle_text(stk->pending_cmd, &dummyrsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SETUP_MENU:
+               handle_command_set_up_menu(stk->pending_cmd, &dummyrsp, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SETUP_CALL:
+               ok = handle_setup_call_confirmation_req(stk->pending_cmd, stk);
+               break;
+
+       case STK_COMMAND_TYPE_SEND_USSD:
+               ok = handled_alpha_id_set(stk,
+                                       stk->pending_cmd->send_ussd.alpha_id,
+                                       &stk->pending_cmd->send_ussd.text_attr,
+                                       &stk->pending_cmd->send_ussd.icon_id);
+               break;
+
+       case STK_COMMAND_TYPE_SEND_SS:
+               ok = handled_alpha_id_set(stk,
+                                       stk->pending_cmd->send_ss.alpha_id,
+                                       &stk->pending_cmd->send_ss.text_attr,
+                                       &stk->pending_cmd->send_ss.icon_id);
+               break;
+
+       case STK_COMMAND_TYPE_SEND_DTMF:
+               ok = handled_alpha_id_set(stk,
+                                       stk->pending_cmd->send_dtmf.alpha_id,
+                                       &stk->pending_cmd->send_dtmf.text_attr,
+                                       &stk->pending_cmd->send_dtmf.icon_id);
+               break;
+
+       case STK_COMMAND_TYPE_REFRESH:
+               handle_command_refresh(stk->pending_cmd, NULL, stk);
+               break;
+       }
+
+out:
+       if (ok == FALSE) {
+               stk_command_free(stk->pending_cmd);
+               stk->pending_cmd = NULL;
+       }
+}
+
+int ofono_stk_driver_register(const struct ofono_stk_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_stk_driver_unregister(const struct ofono_stk_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void stk_unregister(struct ofono_atom *atom)
+{
+       struct ofono_stk *stk = __ofono_atom_get_data(atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       if (stk->session_agent)
+               stk_agent_free(stk->session_agent);
+
+       if (stk->default_agent)
+               stk_agent_free(stk->default_agent);
+
+       if (stk->pending_cmd) {
+               stk_command_free(stk->pending_cmd);
+               stk->pending_cmd = NULL;
+               stk->cancel_cmd = NULL;
+       }
+
+       g_free(stk->idle_mode_text);
+       stk->idle_mode_text = NULL;
+
+       if (stk->timers_source) {
+               g_source_remove(stk->timers_source);
+               stk->timers_source = 0;
+       }
+
+       if (stk->main_menu) {
+               stk_menu_free(stk->main_menu);
+               stk->main_menu = NULL;
+       }
+
+       g_queue_foreach(stk->envelope_q, (GFunc) g_free, NULL);
+       g_queue_free(stk->envelope_q);
+
+       ofono_modem_remove_interface(modem, OFONO_STK_INTERFACE);
+       g_dbus_unregister_interface(conn, path, OFONO_STK_INTERFACE);
+}
+
+static void stk_remove(struct ofono_atom *atom)
+{
+       struct ofono_stk *stk = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (stk == NULL)
+               return;
+
+       if (stk->driver && stk->driver->remove)
+               stk->driver->remove(stk);
+
+       g_free(stk);
+}
+
+struct ofono_stk *ofono_stk_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_stk *stk;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       stk = g_try_new0(struct ofono_stk, 1);
+
+       if (stk == NULL)
+               return NULL;
+
+       stk->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_STK,
+                                               stk_remove, stk);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_stk_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(stk, vendor, data) < 0)
+                       continue;
+
+               stk->driver = drv;
+               break;
+       }
+
+       return stk;
+}
+
+void ofono_stk_register(struct ofono_stk *stk)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom);
+       const char *path = __ofono_atom_get_path(stk->atom);
+
+       if (!g_dbus_register_interface(conn, path, OFONO_STK_INTERFACE,
+                                       stk_methods, stk_signals, NULL,
+                                       stk, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_STK_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_STK_INTERFACE);
+
+       __ofono_atom_register(stk->atom, stk_unregister);
+
+       stk->timeout = 180; /* 3 minutes */
+       stk->short_timeout = 20; /* 20 seconds */
+       stk->envelope_q = g_queue_new();
+}
+
+void ofono_stk_remove(struct ofono_stk *stk)
+{
+       __ofono_atom_free(stk->atom);
+}
+
+void ofono_stk_set_data(struct ofono_stk *stk, void *data)
+{
+       stk->driver_data = data;
+}
+
+void *ofono_stk_get_data(struct ofono_stk *stk)
+{
+       return stk->driver_data;
+}
diff --git a/src/stkagent.c b/src/stkagent.c
new file mode 100644 (file)
index 0000000..7c3f697
--- /dev/null
@@ -0,0 +1,1218 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "smsutil.h"
+#include "stkutil.h"
+#include "stkagent.h"
+
+#ifndef DBUS_TIMEOUT_INFINITE
+#define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff)
+#endif
+
+enum allowed_error {
+       ALLOWED_ERROR_GO_BACK   = 0x1,
+       ALLOWED_ERROR_TERMINATE = 0x2,
+       ALLOWED_ERROR_BUSY              = 0x4,
+};
+
+struct stk_agent {
+       char *path;                             /* Agent Path */
+       char *bus;                              /* Agent bus */
+       guint disconnect_watch;                 /* DBus disconnect watch */
+       ofono_bool_t remove_on_terminate;
+       ofono_destroy_func removed_cb;
+       void *removed_data;
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       void *user_cb;
+       void *user_data;
+       ofono_destroy_func user_destroy;
+
+       const struct stk_menu *request_selection_menu;
+};
+
+#define ERROR_PREFIX OFONO_SERVICE ".Error"
+#define GOBACK_ERROR ERROR_PREFIX ".GoBack"
+#define TERMINATE_ERROR ERROR_PREFIX ".EndSession"
+#define BUSY_ERROR ERROR_PREFIX ".Busy"
+
+static void stk_agent_send_noreply(struct stk_agent *agent, const char *method)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       DBusMessage *message;
+
+       message = dbus_message_new_method_call(agent->bus, agent->path,
+                                               OFONO_SIM_APP_INTERFACE,
+                                               method);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(conn, message);
+}
+
+static inline void stk_agent_send_release(struct stk_agent *agent)
+{
+       stk_agent_send_noreply(agent, "Release");
+}
+
+static inline void stk_agent_send_cancel(struct stk_agent *agent)
+{
+       stk_agent_send_noreply(agent, "Cancel");
+}
+
+static void stk_agent_request_end(struct stk_agent *agent)
+{
+       if (agent->msg) {
+               dbus_message_unref(agent->msg);
+               agent->msg = NULL;
+       }
+
+       if (agent->call) {
+               dbus_pending_call_unref(agent->call);
+               agent->call = NULL;
+       }
+
+       if (agent->user_destroy)
+               agent->user_destroy(agent->user_data);
+
+       agent->user_destroy = NULL;
+       agent->user_data = NULL;
+       agent->user_cb = NULL;
+}
+
+ofono_bool_t stk_agent_matches(struct stk_agent *agent,
+                               const char *path, const char *sender)
+{
+       return !strcmp(agent->path, path) && !strcmp(agent->bus, sender);
+}
+
+void stk_agent_set_removed_notify(struct stk_agent *agent,
+                                       ofono_destroy_func destroy,
+                                       void *user_data)
+{
+       agent->removed_cb = destroy;
+       agent->removed_data = user_data;
+}
+
+void stk_agent_request_cancel(struct stk_agent *agent)
+{
+       if (agent->call == NULL)
+               return;
+
+       dbus_pending_call_cancel(agent->call);
+
+       if (agent->disconnect_watch)
+               stk_agent_send_cancel(agent);
+
+       stk_agent_request_end(agent);
+}
+
+void stk_agent_free(struct stk_agent *agent)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       stk_agent_request_cancel(agent);
+
+       if (agent->disconnect_watch) {
+               stk_agent_send_release(agent);
+
+               g_dbus_remove_watch(conn, agent->disconnect_watch);
+               agent->disconnect_watch = 0;
+       }
+
+       if (agent->removed_cb)
+               agent->removed_cb(agent->removed_data);
+
+       g_free(agent->path);
+       g_free(agent->bus);
+       g_free(agent);
+}
+
+static int check_error(struct stk_agent *agent, DBusMessage *reply,
+                               int allowed_errors,
+                               enum stk_agent_result *out_result)
+{
+       DBusError err;
+       int result = 0;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply) == FALSE) {
+               *out_result = STK_AGENT_RESULT_OK;
+               return 0;
+       }
+
+       ofono_debug("SimToolkitAgent %s replied with error %s, %s",
+                       agent->path, err.name, err.message);
+
+       /* Timeout is always valid */
+       if (g_str_equal(err.name, DBUS_ERROR_NO_REPLY)) {
+               /* Send a Cancel() to the agent since its taking too long */
+               stk_agent_send_cancel(agent);
+               *out_result = STK_AGENT_RESULT_TIMEOUT;
+               goto out;
+       }
+
+       if ((allowed_errors & ALLOWED_ERROR_GO_BACK) &&
+                       g_str_equal(err.name, GOBACK_ERROR)) {
+               *out_result = STK_AGENT_RESULT_BACK;
+               goto out;
+       }
+
+       if ((allowed_errors & ALLOWED_ERROR_TERMINATE) &&
+                       g_str_equal(err.name, TERMINATE_ERROR)) {
+               *out_result = STK_AGENT_RESULT_TERMINATE;
+               goto out;
+       }
+
+       if ((allowed_errors & ALLOWED_ERROR_BUSY) &&
+                       g_str_equal(err.name, BUSY_ERROR)) {
+               *out_result = STK_AGENT_RESULT_BUSY;
+               goto out;
+       }
+
+       result = -EINVAL;
+
+out:
+       dbus_error_free(&err);
+       return result;
+}
+
+static void stk_agent_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+       struct stk_agent *agent = user_data;
+
+       ofono_debug("Agent exited without calling Unregister");
+
+       agent->disconnect_watch = 0;
+
+       stk_agent_free(agent);
+}
+
+struct stk_agent *stk_agent_new(const char *path, const char *sender,
+                               ofono_bool_t remove_on_terminate)
+{
+       struct stk_agent *agent = g_try_new0(struct stk_agent, 1);
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       if (agent == NULL)
+               return NULL;
+
+       agent->path = g_strdup(path);
+       agent->bus = g_strdup(sender);
+       agent->remove_on_terminate = remove_on_terminate;
+
+       agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, sender,
+                                                       stk_agent_disconnect_cb,
+                                                       agent, NULL);
+
+       return agent;
+}
+
+static void append_menu_items(DBusMessageIter *iter,
+                               const struct stk_menu_item *item)
+{
+       DBusMessageIter array, entry;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                               "(sy)", &array);
+
+       while (item && item->text) {
+               dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT,
+                                                       NULL, &entry);
+
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+                                               &item->text);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_BYTE,
+                                               &item->icon_id);
+
+               dbus_message_iter_close_container(&array, &entry);
+               item++;
+       }
+
+       dbus_message_iter_close_container(iter, &array);
+}
+
+void append_menu_items_variant(DBusMessageIter *iter,
+                               const struct stk_menu_item *items)
+{
+       DBusMessageIter variant;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               "a(sy)", &variant);
+
+       append_menu_items(&variant, items);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+#define CALLBACK_END()                                         \
+done:                                                          \
+       if (result == STK_AGENT_RESULT_TERMINATE &&             \
+                       agent->remove_on_terminate)             \
+               remove_agent = TRUE;                            \
+       else                                                    \
+               remove_agent = FALSE;                           \
+                                                               \
+error:                                                         \
+       stk_agent_request_end(agent);                           \
+       dbus_message_unref(reply);                              \
+                                                               \
+       if (remove_agent)                                       \
+               stk_agent_free(agent)                           \
+
+static void request_selection_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       const struct stk_menu *menu = agent->request_selection_menu;
+       stk_agent_selection_cb cb = (stk_agent_selection_cb) agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       unsigned char selection, i;
+       enum stk_agent_result result;
+       gboolean remove_agent;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE,
+                       &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, 0, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_BYTE, &selection,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to RequestSelection()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       for (i = 0; i < selection && menu->items[i].text; i++);
+
+       if (i != selection) {
+               ofono_error("Invalid item selected");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, menu->items[selection].item_id, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_request_selection(struct stk_agent *agent,
+                               const struct stk_menu *menu,
+                               stk_agent_selection_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_int16_t default_item = menu->default_item;
+       DBusMessageIter iter;
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "RequestSelection");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(agent->msg, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &menu->title);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, &menu->icon.id);
+       append_menu_items(&iter, menu->items);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT16, &default_item);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       agent->request_selection_menu = menu;
+
+       dbus_pending_call_set_notify(agent->call, request_selection_cb,
+                                       agent, NULL);
+
+       return 0;
+}
+
+static void display_text_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_display_text_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE |
+                       ALLOWED_ERROR_BUSY, &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to DisplayText()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_display_text(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               ofono_bool_t urgent,
+                               stk_agent_display_text_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t priority = urgent;
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "DisplayText");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_BOOLEAN, &priority,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, display_text_cb,
+                                       agent, NULL);
+
+       return 0;
+}
+
+static void get_confirmation_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_confirmation_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       dbus_bool_t confirm;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE,
+                       &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, FALSE, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_BOOLEAN, &confirm,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to GetConfirmation()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, confirm, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_request_confirmation(struct stk_agent *agent, const char *text,
+                                       const struct stk_icon_id *icon,
+                                       stk_agent_confirmation_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy,
+                                       int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "RequestConfirmation");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, get_confirmation_cb,
+                                       agent, NULL);
+
+       return 0;
+}
+
+static void get_digit_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_string_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       char *digit;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE,
+                       &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, NULL, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_STRING, &digit,
+                                       DBUS_TYPE_INVALID) == FALSE ||
+                       strlen(digit) != 1 ||
+                       !valid_phone_number_format(digit)) {
+               ofono_error("Can't parse the reply to GetDigit()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, digit, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_request_digit(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               stk_agent_string_cb cb, void *user_data,
+                               ofono_destroy_func destroy, int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "RequestDigit");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, get_digit_cb, agent, NULL);
+
+       return 0;
+}
+
+static void get_key_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_string_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       char *key;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE,
+                       &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, NULL, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_STRING, &key,
+                                       DBUS_TYPE_INVALID) == FALSE ||
+                       g_utf8_strlen(key, 10) != 1) {
+               ofono_error("Can't parse the reply to GetKey()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, key, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_request_key(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               ofono_bool_t unicode_charset,
+                               stk_agent_string_cb cb, void *user_data,
+                               ofono_destroy_func destroy, int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "RequestKey");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, get_key_cb, agent, NULL);
+
+       return 0;
+}
+
+static void get_digits_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_string_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       char *string;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE,
+                       &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, NULL, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_STRING, &string,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to GetDigits()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, string, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_request_digits(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               const char *default_text,
+                               int min, int max, ofono_bool_t hidden,
+                               stk_agent_string_cb cb, void *user_data,
+                               ofono_destroy_func destroy, int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       uint8_t min_val = min;
+       uint8_t max_val = max;
+       dbus_bool_t hidden_val = hidden;
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "RequestDigits");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       if (default_text == NULL)
+               default_text = "";
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_STRING, &default_text,
+                                       DBUS_TYPE_BYTE, &min_val,
+                                       DBUS_TYPE_BYTE, &max_val,
+                                       DBUS_TYPE_BOOLEAN, &hidden_val,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, get_digits_cb, agent, NULL);
+
+       return 0;
+}
+
+static void get_input_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_string_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       char *string;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE,
+                       &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, NULL, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_STRING, &string,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to GetInput()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, string, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_request_input(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               const char *default_text,
+                               ofono_bool_t unicode_charset, int min, int max,
+                               ofono_bool_t hidden, stk_agent_string_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       uint8_t min_val = min;
+       uint8_t max_val = max;
+       dbus_bool_t hidden_val = hidden;
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "RequestInput");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       if (default_text == NULL)
+               default_text = "";
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_STRING, &default_text,
+                                       DBUS_TYPE_BYTE, &min_val,
+                                       DBUS_TYPE_BYTE, &max_val,
+                                       DBUS_TYPE_BOOLEAN, &hidden_val,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, get_input_cb, agent, NULL);
+
+       return 0;
+}
+
+static void confirm_call_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_confirmation_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       dbus_bool_t confirm;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, FALSE, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_BOOLEAN, &confirm,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to ConfirmCallSetup()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, confirm, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_confirm_call(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               stk_agent_confirmation_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "ConfirmCallSetup");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, confirm_call_cb, agent, NULL);
+
+       return 0;
+}
+
+static void play_tone_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_tone_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to PlayTone()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, agent->user_data);
+       goto done;
+
+       CALLBACK_END();
+}
+
+int stk_agent_play_tone(struct stk_agent *agent, const char *text,
+                       const struct stk_icon_id *icon, ofono_bool_t vibrate,
+                       const char *tone, stk_agent_tone_cb cb, void *user_data,
+                       ofono_destroy_func destroy, int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "PlayTone");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &tone,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, play_tone_cb,
+                                       agent, NULL);
+
+       return 0;
+}
+
+int stk_agent_loop_tone(struct stk_agent *agent, const char *text,
+                       const struct stk_icon_id *icon, ofono_bool_t vibrate,
+                       const char *tone, stk_agent_tone_cb cb, void *user_data,
+                       ofono_destroy_func destroy, int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "LoopTone");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &tone,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, play_tone_cb,
+                                       agent, NULL);
+
+       return 0;
+}
+
+static void action_info_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+
+       if (check_error(agent, reply, 0, &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to DisplayActionInfo()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       goto done;
+
+       CALLBACK_END();
+}
+
+int stk_agent_display_action_info(struct stk_agent *agent, const char *text,
+                                       const struct stk_icon_id *icon)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                               OFONO_SIM_APP_INTERFACE,
+                                               "DisplayActionInformation");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                       DBUS_TIMEOUT_INFINITE) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       dbus_pending_call_set_notify(agent->call, action_info_cb, agent, NULL);
+
+       return 0;
+}
+
+static void confirm_launch_browser_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_confirmation_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       dbus_bool_t confirm;
+
+       if (check_error(agent, reply, 0, &result) == -EINVAL) {
+               remove_agent = TRUE;
+               cb(STK_AGENT_RESULT_TERMINATE, FALSE, agent->user_data);
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, FALSE, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_BOOLEAN, &confirm,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to ConfirmLaunchBrowser()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, confirm, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_confirm_launch_browser(struct stk_agent *agent, const char *text,
+                                       unsigned char icon_id, const char *url,
+                                       stk_agent_confirmation_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy, int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "ConfirmLaunchBrowser");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       if (url == NULL)
+               url = "";
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon_id,
+                                       DBUS_TYPE_STRING, &url,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                                               agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, confirm_launch_browser_cb,
+                                       agent, NULL);
+
+       return 0;
+}
+
+static void display_action_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_display_action_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to DisplayAction()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, agent->user_data);
+       goto done;
+
+       CALLBACK_END();
+}
+
+int stk_agent_display_action(struct stk_agent *agent,
+                                       const char *text,
+                                       const struct stk_icon_id *icon,
+                                       stk_agent_display_action_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                               OFONO_SIM_APP_INTERFACE,
+                                               "DisplayAction");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                       DBUS_TIMEOUT_INFINITE) == FALSE ||
+                       agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, display_action_cb,
+                                       agent, NULL);
+
+       return 0;
+}
+
+static void confirm_open_channel_cb(DBusPendingCall *call, void *data)
+{
+       struct stk_agent *agent = data;
+       stk_agent_confirmation_cb cb = agent->user_cb;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       enum stk_agent_result result;
+       gboolean remove_agent;
+       dbus_bool_t confirm;
+
+       if (check_error(agent, reply,
+                       ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) {
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       if (result != STK_AGENT_RESULT_OK) {
+               cb(result, FALSE, agent->user_data);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_BOOLEAN, &confirm,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               ofono_error("Can't parse the reply to ConfirmOpenChannel()");
+               remove_agent = TRUE;
+               goto error;
+       }
+
+       cb(result, confirm, agent->user_data);
+
+       CALLBACK_END();
+}
+
+int stk_agent_confirm_open_channel(struct stk_agent *agent, const char *text,
+                                       const struct stk_icon_id *icon,
+                                       stk_agent_confirmation_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy, int timeout)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+
+       agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+                                                       OFONO_SIM_APP_INTERFACE,
+                                                       "ConfirmOpenChannel");
+       if (agent->msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(agent->msg,
+                                       DBUS_TYPE_STRING, &text,
+                                       DBUS_TYPE_BYTE, &icon->id,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+                                               timeout) == FALSE ||
+                                               agent->call == NULL)
+               return -EIO;
+
+       agent->user_cb = cb;
+       agent->user_data = user_data;
+       agent->user_destroy = destroy;
+
+       dbus_pending_call_set_notify(agent->call, confirm_open_channel_cb,
+                                       agent, NULL);
+
+       return 0;
+}
diff --git a/src/stkagent.h b/src/stkagent.h
new file mode 100644 (file)
index 0000000..6e267fc
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct stk_agent;
+
+enum stk_agent_result {
+       STK_AGENT_RESULT_OK,
+       STK_AGENT_RESULT_BACK,
+       STK_AGENT_RESULT_TERMINATE,
+       STK_AGENT_RESULT_TIMEOUT,
+       STK_AGENT_RESULT_BUSY,
+};
+
+struct stk_menu_item {
+       char *text;
+       uint8_t icon_id;
+       uint8_t item_id;
+};
+
+struct stk_menu {
+       char *title;
+       struct stk_icon_id icon;
+       struct stk_menu_item *items;
+       int default_item;
+       gboolean soft_key;
+       gboolean has_help;
+};
+
+typedef void (*stk_agent_display_text_cb)(enum stk_agent_result result,
+                                               void *user_data);
+
+typedef void (*stk_agent_selection_cb)(enum stk_agent_result result,
+                                       uint8_t id, void *user_data);
+
+typedef void (*stk_agent_confirmation_cb)(enum stk_agent_result result,
+                                               ofono_bool_t confirm,
+                                               void *user_data);
+
+typedef void (*stk_agent_string_cb)(enum stk_agent_result result,
+                                       char *string, void *user_data);
+
+typedef void (*stk_agent_tone_cb)(enum stk_agent_result result,
+                                               void *user_data);
+
+typedef void (*stk_agent_display_action_cb)(enum stk_agent_result result,
+                                               void *user_data);
+
+struct stk_agent *stk_agent_new(const char *path, const char *sender,
+                                       ofono_bool_t remove_on_terminate);
+
+void stk_agent_free(struct stk_agent *agent);
+
+void stk_agent_set_removed_notify(struct stk_agent *agent,
+                                       ofono_destroy_func removed_cb,
+                                       void *user_data);
+
+ofono_bool_t stk_agent_matches(struct stk_agent *agent,
+                               const char *path, const char *sender);
+
+void stk_agent_request_cancel(struct stk_agent *agent);
+
+int stk_agent_request_selection(struct stk_agent *agent,
+                               const struct stk_menu *menu,
+                               stk_agent_selection_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout);
+
+int stk_agent_display_text(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               ofono_bool_t urgent,
+                               stk_agent_display_text_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout);
+
+int stk_agent_request_confirmation(struct stk_agent *agent, const char *text,
+                                       const struct stk_icon_id *icon,
+                                       stk_agent_confirmation_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy,
+                                       int timeout);
+
+int stk_agent_request_digit(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               stk_agent_string_cb cb, void *user_data,
+                               ofono_destroy_func destroy, int timeout);
+
+int stk_agent_request_key(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               ofono_bool_t unicode_charset,
+                               stk_agent_string_cb cb, void *user_data,
+                               ofono_destroy_func destroy, int timeout);
+
+int stk_agent_request_digits(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               const char *default_text, int min, int max,
+                               ofono_bool_t hidden, stk_agent_string_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout);
+
+int stk_agent_request_input(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               const char *default_text,
+                               ofono_bool_t unicode_charset, int min, int max,
+                               ofono_bool_t hidden, stk_agent_string_cb cb,
+                               void *user_data, ofono_destroy_func destroy,
+                               int timeout);
+
+int stk_agent_confirm_call(struct stk_agent *agent, const char *text,
+                               const struct stk_icon_id *icon,
+                               stk_agent_confirmation_cb cb, void *user_data,
+                               ofono_destroy_func destroy, int timeout);
+
+int stk_agent_play_tone(struct stk_agent *agent, const char *text,
+                       const struct stk_icon_id *icon, ofono_bool_t vibrate,
+                       const char *tone, stk_agent_tone_cb cb, void *user_data,
+                       ofono_destroy_func destroy, int timeout);
+
+int stk_agent_loop_tone(struct stk_agent *agent, const char *text,
+                       const struct stk_icon_id *icon, ofono_bool_t vibrate,
+                       const char *tone, stk_agent_tone_cb cb, void *user_data,
+                       ofono_destroy_func destroy, int timeout);
+
+void append_menu_items_variant(DBusMessageIter *iter,
+                               const struct stk_menu_item *items);
+
+int stk_agent_display_action_info(struct stk_agent *agent, const char *text,
+                                       const struct stk_icon_id *icon);
+
+int stk_agent_confirm_launch_browser(struct stk_agent *agent, const char *text,
+                                       unsigned char icon_id, const char *url,
+                                       stk_agent_confirmation_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy,
+                                       int timeout);
+
+int stk_agent_display_action(struct stk_agent *agent, const char *text,
+                                       const struct stk_icon_id *icon,
+                                       stk_agent_display_action_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy);
+
+int stk_agent_confirm_open_channel(struct stk_agent *agent, const char *text,
+                                       const struct stk_icon_id *icon,
+                                       stk_agent_confirmation_cb cb,
+                                       void *user_data,
+                                       ofono_destroy_func destroy,
+                                       int timeout);
diff --git a/src/stkutil.c b/src/stkutil.c
new file mode 100644 (file)
index 0000000..a03e9b7
--- /dev/null
@@ -0,0 +1,6606 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <glib.h>
+
+#include <ofono/types.h>
+#include "smsutil.h"
+#include "stkutil.h"
+#include "simutil.h"
+#include "util.h"
+
+enum stk_data_object_flag {
+       DATAOBJ_FLAG_MANDATORY =        1,
+       DATAOBJ_FLAG_MINIMUM =          2,
+       DATAOBJ_FLAG_CR =               4,
+       DATAOBJ_FLAG_LIST =             8,
+};
+
+struct stk_file_iter {
+       const unsigned char *start;
+       unsigned int pos;
+       unsigned int max;
+       unsigned char len;
+       const unsigned char *file;
+};
+
+struct stk_tlv_builder {
+       struct comprehension_tlv_builder ctlv;
+       unsigned char *value;
+       unsigned int len;
+       unsigned int max_len;
+};
+
+typedef gboolean (*dataobj_handler)(struct comprehension_tlv_iter *, void *);
+typedef gboolean (*dataobj_writer)(struct stk_tlv_builder *,
+                                       const void *, gboolean);
+
+/*
+ * Defined in TS 102.223 Section 8.13
+ * The type of gsm sms can be SMS-COMMAND AND SMS-SUBMIT. According to 23.040,
+ * the maximum length is 164 bytes. But for SMS-SUBMIT, sms may be packed by
+ * ME. Thus the maximum length of messsage could be 160 bytes, instead of 140
+ * bytes. So the total maximum length could be 184 bytes. Refer TS 31.111,
+ * section 6.4.10 for details.
+ */
+struct gsm_sms_tpdu {
+       unsigned int len;
+       unsigned char tpdu[184];
+};
+
+#define CHECK_TEXT_AND_ICON(text, icon_id)                     \
+       if (status != STK_PARSE_RESULT_OK)                      \
+               return status;                                  \
+                                                               \
+       if ((text == NULL || text[0] == '\0') && icon_id != 0)  \
+               status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;  \
+
+static char *decode_text(unsigned char dcs, int len, const unsigned char *data)
+{
+       char *utf8;
+       enum sms_charset charset;
+
+       if (sms_dcs_decode(dcs, NULL, &charset, NULL, NULL) == FALSE)
+               return NULL;
+
+       switch (charset) {
+       case SMS_CHARSET_7BIT:
+       {
+               long written;
+               unsigned long max_to_unpack = len * 8 / 7;
+               unsigned char *unpacked = unpack_7bit(data, len, 0, FALSE,
+                                                       max_to_unpack,
+                                                       &written, 0);
+               if (unpacked == NULL)
+                       return NULL;
+
+               utf8 = convert_gsm_to_utf8(unpacked, written,
+                                               NULL, NULL, 0);
+               g_free(unpacked);
+               break;
+       }
+       case SMS_CHARSET_8BIT:
+               utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0);
+               break;
+       case SMS_CHARSET_UCS2:
+               utf8 = g_convert((const gchar *) data, len,
+                                       "UTF-8//TRANSLIT", "UCS-2BE",
+                                       NULL, NULL, NULL);
+               break;
+       default:
+               utf8 = NULL;
+       }
+
+       return utf8;
+}
+
+/* For data object only to indicate its existence */
+static gboolean parse_dataobj_common_bool(struct comprehension_tlv_iter *iter,
+                                               gboolean *out)
+{
+       if (comprehension_tlv_iter_get_length(iter) != 0)
+               return FALSE;
+
+       *out = TRUE;
+
+       return TRUE;
+}
+
+/* For data object that only has one byte */
+static gboolean parse_dataobj_common_byte(struct comprehension_tlv_iter *iter,
+                                               unsigned char *out)
+{
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       *out = data[0];
+
+       return TRUE;
+}
+
+/* For data object that only has text terminated by '\0' */
+static gboolean parse_dataobj_common_text(struct comprehension_tlv_iter *iter,
+                                               char **text)
+{
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       *text = g_try_malloc(len + 1);
+       if (*text == NULL)
+               return FALSE;
+
+       memcpy(*text, data, len);
+       (*text)[len] = '\0';
+
+       return TRUE;
+}
+
+/* For data object that only has a byte array with undetermined length */
+static gboolean parse_dataobj_common_byte_array(
+                       struct comprehension_tlv_iter *iter,
+                       struct stk_common_byte_array *array)
+{
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       array->len = len;
+
+       array->array = g_try_malloc(len);
+       if (array->array == NULL)
+               return FALSE;
+
+       memcpy(array->array, data, len);
+
+       return TRUE;
+}
+
+static void stk_file_iter_init(struct stk_file_iter *iter,
+                               const unsigned char *start, unsigned int len)
+{
+       iter->start = start;
+       iter->max = len;
+       iter->pos = 0;
+}
+
+static gboolean stk_file_iter_next(struct stk_file_iter *iter)
+{
+       unsigned int pos = iter->pos;
+       const unsigned int max = iter->max;
+       const unsigned char *start = iter->start;
+       unsigned int i;
+       unsigned char last_type;
+
+       if (pos + 2 >= max)
+               return FALSE;
+
+       /* SIM EFs always start with ROOT MF, 0x3f */
+       if (start[iter->pos] != 0x3f)
+               return FALSE;
+
+       last_type = 0x3f;
+
+       for (i = pos + 2; i < max; i += 2) {
+               /*
+                * Check the validity of file type.
+                * According to TS 11.11, each file id contains of two bytes,
+                * in which the first byte is the type of file. For GSM is:
+                * 0x3f: master file
+                * 0x7f: 1st level dedicated file
+                * 0x5f: 2nd level dedicated file
+                * 0x2f: elementary file under the master file
+                * 0x6f: elementary file under 1st level dedicated file
+                * 0x4f: elementary file under 2nd level dedicated file
+                */
+               switch (start[i]) {
+               case 0x2f:
+                       if (last_type != 0x3f)
+                               return FALSE;
+                       break;
+               case 0x6f:
+                       if (last_type != 0x7f)
+                               return FALSE;
+                       break;
+               case 0x4f:
+                       if (last_type != 0x5f)
+                               return FALSE;
+                       break;
+               case 0x7f:
+                       if (last_type != 0x3f)
+                               return FALSE;
+                       break;
+               case 0x5f:
+                       if (last_type != 0x7f)
+                               return FALSE;
+                       break;
+               default:
+                       return FALSE;
+               }
+
+               if ((start[i] == 0x2f) || (start[i] == 0x6f) ||
+                                               (start[i] == 0x4f)) {
+                       if (i + 1 >= max)
+                               return FALSE;
+
+                       iter->file = start + pos;
+                       iter->len = i - pos + 2;
+                       iter->pos = i + 2;
+
+                       return TRUE;
+               }
+
+               last_type = start[i];
+       }
+
+       return FALSE;
+}
+
+/* Defined in TS 102.223 Section 8.1 */
+static gboolean parse_dataobj_address(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_address *addr = user;
+       const unsigned char *data;
+       unsigned int len;
+       char *number;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       number = g_try_malloc(len * 2 - 1);
+       if (number == NULL)
+               return FALSE;
+
+       addr->ton_npi = data[0];
+       addr->number = number;
+       sim_extract_bcd_number(data + 1, len - 1, addr->number);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.2 */
+static gboolean parse_dataobj_alpha_id(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       char **alpha_id = user;
+       const unsigned char *data;
+       unsigned int len;
+       char *utf8;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len == 0) {
+               *alpha_id = NULL;
+               return TRUE;
+       }
+
+       data = comprehension_tlv_iter_get_data(iter);
+       utf8 = sim_string_to_utf8(data, len);
+
+       if (utf8 == NULL)
+               return FALSE;
+
+       *alpha_id = utf8;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.3 */
+static gboolean parse_dataobj_subaddress(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_subaddress *subaddr = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 1)
+               return FALSE;
+
+       if (len > sizeof(subaddr->subaddr))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       subaddr->len = len;
+       memcpy(subaddr->subaddr, data, len);
+
+       subaddr->has_subaddr = TRUE;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.4 */
+static gboolean parse_dataobj_ccp(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_ccp *ccp = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 1)
+               return FALSE;
+
+       if (len > sizeof(ccp->ccp))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       ccp->len = len;
+       memcpy(ccp->ccp, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 31.111 Section 8.5 */
+static gboolean parse_dataobj_cbs_page(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_cbs_page *cp = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 1)
+               return FALSE;
+
+       if (len > sizeof(cp->page))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       cp->len = len;
+       memcpy(cp->page, data, len);
+
+       return TRUE;
+}
+
+/* Described in TS 102.223 Section 8.8 */
+static gboolean parse_dataobj_duration(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_duration *duration = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if (data[0] > 0x02)
+               return FALSE;
+
+       if (data[1] == 0)
+               return FALSE;
+
+       duration->unit = data[0];
+       duration->interval = data[1];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.9 */
+static gboolean parse_dataobj_item(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_item *item = user;
+       const unsigned char *data;
+       unsigned int len;
+       char *utf8;
+
+       len = comprehension_tlv_iter_get_length(iter);
+
+       if (len == 0)
+               return TRUE;
+
+       if (len == 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* The identifier is between 0x01 and 0xFF */
+       if (data[0] == 0)
+               return FALSE;
+
+       utf8 = sim_string_to_utf8(data + 1, len - 1);
+
+       if (utf8 == NULL)
+               return FALSE;
+
+       item->id = data[0];
+       item->text = utf8;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.10 */
+static gboolean parse_dataobj_item_id(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       unsigned char *id = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       *id = data[0];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.11 */
+static gboolean parse_dataobj_response_len(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_response_length *response_len = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       response_len->min = data[0];
+       response_len->max = data[1];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.12 */
+static gboolean parse_dataobj_result(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_result *result = user;
+       const unsigned char *data;
+       unsigned int len;
+       unsigned char *additional;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if ((len < 2) && ((data[0] == 0x20) || (data[0] == 0x21) ||
+                               (data[0] == 0x26) || (data[0] == 0x38) ||
+                               (data[0] == 0x39) || (data[0] == 0x3a) ||
+                               (data[0] == 0x3c) || (data[0] == 0x3d)))
+               return FALSE;
+
+       additional = g_try_malloc(len - 1);
+       if (additional == NULL)
+               return FALSE;
+
+       result->type = data[0];
+       result->additional_len = len - 1;
+       result->additional = additional;
+       memcpy(result->additional, data + 1, len - 1);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.13 */
+static gboolean parse_dataobj_gsm_sms_tpdu(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct gsm_sms_tpdu *tpdu = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 1 || len > sizeof(tpdu->tpdu))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       tpdu->len = len;
+       memcpy(tpdu->tpdu, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.14 */
+static gboolean parse_dataobj_ss(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_ss *ss = user;
+       const unsigned char *data;
+       unsigned int len;
+       char *s;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       s = g_try_malloc(len * 2 - 1);
+       if (s == NULL)
+               return FALSE;
+
+       ss->ton_npi = data[0];
+       ss->ss = s;
+       sim_extract_bcd_number(data + 1, len - 1, ss->ss);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.15 */
+static gboolean parse_dataobj_text(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       char **text = user;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+       const unsigned char *data;
+       char *utf8;
+
+       if (len <= 1) {
+               *text = g_try_malloc0(1);
+               return TRUE;
+       }
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       utf8 = decode_text(data[0], len - 1, data + 1);
+
+       if (utf8 == NULL)
+               return FALSE;
+
+       *text = utf8;
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.16 */
+static gboolean parse_dataobj_tone(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       unsigned char *byte = user;
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.17 */
+static gboolean parse_dataobj_ussd(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_ussd_string *us = user;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+       const unsigned char *data = comprehension_tlv_iter_get_data(iter);
+
+       if (len <= 1 || len > 161)
+               return FALSE;
+
+       us->dcs = data[0];
+       us->len = len - 1;
+       memcpy(us->string, data + 1, us->len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.18 */
+static gboolean parse_dataobj_file_list(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       GSList **fl = user;
+       const unsigned char *data;
+       unsigned int len;
+       struct stk_file *sf;
+       struct stk_file_iter sf_iter;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 5)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       stk_file_iter_init(&sf_iter, data + 1, len - 1);
+
+       while (stk_file_iter_next(&sf_iter)) {
+               sf = g_try_new0(struct stk_file, 1);
+               if (sf == NULL)
+                       goto error;
+
+               sf->len = sf_iter.len;
+               memcpy(sf->file, sf_iter.file, sf_iter.len);
+               *fl = g_slist_prepend(*fl, sf);
+       }
+
+       if (sf_iter.pos != sf_iter.max)
+               goto error;
+
+       *fl = g_slist_reverse(*fl);
+       return TRUE;
+
+error:
+       g_slist_foreach(*fl, (GFunc) g_free, NULL);
+       g_slist_free(*fl);
+       return FALSE;
+}
+
+/* Defined in TS 102.223 Section 8.19 */
+static gboolean parse_dataobj_location_info(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_location_info *li = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if ((len != 5) && (len != 7) && (len != 9))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       sim_parse_mcc_mnc(data, li->mcc, li->mnc);
+       li->lac_tac = (data[3] << 8) + data[4];
+
+       if (len >= 7) {
+               li->has_ci = TRUE;
+               li->ci = (data[5] << 8) + data[6];
+       }
+
+       if (len == 9) {
+               li->has_ext_ci = TRUE;
+               li->ext_ci = (data[7] << 8) + data[8];
+       }
+
+       return TRUE;
+}
+
+/*
+ * Defined in TS 102.223 Section 8.20.
+ *
+ * According to 3GPP TS 24.008, Section 10.5.1.4, IMEI is composed of
+ * 15 digits and totally 8 bytes are used to represent it.
+ *
+ * Bits 1-3 of first byte represent the type of identity, and they
+ * are 0 1 0 separately for IMEI. Bit 4 of first byte is the odd/even
+ * indication, and it's 1 to indicate IMEI has odd number of digits (15).
+ * The rest bytes are coded using BCD coding.
+ *
+ * For example, if the IMEI is "123456789012345", then it's coded as
+ * "1A 32 54 76 98 10 32 54".
+ */
+static gboolean parse_dataobj_imei(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       char *imei = user;
+       const unsigned char *data;
+       unsigned int len;
+       static const char digit_lut[] = "0123456789*#abc\0";
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len != 8)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if ((data[0] & 0x0f) != 0x0a)
+               return FALSE;
+
+       /* Assume imei is at least 16 bytes long (15 for imei + null) */
+       imei[0] = digit_lut[(data[0] & 0xf0) >> 4];
+       extract_bcd_number(data + 1, 7, imei + 1);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.21 */
+static gboolean parse_dataobj_help_request(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       gboolean *ret = user;
+       return parse_dataobj_common_bool(iter, ret);
+}
+
+/* Defined in TS 102.223 Section 8.22 */
+static gboolean parse_dataobj_network_measurement_results(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *nmr = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len != 0x10)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* Assume network measurement result is 16 bytes long */
+       memcpy(nmr, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.23 */
+static gboolean parse_dataobj_default_text(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       char **text = user;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+       const unsigned char *data = comprehension_tlv_iter_get_data(iter);
+       char *utf8;
+
+       /* DCS followed by some text, cannot be 1 */
+       if (len <= 1)
+               return FALSE;
+
+       utf8 = decode_text(data[0], len - 1, data + 1);
+
+       if (utf8 == NULL)
+               return FALSE;
+
+       *text = utf8;
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.24 */
+static gboolean parse_dataobj_items_next_action_indicator(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_items_next_action_indicator *inai = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len < 1) || (len > sizeof(inai->list)))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       inai->len = len;
+       memcpy(inai->list, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.25 */
+static gboolean parse_dataobj_event_list(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_event_list *el = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len == 0)
+               return TRUE;
+
+       if (len > sizeof(el->list))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       el->len = len;
+       memcpy(el->list, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.26 */
+static gboolean parse_dataobj_cause(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_cause *cause = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len == 1) || (len > sizeof(cause->cause)))
+               return FALSE;
+
+       cause->has_cause = TRUE;
+
+       if (len == 0)
+               return TRUE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       cause->len = len;
+       memcpy(cause->cause, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.27 */
+static gboolean parse_dataobj_location_status(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.28 */
+static gboolean parse_dataobj_transaction_id(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_transaction_id *ti = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len < 1) || (len > sizeof(ti->list)))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       ti->len = len;
+       memcpy(ti->list, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 31.111 Section 8.29 */
+static gboolean parse_dataobj_bcch_channel_list(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_bcch_channel_list *bcl = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+       unsigned int i;
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       bcl->num = len * 8 / 10;
+
+       for (i = 0; i < bcl->num; i++) {
+               unsigned int index = i * 10 / 8;
+               unsigned int occupied = i * 10 % 8;
+
+               bcl->channels[i] = (data[index] << (2 + occupied)) +
+                                       (data[index + 1] >> (6 - occupied));
+       }
+
+       bcl->has_list = TRUE;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.30 */
+static gboolean parse_dataobj_call_control_requested_action(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_common_byte_array *array = user;
+
+       return parse_dataobj_common_byte_array(iter, array);
+}
+
+/* Defined in TS 102.223 Section 8.31 */
+static gboolean parse_dataobj_icon_id(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_icon_id *id = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       id->qualifier = data[0];
+       id->id = data[1];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.32 */
+static gboolean parse_dataobj_item_icon_id_list(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_item_icon_id_list *iiil = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len < 2) || (len > 127))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       iiil->qualifier = data[0];
+       iiil->len = len - 1;
+       memcpy(iiil->list, data + 1, iiil->len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.33 */
+static gboolean parse_dataobj_card_reader_status(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.34 */
+static gboolean parse_dataobj_card_atr(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_card_atr *ca = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len < 1) || (len > sizeof(ca->atr)))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       ca->len = len;
+       memcpy(ca->atr, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.35 */
+static gboolean parse_dataobj_c_apdu(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_c_apdu *ca = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+       unsigned int pos;
+
+       if ((len < 4) || (len > 241))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       ca->cla = data[0];
+       ca->ins = data[1];
+       ca->p1 = data[2];
+       ca->p2 = data[3];
+
+       pos = 4;
+
+       /*
+        * lc is 0 has the same meaning as lc is absent. But le is 0 means
+        * the maximum number of bytes expected in the response data field
+        * is 256. So we need to rely on has_le to know if it presents.
+        */
+       if (len > 5) {
+               ca->lc = data[4];
+               if (ca->lc > sizeof(ca->data))
+                       return FALSE;
+
+               pos += ca->lc + 1;
+
+               if (len - pos > 1)
+                       return FALSE;
+
+               memcpy(ca->data, data+5, ca->lc);
+       }
+
+       if (len - pos > 0) {
+               ca->le = data[len - 1];
+               ca->has_le = TRUE;
+       }
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.36 */
+static gboolean parse_dataobj_r_apdu(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_r_apdu *ra = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len < 2) || (len > 239))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       ra->sw1 = data[len-2];
+       ra->sw2 = data[len-1];
+
+       if (len > 2) {
+               ra->len = len - 2;
+               memcpy(ra->data, data, ra->len);
+       } else
+               ra->len = 0;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.37 */
+static gboolean parse_dataobj_timer_id(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       unsigned char *byte = user;
+
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.38 */
+static gboolean parse_dataobj_timer_value(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_timer_value *tv = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 3)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       tv->hour = sms_decode_semi_octet(data[0]);
+       tv->minute = sms_decode_semi_octet(data[1]);
+       tv->second = sms_decode_semi_octet(data[2]);
+       tv->has_value = TRUE;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.39 */
+static gboolean parse_dataobj_datetime_timezone(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct sms_scts *scts = user;
+       const unsigned char *data;
+       int offset = 0;
+
+       if (comprehension_tlv_iter_get_length(iter) != 7)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       sms_decode_scts(data, 7, &offset, scts);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.40 */
+static gboolean parse_dataobj_at_command(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       char **command = user;
+       return parse_dataobj_common_text(iter, command);
+}
+
+/* Defined in TS 102.223 Section 8.41 */
+static gboolean parse_dataobj_at_response(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       char **response = user;
+       return parse_dataobj_common_text(iter, response);
+}
+
+/* Defined in TS 102.223 Section 8.42 */
+static gboolean parse_dataobj_bc_repeat_indicator(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_bc_repeat *bc_repeat = user;
+
+       if (parse_dataobj_common_byte(iter, &bc_repeat->value) != TRUE)
+               return FALSE;
+
+       bc_repeat->has_bc_repeat = TRUE;
+       return TRUE;
+}
+
+/* Defined in 102.223 Section 8.43 */
+static gboolean parse_dataobj_imm_resp(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       gboolean *ret = user;
+       return parse_dataobj_common_bool(iter, ret);
+}
+
+/* Defined in 102.223 Section 8.44 */
+static gboolean parse_dataobj_dtmf_string(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       char **dtmf = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       *dtmf = g_try_malloc(len * 2 + 1);
+       if (*dtmf == NULL)
+               return FALSE;
+
+       sim_extract_bcd_number(data, len, *dtmf);
+
+       return TRUE;
+}
+
+/* Defined in 102.223 Section 8.45 */
+static gboolean parse_dataobj_language(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       char *lang = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len != 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /*
+        * This is a 2 character pair as defined in ISO 639, coded using
+        * GSM default 7 bit alphabet with bit 8 set to 0.  Since the english
+        * letters have the same mapping in GSM as ASCII, no conversion
+        * is required here
+        */
+       memcpy(lang, data, len);
+       lang[len] = '\0';
+
+       return TRUE;
+}
+
+/* Defined in 31.111 Section 8.46 */
+static gboolean parse_dataobj_timing_advance(
+                       struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_timing_advance *ta = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len != 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       ta->has_value = TRUE;
+       ta->status = data[0];
+       ta->advance = data[1];
+
+       return TRUE;
+}
+
+/* Defined in 102.223 Section 8.47 */
+static gboolean parse_dataobj_browser_id(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       unsigned char *byte = user;
+
+       if (parse_dataobj_common_byte(iter, byte) == FALSE || *byte > 4)
+               return FALSE;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.48 */
+static gboolean parse_dataobj_url(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       char **url = user;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len == 0) {
+               *url = NULL;
+               return TRUE;
+       }
+
+       return parse_dataobj_common_text(iter, url);
+}
+
+/* Defined in TS 102.223 Section 8.49 */
+static gboolean parse_dataobj_bearer(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_common_byte_array *array = user;
+       return parse_dataobj_common_byte_array(iter, array);
+}
+
+/* Defined in TS 102.223 Section 8.50 */
+static gboolean parse_dataobj_provisioning_file_reference(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_file *f = user;
+       const unsigned char *data;
+       struct stk_file_iter sf_iter;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len < 1) || (len > 8))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       stk_file_iter_init(&sf_iter, data, len);
+       stk_file_iter_next(&sf_iter);
+
+       if (sf_iter.pos != sf_iter.max)
+               return FALSE;
+
+       f->len = len;
+       memcpy(f->file, data, len);
+
+       return TRUE;
+}
+
+/* Defined in 102.223 Section 8.51 */
+static gboolean parse_dataobj_browser_termination_cause(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.52 */
+static gboolean parse_dataobj_bearer_description(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_bearer_description *bd = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       bd->type = data[0];
+
+       /* Parse only the packet data service bearer parameters */
+       if (bd->type != STK_BEARER_TYPE_GPRS_UTRAN)
+               return FALSE;
+
+       if (len < 7)
+               return FALSE;
+
+       bd->gprs.precedence = data[1];
+       bd->gprs.delay = data[2];
+       bd->gprs.reliability = data[3];
+       bd->gprs.peak = data[4];
+       bd->gprs.mean = data[5];
+       bd->gprs.pdp_type = data[6];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.53 */
+static gboolean parse_dataobj_channel_data(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_common_byte_array *array = user;
+       return parse_dataobj_common_byte_array(iter, array);
+}
+
+/* Defined in TS 102.223 Section 8.54 */
+static gboolean parse_dataobj_channel_data_length(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.55 */
+static gboolean parse_dataobj_buffer_size(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       unsigned short *size = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       *size = (data[0] << 8) + data[1];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.56 */
+static gboolean parse_dataobj_channel_status(
+                       struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *status = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* Assume channel status is 2 bytes long */
+       memcpy(status, data, 2);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.57 */
+static gboolean parse_dataobj_card_reader_id(
+                       struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_card_reader_id *cr_id = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       cr_id->len = len;
+       memcpy(cr_id->id, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.58 */
+static gboolean parse_dataobj_other_address(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_other_address *oa = user;
+       const unsigned char *data;
+       unsigned char len = comprehension_tlv_iter_get_length(iter);
+
+       if (len == 0) {
+               oa->type = STK_ADDRESS_AUTO;
+               return TRUE;
+       }
+
+       if ((len != 5) && (len != 17))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if (data[0] != STK_ADDRESS_IPV4 && data[0] != STK_ADDRESS_IPV6)
+               return FALSE;
+
+       oa->type = data[0];
+
+       if (oa->type == STK_ADDRESS_IPV4)
+               memcpy(&oa->addr.ipv4, data + 1, 4);
+       else
+               memcpy(&oa->addr.ipv6, data + 1, 16);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.59 */
+static gboolean parse_dataobj_uicc_te_interface(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_uicc_te_interface *uti = user;
+       const unsigned char *data;
+       unsigned char len = comprehension_tlv_iter_get_length(iter);
+
+       if (len != 3)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       uti->protocol = data[0];
+       uti->port = (data[1] << 8) + data[2];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.60 */
+static gboolean parse_dataobj_aid(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_aid *aid = user;
+       const unsigned char *data;
+       unsigned char len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len > 16) || (len < 12))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       aid->len = len;
+       memcpy(aid->aid, data, len);
+
+       return TRUE;
+}
+
+/*
+ * Defined in TS 102.223 Section 8.61. According to it, the technology field
+ * can have at most 127 bytes. However, all the defined values are only 1 byte,
+ * so we just use 1 byte to represent it.
+ */
+static gboolean parse_dataobj_access_technology(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.62 */
+static gboolean parse_dataobj_display_parameters(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_display_parameters *dp = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 3)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       dp->height = data[0];
+       dp->width = data[1];
+       dp->effects = data[2];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.63 */
+static gboolean parse_dataobj_service_record(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_service_record *sr = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len < 3)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       sr->tech_id = data[0];
+       sr->serv_id = data[1];
+       sr->len = len - 2;
+
+       sr->serv_rec = g_try_malloc(sr->len);
+       if (sr->serv_rec == NULL)
+               return FALSE;
+
+       memcpy(sr->serv_rec, data + 2, sr->len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.64 */
+static gboolean parse_dataobj_device_filter(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_device_filter *df = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* According to TS 102.223, everything except BT & IRDA is RFU */
+       if (data[0] != STK_TECHNOLOGY_BLUETOOTH &&
+                       data[0] != STK_TECHNOLOGY_IRDA)
+               return FALSE;
+
+       df->tech_id = data[0];
+       df->len = len - 1;
+
+       df->dev_filter = g_try_malloc(df->len);
+       if (df->dev_filter == NULL)
+               return FALSE;
+
+       memcpy(df->dev_filter, data + 1, df->len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.65 */
+static gboolean parse_dataobj_service_search(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_service_search *ss = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* According to TS 102.223, everything except BT & IRDA is RFU */
+       if (data[0] != STK_TECHNOLOGY_BLUETOOTH &&
+                       data[0] != STK_TECHNOLOGY_IRDA)
+               return FALSE;
+
+       ss->tech_id = data[0];
+       ss->len = len - 1;
+
+       ss->ser_search = g_try_malloc(ss->len);
+       if (ss->ser_search == NULL)
+               return FALSE;
+
+       memcpy(ss->ser_search, data + 1, ss->len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.66 */
+static gboolean parse_dataobj_attribute_info(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_attribute_info *ai = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* According to TS 102.223, everything except BT & IRDA is RFU */
+       if (data[0] != STK_TECHNOLOGY_BLUETOOTH &&
+                       data[0] != STK_TECHNOLOGY_IRDA)
+               return FALSE;
+
+       ai->tech_id = data[0];
+       ai->len = len - 1;
+
+       ai->attr_info = g_try_malloc(ai->len);
+       if (ai->attr_info == NULL)
+               return FALSE;
+
+       memcpy(ai->attr_info, data + 1, ai->len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.67 */
+static gboolean parse_dataobj_service_availability(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_common_byte_array *array = user;
+       return parse_dataobj_common_byte_array(iter, array);
+}
+
+/* Defined in TS 102.223 Section 8.68 */
+static gboolean parse_dataobj_remote_entity_address(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_remote_entity_address *rea = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       switch (data[0]) {
+       case 0x00:
+               if (len != 7)
+                       return FALSE;
+               break;
+       case 0x01:
+               if (len != 5)
+                       return FALSE;
+               break;
+       default:
+               return FALSE;
+       }
+
+       rea->has_address = TRUE;
+       rea->coding_type = data[0];
+       memcpy(&rea->addr, data + 1, len - 1);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.69 */
+static gboolean parse_dataobj_esn(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       unsigned char *esn = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len != 4)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* Assume esn is 4 bytes long */
+       memcpy(esn, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.70 */
+static gboolean parse_dataobj_network_access_name(
+                                       struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       char **apn = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+       unsigned char label_size;
+       unsigned char offset = 0;
+       char decoded_apn[100];
+
+       if (len == 0 || len > 100)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /*
+        * As specified in TS 23 003 Section 9
+        * The APN consists of one or more labels. Each label is coded as
+        * a one octet length field followed by that number of octets coded
+        * as 8 bit ASCII characters
+        */
+       while (len) {
+               label_size = *data;
+
+               if (label_size == 0 || label_size > (len - 1))
+                       return FALSE;
+
+               memcpy(decoded_apn + offset, data + 1, label_size);
+
+               data += label_size + 1;
+               offset += label_size;
+               len -= label_size + 1;
+
+               if (len)
+                       decoded_apn[offset++] = '.';
+       }
+
+       decoded_apn[offset] = '\0';
+       *apn = g_strdup(decoded_apn);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.71 */
+static gboolean parse_dataobj_cdma_sms_tpdu(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_common_byte_array *array = user;
+       return parse_dataobj_common_byte_array(iter, array);
+}
+
+/* Defined in TS 102.223 Section 8.72 */
+static gboolean parse_dataobj_text_attr(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_text_attribute *attr = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+
+       if (len > sizeof(attr->attributes))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       memcpy(attr->attributes, data, len);
+       attr->len = len;
+
+       return TRUE;
+}
+
+/* Defined in TS 31.111 Section 8.72 */
+static gboolean parse_dataobj_pdp_act_par(
+                       struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_pdp_act_par *pcap = user;
+       const unsigned char *data;
+       unsigned int len;
+
+       len = comprehension_tlv_iter_get_length(iter);
+
+       if (len > sizeof(pcap->par))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       memcpy(pcap->par, data, len);
+       pcap->len = len;
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.73 */
+static gboolean parse_dataobj_item_text_attribute_list(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_item_text_attribute_list *ital = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if ((len > sizeof(ital->list)) || (len % 4 != 0))
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       memcpy(ital->list, data, len);
+       ital->len = len;
+
+       return TRUE;
+}
+
+/* Defined in TS 31.111 Section 8.73 */
+static gboolean parse_dataobj_utran_meas_qualifier(
+                       struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/*
+ * Defined in TS 102.223 Section 8.74.
+ *
+ * According to 3GPP TS 24.008, Section 10.5.1.4, IMEISV is composed of
+ * 16 digits and totally 9 bytes are used to represent it.
+ *
+ * Bits 1-3 of first byte represent the type of identity, and they
+ * are 0 1 1 separately for IMEISV. Bit 4 of first byte is the odd/even
+ * indication, and it's 0 to indicate IMEISV has odd number of digits (16).
+ * The rest bytes are coded using BCD coding.
+ *
+ * For example, if the IMEISV is "1234567890123456", then it's coded as
+ * "13 32 54 76 98 10 32 54 F6".
+ */
+static gboolean parse_dataobj_imeisv(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       char *imeisv = user;
+       const unsigned char *data;
+       unsigned int len;
+       static const char digit_lut[] = "0123456789*#abc\0";
+
+       len = comprehension_tlv_iter_get_length(iter);
+       if (len != 9)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if ((data[0] & 0x0f) != 0x03)
+               return FALSE;
+
+       if (data[8] >> 4 != 0x0f)
+               return FALSE;
+
+       /* Assume imeisv is at least 17 bytes long (16 for imeisv + null) */
+       imeisv[0] = digit_lut[data[0] >> 4];
+       extract_bcd_number(data + 1, 7, imeisv + 1);
+       imeisv[15] = digit_lut[data[8] & 0x0f];
+       imeisv[16] = '\0';
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.75 */
+static gboolean parse_dataobj_network_search_mode(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.76 */
+static gboolean parse_dataobj_battery_state(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       unsigned char *byte = user;
+       return parse_dataobj_common_byte(iter, byte);
+}
+
+/* Defined in TS 102.223 Section 8.77 */
+static gboolean parse_dataobj_browsing_status(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_common_byte_array *array = user;
+       return parse_dataobj_common_byte_array(iter, array);
+}
+
+/* Defined in TS 102.223 Section 8.78 */
+static gboolean parse_dataobj_frame_layout(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_frame_layout *fl = user;
+       const unsigned char *data;
+       unsigned char len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if (data[0] != STK_LAYOUT_HORIZONTAL &&
+                       data[0] != STK_LAYOUT_VERTICAL)
+               return FALSE;
+
+       fl->layout = data[0];
+       fl->len = len - 1;
+       memcpy(fl->size, data + 1, fl->len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.79 */
+static gboolean parse_dataobj_frames_info(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_frames_info *fi = user;
+       const unsigned char *data;
+       unsigned char len = comprehension_tlv_iter_get_length(iter);
+       unsigned int i;
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if (data[0] > 0x0f)
+               return FALSE;
+
+       if ((len == 1 && data[0] != 0) || (len > 1 && data[0] == 0))
+               return FALSE;
+
+       if (len % 2 == 0)
+               return FALSE;
+
+       if (len == 1)
+               return TRUE;
+
+       fi->id = data[0];
+       fi->len = (len - 1) / 2;
+       for (i = 0; i < len; i++) {
+               fi->list[i].height = data[i * 2 + 1] & 0x1f;
+               fi->list[i].width = data[i * 2 + 2] & 0x7f;
+       }
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.80 */
+static gboolean parse_dataobj_frame_id(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_frame_id *fi = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if (data[0] >= 0x10)
+               return FALSE;
+
+       fi->has_id = TRUE;
+       fi->id = data[0];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.81 */
+static gboolean parse_dataobj_meid(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       unsigned char *meid = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 8)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       /* Assume meid is 8 bytes long */
+       memcpy(meid, data, 8);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.82 */
+static gboolean parse_dataobj_mms_reference(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       struct stk_mms_reference *mr = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       mr->len = len;
+       memcpy(mr->ref, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.83 */
+static gboolean parse_dataobj_mms_id(struct comprehension_tlv_iter *iter,
+                                       void *user)
+{
+       struct stk_mms_id *mi = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       mi->len = len;
+       memcpy(mi->id, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.84 */
+static gboolean parse_dataobj_mms_transfer_status(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_mms_transfer_status *mts = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       mts->len = len;
+       memcpy(mts->status, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.85 */
+static gboolean parse_dataobj_mms_content_id(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_mms_content_id *mci = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+       mci->len = len;
+       memcpy(mci->id, data, len);
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.86 */
+static gboolean parse_dataobj_mms_notification(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_common_byte_array *array = user;
+       return parse_dataobj_common_byte_array(iter, array);
+}
+
+/* Defined in TS 102.223 Section 8.87 */
+static gboolean parse_dataobj_last_envelope(struct comprehension_tlv_iter *iter,
+                                               void *user)
+{
+       gboolean *ret = user;
+       return parse_dataobj_common_bool(iter, ret);
+}
+
+/* Defined in TS 102.223 Section 8.88 */
+static gboolean parse_dataobj_registry_application_data(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_registry_application_data *rad = user;
+       const unsigned char *data;
+       char *utf8;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 5)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       utf8 = decode_text(data[2], len - 4, data + 4);
+
+       if (utf8 == NULL)
+               return FALSE;
+
+       rad->name = utf8;
+       rad->port = (data[0] << 8) + data[1];
+       rad->type = data[3];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.89 */
+static gboolean parse_dataobj_activate_descriptor(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       unsigned char *byte = user;
+       const unsigned char *data;
+
+       if (comprehension_tlv_iter_get_length(iter) != 1)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if (data[0] != 0x01)
+               return FALSE;
+
+       *byte = data[0];
+
+       return TRUE;
+}
+
+/* Defined in TS 102.223 Section 8.90 */
+static gboolean parse_dataobj_broadcast_network_info(
+               struct comprehension_tlv_iter *iter, void *user)
+{
+       struct stk_broadcast_network_information *bni = user;
+       const unsigned char *data;
+       unsigned int len = comprehension_tlv_iter_get_length(iter);
+
+       if (len < 2)
+               return FALSE;
+
+       data = comprehension_tlv_iter_get_data(iter);
+
+       if (data[0] > 0x03)
+               return FALSE;
+
+       bni->tech = data[0];
+       bni->len = len - 1;
+       memcpy(bni->loc_info, data + 1, bni->len);
+
+       return TRUE;
+}
+
+static dataobj_handler handler_for_type(enum stk_data_object_type type)
+{
+       switch (type) {
+       case STK_DATA_OBJECT_TYPE_ADDRESS:
+               return parse_dataobj_address;
+       case STK_DATA_OBJECT_TYPE_ALPHA_ID:
+               return parse_dataobj_alpha_id;
+       case STK_DATA_OBJECT_TYPE_SUBADDRESS:
+               return parse_dataobj_subaddress;
+       case STK_DATA_OBJECT_TYPE_CCP:
+               return parse_dataobj_ccp;
+       case STK_DATA_OBJECT_TYPE_CBS_PAGE:
+               return parse_dataobj_cbs_page;
+       case STK_DATA_OBJECT_TYPE_DURATION:
+               return parse_dataobj_duration;
+       case STK_DATA_OBJECT_TYPE_ITEM:
+               return parse_dataobj_item;
+       case STK_DATA_OBJECT_TYPE_ITEM_ID:
+               return parse_dataobj_item_id;
+       case STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH:
+               return parse_dataobj_response_len;
+       case STK_DATA_OBJECT_TYPE_RESULT:
+               return parse_dataobj_result;
+       case STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU:
+               return parse_dataobj_gsm_sms_tpdu;
+       case STK_DATA_OBJECT_TYPE_SS_STRING:
+               return parse_dataobj_ss;
+       case STK_DATA_OBJECT_TYPE_TEXT:
+               return parse_dataobj_text;
+       case STK_DATA_OBJECT_TYPE_TONE:
+               return parse_dataobj_tone;
+       case STK_DATA_OBJECT_TYPE_USSD_STRING:
+               return parse_dataobj_ussd;
+       case STK_DATA_OBJECT_TYPE_FILE_LIST:
+               return parse_dataobj_file_list;
+       case STK_DATA_OBJECT_TYPE_LOCATION_INFO:
+               return parse_dataobj_location_info;
+       case STK_DATA_OBJECT_TYPE_IMEI:
+               return parse_dataobj_imei;
+       case STK_DATA_OBJECT_TYPE_HELP_REQUEST:
+               return parse_dataobj_help_request;
+       case STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS:
+               return parse_dataobj_network_measurement_results;
+       case STK_DATA_OBJECT_TYPE_DEFAULT_TEXT:
+               return parse_dataobj_default_text;
+       case STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR:
+               return parse_dataobj_items_next_action_indicator;
+       case STK_DATA_OBJECT_TYPE_EVENT_LIST:
+               return parse_dataobj_event_list;
+       case STK_DATA_OBJECT_TYPE_CAUSE:
+               return parse_dataobj_cause;
+       case STK_DATA_OBJECT_TYPE_LOCATION_STATUS:
+               return parse_dataobj_location_status;
+       case STK_DATA_OBJECT_TYPE_TRANSACTION_ID:
+               return parse_dataobj_transaction_id;
+       case STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST:
+               return parse_dataobj_bcch_channel_list;
+       case STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION:
+               return parse_dataobj_call_control_requested_action;
+       case STK_DATA_OBJECT_TYPE_ICON_ID:
+               return parse_dataobj_icon_id;
+       case STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST:
+               return parse_dataobj_item_icon_id_list;
+       case STK_DATA_OBJECT_TYPE_CARD_READER_STATUS:
+               return parse_dataobj_card_reader_status;
+       case STK_DATA_OBJECT_TYPE_CARD_ATR:
+               return parse_dataobj_card_atr;
+       case STK_DATA_OBJECT_TYPE_C_APDU:
+               return parse_dataobj_c_apdu;
+       case STK_DATA_OBJECT_TYPE_R_APDU:
+               return parse_dataobj_r_apdu;
+       case STK_DATA_OBJECT_TYPE_TIMER_ID:
+               return parse_dataobj_timer_id;
+       case STK_DATA_OBJECT_TYPE_TIMER_VALUE:
+               return parse_dataobj_timer_value;
+       case STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE:
+               return parse_dataobj_datetime_timezone;
+       case STK_DATA_OBJECT_TYPE_AT_COMMAND:
+               return parse_dataobj_at_command;
+       case STK_DATA_OBJECT_TYPE_AT_RESPONSE:
+               return parse_dataobj_at_response;
+       case STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR:
+               return parse_dataobj_bc_repeat_indicator;
+       case STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE:
+               return parse_dataobj_imm_resp;
+       case STK_DATA_OBJECT_TYPE_DTMF_STRING:
+               return parse_dataobj_dtmf_string;
+       case STK_DATA_OBJECT_TYPE_LANGUAGE:
+               return parse_dataobj_language;
+       case STK_DATA_OBJECT_TYPE_BROWSER_ID:
+               return parse_dataobj_browser_id;
+       case STK_DATA_OBJECT_TYPE_TIMING_ADVANCE:
+               return parse_dataobj_timing_advance;
+       case STK_DATA_OBJECT_TYPE_URL:
+               return parse_dataobj_url;
+       case STK_DATA_OBJECT_TYPE_BEARER:
+               return parse_dataobj_bearer;
+       case STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF:
+               return parse_dataobj_provisioning_file_reference;
+       case STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE:
+               return parse_dataobj_browser_termination_cause;
+       case STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION:
+               return parse_dataobj_bearer_description;
+       case STK_DATA_OBJECT_TYPE_CHANNEL_DATA:
+               return parse_dataobj_channel_data;
+       case STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH:
+               return parse_dataobj_channel_data_length;
+       case STK_DATA_OBJECT_TYPE_BUFFER_SIZE:
+               return parse_dataobj_buffer_size;
+       case STK_DATA_OBJECT_TYPE_CHANNEL_STATUS:
+               return parse_dataobj_channel_status;
+       case STK_DATA_OBJECT_TYPE_CARD_READER_ID:
+               return parse_dataobj_card_reader_id;
+       case STK_DATA_OBJECT_TYPE_OTHER_ADDRESS:
+               return parse_dataobj_other_address;
+       case STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE:
+               return parse_dataobj_uicc_te_interface;
+       case STK_DATA_OBJECT_TYPE_AID:
+               return parse_dataobj_aid;
+       case STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY:
+               return parse_dataobj_access_technology;
+       case STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS:
+               return parse_dataobj_display_parameters;
+       case STK_DATA_OBJECT_TYPE_SERVICE_RECORD:
+               return parse_dataobj_service_record;
+       case STK_DATA_OBJECT_TYPE_DEVICE_FILTER:
+               return parse_dataobj_device_filter;
+       case STK_DATA_OBJECT_TYPE_SERVICE_SEARCH:
+               return parse_dataobj_service_search;
+       case STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO:
+               return parse_dataobj_attribute_info;
+       case STK_DATA_OBJECT_TYPE_SERVICE_AVAILABILITY:
+               return parse_dataobj_service_availability;
+       case STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS:
+               return parse_dataobj_remote_entity_address;
+       case STK_DATA_OBJECT_TYPE_ESN:
+               return parse_dataobj_esn;
+       case STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME:
+               return parse_dataobj_network_access_name;
+       case STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU:
+               return parse_dataobj_cdma_sms_tpdu;
+       case STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE:
+               return parse_dataobj_text_attr;
+       case STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER:
+               return parse_dataobj_pdp_act_par;
+       case STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST:
+               return parse_dataobj_item_text_attribute_list;
+       case STK_DATA_OBJECT_TYPE_UTRAN_MEASUREMENT_QUALIFIER:
+               return parse_dataobj_utran_meas_qualifier;
+       case STK_DATA_OBJECT_TYPE_IMEISV:
+               return parse_dataobj_imeisv;
+       case STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE:
+               return parse_dataobj_network_search_mode;
+       case STK_DATA_OBJECT_TYPE_BATTERY_STATE:
+               return parse_dataobj_battery_state;
+       case STK_DATA_OBJECT_TYPE_BROWSING_STATUS:
+               return parse_dataobj_browsing_status;
+       case STK_DATA_OBJECT_TYPE_FRAME_LAYOUT:
+               return parse_dataobj_frame_layout;
+       case STK_DATA_OBJECT_TYPE_FRAMES_INFO:
+               return parse_dataobj_frames_info;
+       case STK_DATA_OBJECT_TYPE_FRAME_ID:
+               return parse_dataobj_frame_id;
+       case STK_DATA_OBJECT_TYPE_MEID:
+               return parse_dataobj_meid;
+       case STK_DATA_OBJECT_TYPE_MMS_REFERENCE:
+               return parse_dataobj_mms_reference;
+       case STK_DATA_OBJECT_TYPE_MMS_ID:
+               return parse_dataobj_mms_id;
+       case STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS:
+               return parse_dataobj_mms_transfer_status;
+       case STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID:
+               return parse_dataobj_mms_content_id;
+       case STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION:
+               return parse_dataobj_mms_notification;
+       case STK_DATA_OBJECT_TYPE_LAST_ENVELOPE:
+               return parse_dataobj_last_envelope;
+       case STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA:
+               return parse_dataobj_registry_application_data;
+       case STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR:
+               return parse_dataobj_activate_descriptor;
+       case STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO:
+               return parse_dataobj_broadcast_network_info;
+       default:
+               return NULL;
+       }
+}
+
+static void destroy_stk_item(struct stk_item *item)
+{
+       g_free(item->text);
+       g_free(item);
+}
+
+static gboolean parse_item_list(struct comprehension_tlv_iter *iter,
+                               void *data)
+{
+       GSList **out = data;
+       unsigned short tag = STK_DATA_OBJECT_TYPE_ITEM;
+       struct comprehension_tlv_iter iter_old;
+       struct stk_item item;
+       GSList *list = NULL;
+       unsigned int count = 0;
+       gboolean has_empty = FALSE;
+
+       do {
+               comprehension_tlv_iter_copy(iter, &iter_old);
+               memset(&item, 0, sizeof(item));
+               count++;
+
+               if (parse_dataobj_item(iter, &item) == TRUE) {
+                       if (item.id == 0) {
+                               has_empty = TRUE;
+                               continue;
+                       }
+
+                       list = g_slist_prepend(list,
+                                               g_memdup(&item, sizeof(item)));
+               }
+       } while (comprehension_tlv_iter_next(iter) == TRUE &&
+                       comprehension_tlv_iter_get_tag(iter) == tag);
+
+       comprehension_tlv_iter_copy(&iter_old, iter);
+
+       if (!has_empty) {
+               *out = g_slist_reverse(list);
+               return TRUE;
+       }
+
+       if (count == 1)
+               return TRUE;
+
+       g_slist_foreach(list, (GFunc) destroy_stk_item, NULL);
+       g_slist_free(list);
+       return FALSE;
+
+}
+
+static gboolean parse_provisioning_list(struct comprehension_tlv_iter *iter,
+                                       void *data)
+{
+       GSList **out = data;
+       unsigned short tag = STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF;
+       struct comprehension_tlv_iter iter_old;
+       struct stk_file file;
+       GSList *list = NULL;
+
+       do {
+               comprehension_tlv_iter_copy(iter, &iter_old);
+               memset(&file, 0, sizeof(file));
+
+               if (parse_dataobj_provisioning_file_reference(iter, &file)
+                                                                       == TRUE)
+                       list = g_slist_prepend(list,
+                                               g_memdup(&file, sizeof(file)));
+       } while (comprehension_tlv_iter_next(iter) == TRUE &&
+                       comprehension_tlv_iter_get_tag(iter) == tag);
+
+       comprehension_tlv_iter_copy(&iter_old, iter);
+       *out = g_slist_reverse(list);
+
+       return TRUE;
+}
+
+static dataobj_handler list_handler_for_type(enum stk_data_object_type type)
+{
+       switch (type) {
+       case STK_DATA_OBJECT_TYPE_ITEM:
+               return parse_item_list;
+       case STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF:
+               return parse_provisioning_list;
+       default:
+               return NULL;
+       }
+}
+
+struct dataobj_handler_entry {
+       enum stk_data_object_type type;
+       int flags;
+       void *data;
+};
+
+static enum stk_command_parse_result parse_dataobj(
+                                       struct comprehension_tlv_iter *iter,
+                                       enum stk_data_object_type type, ...)
+{
+       GSList *entries = NULL;
+       GSList *l;
+       va_list args;
+       gboolean minimum_set = TRUE;
+       gboolean parse_error = FALSE;
+
+       va_start(args, type);
+
+       while (type != STK_DATA_OBJECT_TYPE_INVALID) {
+               struct dataobj_handler_entry *entry;
+
+               entry = g_new0(struct dataobj_handler_entry, 1);
+
+               entry->type = type;
+               entry->flags = va_arg(args, int);
+               entry->data = va_arg(args, void *);
+
+               type = va_arg(args, enum stk_data_object_type);
+               entries = g_slist_prepend(entries, entry);
+       }
+
+       va_end(args);
+
+       entries = g_slist_reverse(entries);
+
+       l = entries;
+       while (comprehension_tlv_iter_next(iter) == TRUE) {
+               dataobj_handler handler;
+               struct dataobj_handler_entry *entry;
+               GSList *l2;
+
+               for (l2 = l; l2; l2 = l2->next) {
+                       entry = l2->data;
+
+                       if (comprehension_tlv_iter_get_tag(iter) == entry->type)
+                               break;
+
+                       /* Can't skip over mandatory objects */
+                       if (entry->flags & DATAOBJ_FLAG_MANDATORY) {
+                               l2 = NULL;
+                               break;
+                       }
+               }
+
+               if (l2 == NULL) {
+                       if (comprehension_tlv_get_cr(iter) == TRUE)
+                               parse_error = TRUE;
+
+                       continue;
+               }
+
+               if (entry->flags & DATAOBJ_FLAG_LIST)
+                       handler = list_handler_for_type(entry->type);
+               else
+                       handler = handler_for_type(entry->type);
+
+               if (handler(iter, entry->data) == FALSE)
+                       parse_error = TRUE;
+
+               l = l2->next;
+       }
+
+       for (; l; l = l->next) {
+               struct dataobj_handler_entry *entry = l->data;
+
+               if (entry->flags & DATAOBJ_FLAG_MANDATORY)
+                       minimum_set = FALSE;
+       }
+
+       g_slist_foreach(entries, (GFunc) g_free, NULL);
+       g_slist_free(entries);
+
+       if (minimum_set == FALSE)
+               return STK_PARSE_RESULT_MISSING_VALUE;
+       if (parse_error == TRUE)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static void destroy_display_text(struct stk_command *command)
+{
+       g_free(command->display_text.text);
+}
+
+static enum stk_command_parse_result parse_display_text(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_display_text *obj = &command->display_text;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_DISPLAY)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_display_text;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->text,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0,
+                               &obj->immediate_response,
+                               STK_DATA_OBJECT_TYPE_DURATION, 0,
+                               &obj->duration,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_get_inkey(struct stk_command *command)
+{
+       g_free(command->get_inkey.text);
+}
+
+static enum stk_command_parse_result parse_get_inkey(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_get_inkey *obj = &command->get_inkey;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_get_inkey;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->text,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_DURATION, 0,
+                               &obj->duration,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_get_input(struct stk_command *command)
+{
+       g_free(command->get_input.text);
+       g_free(command->get_input.default_text);
+}
+
+static enum stk_command_parse_result parse_get_input(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_get_input *obj = &command->get_input;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_get_input;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->text,
+                               STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->resp_len,
+                               STK_DATA_OBJECT_TYPE_DEFAULT_TEXT, 0,
+                               &obj->default_text,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
+
+       return status;
+}
+
+static enum stk_command_parse_result parse_more_time(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static void destroy_play_tone(struct stk_command *command)
+{
+       g_free(command->play_tone.alpha_id);
+}
+
+static enum stk_command_parse_result parse_play_tone(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_play_tone *obj = &command->play_tone;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_EARPIECE)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_play_tone;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_TONE, 0,
+                               &obj->tone,
+                               STK_DATA_OBJECT_TYPE_DURATION, 0,
+                               &obj->duration,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static enum stk_command_parse_result parse_poll_interval(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_poll_interval *obj = &command->poll_interval;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_DURATION,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->duration,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_setup_menu(struct stk_command *command)
+{
+       g_free(command->setup_menu.alpha_id);
+       g_slist_foreach(command->setup_menu.items,
+                               (GFunc) destroy_stk_item, NULL);
+       g_slist_free(command->setup_menu.items);
+}
+
+static enum stk_command_parse_result parse_setup_menu(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_setup_menu *obj = &command->setup_menu;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_setup_menu;
+
+       status = parse_dataobj(iter,
+                       STK_DATA_OBJECT_TYPE_ALPHA_ID,
+                       DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                       &obj->alpha_id,
+                       STK_DATA_OBJECT_TYPE_ITEM,
+                       DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM |
+                       DATAOBJ_FLAG_LIST, &obj->items,
+                       STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR, 0,
+                       &obj->next_act,
+                       STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                       &obj->icon_id,
+                       STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST, 0,
+                       &obj->item_icon_id_list,
+                       STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                       &obj->text_attr,
+                       STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST, 0,
+                       &obj->item_text_attr_list,
+                       STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_select_item(struct stk_command *command)
+{
+       g_free(command->select_item.alpha_id);
+       g_slist_foreach(command->select_item.items,
+                               (GFunc) destroy_stk_item, NULL);
+       g_slist_free(command->select_item.items);
+}
+
+static enum stk_command_parse_result parse_select_item(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_select_item *obj = &command->select_item;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       status = parse_dataobj(iter,
+                       STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                       &obj->alpha_id,
+                       STK_DATA_OBJECT_TYPE_ITEM,
+                       DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM |
+                       DATAOBJ_FLAG_LIST, &obj->items,
+                       STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR, 0,
+                       &obj->next_act,
+                       STK_DATA_OBJECT_TYPE_ITEM_ID, 0,
+                       &obj->item_id,
+                       STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                       &obj->icon_id,
+                       STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST, 0,
+                       &obj->item_icon_id_list,
+                       STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                       &obj->text_attr,
+                       STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST, 0,
+                       &obj->item_text_attr_list,
+                       STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                       &obj->frame_id,
+                       STK_DATA_OBJECT_TYPE_INVALID);
+
+       command->destructor = destroy_select_item;
+
+       if (status == STK_PARSE_RESULT_OK && obj->items == NULL)
+               status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_send_sms(struct stk_command *command)
+{
+       g_free(command->send_sms.alpha_id);
+       g_free(command->send_sms.cdma_sms.array);
+}
+
+static enum stk_command_parse_result parse_send_sms(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_send_sms *obj = &command->send_sms;
+       enum stk_command_parse_result status;
+       struct gsm_sms_tpdu gsm_tpdu;
+       struct stk_address sc_address = { 0, NULL };
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       memset(&gsm_tpdu, 0, sizeof(gsm_tpdu));
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ADDRESS, 0,
+                               &sc_address,
+                               STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU, 0,
+                               &gsm_tpdu,
+                               STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU, 0,
+                               &obj->cdma_sms,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       command->destructor = destroy_send_sms;
+
+       if (status != STK_PARSE_RESULT_OK)
+               goto out;
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       if (status != STK_PARSE_RESULT_OK)
+               goto out;
+
+       if (gsm_tpdu.len == 0 && obj->cdma_sms.len == 0) {
+               status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               goto out;
+       }
+
+       if (gsm_tpdu.len > 0 && obj->cdma_sms.len > 0) {
+               status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               goto out;
+       }
+
+       /* We don't process CDMA pdus for now */
+       if (obj->cdma_sms.len > 0)
+               goto out;
+
+       /* packing is needed */
+       if (command->qualifier & 0x01) {
+               if (sms_decode_unpacked_stk_pdu(gsm_tpdu.tpdu, gsm_tpdu.len,
+                                                       &obj->gsm_sms) !=
+                               TRUE) {
+                       status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+                       goto out;
+               }
+
+               goto set_addr;
+       }
+
+       if (sms_decode(gsm_tpdu.tpdu, gsm_tpdu.len, TRUE,
+                               gsm_tpdu.len, &obj->gsm_sms) == FALSE) {
+               status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               goto out;
+       }
+
+       if (obj->gsm_sms.type != SMS_TYPE_SUBMIT &&
+                       obj->gsm_sms.type != SMS_TYPE_COMMAND) {
+               status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               goto out;
+       }
+
+set_addr:
+       if (sc_address.number == NULL)
+               goto out;
+
+       if (strlen(sc_address.number) > 20) {
+               status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               goto out;
+       }
+
+       strcpy(obj->gsm_sms.sc_addr.address, sc_address.number);
+       obj->gsm_sms.sc_addr.numbering_plan = sc_address.ton_npi & 15;
+       obj->gsm_sms.sc_addr.number_type = (sc_address.ton_npi >> 4) & 7;
+
+out:
+       g_free(sc_address.number);
+
+       return status;
+}
+
+static void destroy_send_ss(struct stk_command *command)
+{
+       g_free(command->send_ss.alpha_id);
+       g_free(command->send_ss.ss.ss);
+}
+
+static enum stk_command_parse_result parse_send_ss(struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_send_ss *obj = &command->send_ss;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_send_ss;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_SS_STRING,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->ss,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_send_ussd(struct stk_command *command)
+{
+       g_free(command->send_ussd.alpha_id);
+}
+
+static enum stk_command_parse_result parse_send_ussd(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_send_ussd *obj = &command->send_ussd;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_send_ussd;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_USSD_STRING,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->ussd_string,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_setup_call(struct stk_command *command)
+{
+       g_free(command->setup_call.alpha_id_usr_cfm);
+       g_free(command->setup_call.addr.number);
+       g_free(command->setup_call.alpha_id_call_setup);
+}
+
+static enum stk_command_parse_result parse_setup_call(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_setup_call *obj = &command->setup_call;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_setup_call;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id_usr_cfm,
+                               STK_DATA_OBJECT_TYPE_ADDRESS,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->addr,
+                               STK_DATA_OBJECT_TYPE_CCP, 0,
+                               &obj->ccp,
+                               STK_DATA_OBJECT_TYPE_SUBADDRESS, 0,
+                               &obj->subaddr,
+                               STK_DATA_OBJECT_TYPE_DURATION, 0,
+                               &obj->duration,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id_usr_cfm,
+                               STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id_call_setup,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id_call_setup,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr_usr_cfm,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr_call_setup,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id_usr_cfm, obj->icon_id_usr_cfm.id);
+       CHECK_TEXT_AND_ICON(obj->alpha_id_call_setup,
+                                               obj->icon_id_call_setup.id);
+
+       return status;
+}
+
+static void destroy_refresh(struct stk_command *command)
+{
+       g_slist_foreach(command->refresh.file_list, (GFunc) g_free, NULL);
+       g_slist_free(command->refresh.file_list);
+       g_free(command->refresh.alpha_id);
+}
+
+static enum stk_command_parse_result parse_refresh(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_refresh *obj = &command->refresh;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_refresh;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FILE_LIST, 0,
+                               &obj->file_list,
+                               STK_DATA_OBJECT_TYPE_AID, 0,
+                               &obj->aid,
+                               STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static enum stk_command_parse_result parse_polling_off(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static enum stk_command_parse_result parse_provide_local_info(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static enum stk_command_parse_result parse_setup_event_list(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_setup_event_list *obj = &command->setup_event_list;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_EVENT_LIST,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->event_list,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static enum stk_command_parse_result parse_perform_card_apdu(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_perform_card_apdu *obj = &command->perform_card_apdu;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
+                       (command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_C_APDU,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->c_apdu,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static enum stk_command_parse_result parse_power_off_card(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
+                       (command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static enum stk_command_parse_result parse_power_on_card(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
+                       (command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static enum stk_command_parse_result parse_get_reader_status(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       switch (command->qualifier) {
+       case STK_QUALIFIER_TYPE_CARD_READER_STATUS:
+               if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+                       return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               break;
+       case STK_QUALIFIER_TYPE_CARD_READER_ID:
+               if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
+                               (command->dst >
+                                       STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
+                       return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               break;
+       default:
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+       }
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static enum stk_command_parse_result parse_timer_mgmt(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_timer_mgmt *obj = &command->timer_mgmt;
+       enum stk_data_object_flag value_flags = 0;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if ((command->qualifier & 3) == 0) /* Start a timer */
+               value_flags = DATAOBJ_FLAG_MANDATORY;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TIMER_ID,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->timer_id,
+                               STK_DATA_OBJECT_TYPE_TIMER_VALUE, value_flags,
+                               &obj->timer_value,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_setup_idle_mode_text(struct stk_command *command)
+{
+       g_free(command->setup_idle_mode_text.text);
+}
+
+static enum stk_command_parse_result parse_setup_idle_mode_text(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_setup_idle_mode_text *obj =
+                                       &command->setup_idle_mode_text;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_setup_idle_mode_text;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->text,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_run_at_command(struct stk_command *command)
+{
+       g_free(command->run_at_command.alpha_id);
+       g_free(command->run_at_command.at_command);
+}
+
+static enum stk_command_parse_result parse_run_at_command(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_run_at_command *obj = &command->run_at_command;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_run_at_command;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_AT_COMMAND,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->at_command,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_send_dtmf(struct stk_command *command)
+{
+       g_free(command->send_dtmf.alpha_id);
+       g_free(command->send_dtmf.dtmf);
+}
+
+static enum stk_command_parse_result parse_send_dtmf(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_send_dtmf *obj = &command->send_dtmf;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_send_dtmf;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_DTMF_STRING,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->dtmf,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static enum stk_command_parse_result parse_language_notification(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_language_notification *obj =
+                                       &command->language_notification;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_LANGUAGE, 0,
+                               &obj->language,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_launch_browser(struct stk_command *command)
+{
+       g_free(command->launch_browser.url);
+       g_free(command->launch_browser.bearer.array);
+       g_slist_foreach(command->launch_browser.prov_file_refs,
+                               (GFunc) g_free, NULL);
+       g_slist_free(command->launch_browser.prov_file_refs);
+       g_free(command->launch_browser.text_gateway_proxy_id);
+       g_free(command->launch_browser.alpha_id);
+       g_free(command->launch_browser.network_name.array);
+       g_free(command->launch_browser.text_usr);
+       g_free(command->launch_browser.text_passwd);
+}
+
+static enum stk_command_parse_result parse_launch_browser(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_launch_browser *obj = &command->launch_browser;
+
+       if (command->qualifier > 3 || command->qualifier == 1)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_launch_browser;
+
+       return parse_dataobj(iter,
+                               STK_DATA_OBJECT_TYPE_BROWSER_ID, 0,
+                               &obj->browser_id,
+                               STK_DATA_OBJECT_TYPE_URL,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->url,
+                               STK_DATA_OBJECT_TYPE_BEARER, 0,
+                               &obj->bearer,
+                               STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF,
+                               DATAOBJ_FLAG_LIST,
+                               &obj->prov_file_refs,
+                               STK_DATA_OBJECT_TYPE_TEXT, 0,
+                               &obj->text_gateway_proxy_id,
+                               STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME, 0,
+                               &obj->network_name,
+                               STK_DATA_OBJECT_TYPE_TEXT, 0,
+                               &obj->text_usr,
+                               STK_DATA_OBJECT_TYPE_TEXT, 0,
+                               &obj->text_passwd,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_open_channel(struct stk_command *command)
+{
+       g_free(command->open_channel.alpha_id);
+       g_free(command->open_channel.apn);
+       g_free(command->open_channel.text_usr);
+       g_free(command->open_channel.text_passwd);
+}
+
+static enum stk_command_parse_result parse_open_channel(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_open_channel *obj = &command->open_channel;
+       enum stk_command_parse_result status;
+
+       if (command->qualifier >= 0x08)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_open_channel;
+
+       /*
+        * parse the Open Channel data objects related to packet data service
+        * bearer
+        */
+       status = parse_dataobj(iter,
+                               STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->bearer_desc,
+                               STK_DATA_OBJECT_TYPE_BUFFER_SIZE,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->buf_size,
+                               STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME, 0,
+                               &obj->apn,
+                               STK_DATA_OBJECT_TYPE_OTHER_ADDRESS, 0,
+                               &obj->local_addr,
+                               STK_DATA_OBJECT_TYPE_TEXT, 0,
+                               &obj->text_usr,
+                               STK_DATA_OBJECT_TYPE_TEXT, 0,
+                               &obj->text_passwd,
+                               STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE, 0,
+                               &obj->uti,
+                               STK_DATA_OBJECT_TYPE_OTHER_ADDRESS, 0,
+                               &obj->data_dest_addr,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_close_channel(struct stk_command *command)
+{
+       g_free(command->close_channel.alpha_id);
+}
+
+static enum stk_command_parse_result parse_close_channel(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_close_channel *obj = &command->close_channel;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) ||
+                       (command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7))
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_close_channel;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_receive_data(struct stk_command *command)
+{
+       g_free(command->receive_data.alpha_id);
+}
+
+static enum stk_command_parse_result parse_receive_data(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_receive_data *obj = &command->receive_data;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) ||
+                       (command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7))
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_receive_data;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->data_len,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_send_data(struct stk_command *command)
+{
+       g_free(command->send_data.alpha_id);
+       g_free(command->send_data.data.array);
+}
+
+static enum stk_command_parse_result parse_send_data(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_send_data *obj = &command->send_data;
+       enum stk_command_parse_result status;
+
+       if (command->qualifier > STK_SEND_DATA_IMMEDIATELY)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) ||
+                       (command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7))
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_send_data;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_CHANNEL_DATA,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->data,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static enum stk_command_parse_result parse_get_channel_status(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static void destroy_service_search(struct stk_command *command)
+{
+       g_free(command->service_search.alpha_id);
+       g_free(command->service_search.serv_search.ser_search);
+       g_free(command->service_search.dev_filter.dev_filter);
+}
+
+static enum stk_command_parse_result parse_service_search(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_service_search *obj = &command->service_search;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_service_search;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_SERVICE_SEARCH,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->serv_search,
+                               STK_DATA_OBJECT_TYPE_DEVICE_FILTER, 0,
+                               &obj->dev_filter,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_get_service_info(struct stk_command *command)
+{
+       g_free(command->get_service_info.alpha_id);
+       g_free(command->get_service_info.attr_info.attr_info);
+}
+
+static enum stk_command_parse_result parse_get_service_info(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_get_service_info *obj = &command->get_service_info;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_get_service_info;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->attr_info,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static void destroy_declare_service(struct stk_command *command)
+{
+       g_free(command->declare_service.serv_rec.serv_rec);
+}
+
+static enum stk_command_parse_result parse_declare_service(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_declare_service *obj = &command->declare_service;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_declare_service;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_SERVICE_RECORD,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->serv_rec,
+                               STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE, 0,
+                               &obj->intf,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static enum stk_command_parse_result parse_set_frames(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_set_frames *obj = &command->set_frames;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FRAME_ID,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_FRAME_LAYOUT, 0,
+                               &obj->frame_layout,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id_default,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static enum stk_command_parse_result parse_get_frames_status(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return STK_PARSE_RESULT_OK;
+}
+
+static void destroy_retrieve_mms(struct stk_command *command)
+{
+       g_free(command->retrieve_mms.alpha_id);
+       g_slist_foreach(command->retrieve_mms.mms_rec_files,
+                                               (GFunc) g_free, NULL);
+       g_slist_free(command->retrieve_mms.mms_rec_files);
+}
+
+static enum stk_command_parse_result parse_retrieve_mms(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_retrieve_mms *obj = &command->retrieve_mms;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_retrieve_mms;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_MMS_REFERENCE,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->mms_ref,
+                               STK_DATA_OBJECT_TYPE_FILE_LIST,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->mms_rec_files,
+                               STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->mms_content_id,
+                               STK_DATA_OBJECT_TYPE_MMS_ID, 0,
+                               &obj->mms_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_submit_mms(struct stk_command *command)
+{
+       g_free(command->submit_mms.alpha_id);
+       g_slist_foreach(command->submit_mms.mms_subm_files,
+                                               (GFunc) g_free, NULL);
+       g_slist_free(command->submit_mms.mms_subm_files);
+}
+
+static enum stk_command_parse_result parse_submit_mms(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_submit_mms *obj = &command->submit_mms;
+       enum stk_command_parse_result status;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_submit_mms;
+
+       status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
+                               &obj->alpha_id,
+                               STK_DATA_OBJECT_TYPE_ICON_ID, 0,
+                               &obj->icon_id,
+                               STK_DATA_OBJECT_TYPE_FILE_LIST,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->mms_subm_files,
+                               STK_DATA_OBJECT_TYPE_MMS_ID, 0,
+                               &obj->mms_id,
+                               STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
+                               &obj->text_attr,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+
+       CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
+
+       return status;
+}
+
+static void destroy_display_mms(struct stk_command *command)
+{
+       g_slist_foreach(command->display_mms.mms_subm_files,
+                                               (GFunc) g_free, NULL);
+       g_slist_free(command->display_mms.mms_subm_files);
+}
+
+static enum stk_command_parse_result parse_display_mms(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_display_mms *obj = &command->display_mms;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       command->destructor = destroy_display_mms;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FILE_LIST,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->mms_subm_files,
+                               STK_DATA_OBJECT_TYPE_MMS_ID,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->mms_id,
+                               STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0,
+                               &obj->imd_resp,
+                               STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
+                               &obj->frame_id,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static enum stk_command_parse_result parse_activate(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       struct stk_command_activate *obj = &command->activate;
+
+       if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
+               return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+
+       return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR,
+                               DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
+                               &obj->actv_desc,
+                               STK_DATA_OBJECT_TYPE_INVALID);
+}
+
+static enum stk_command_parse_result parse_command_body(
+                                       struct stk_command *command,
+                                       struct comprehension_tlv_iter *iter)
+{
+       switch (command->type) {
+       case STK_COMMAND_TYPE_DISPLAY_TEXT:
+               return parse_display_text(command, iter);
+       case STK_COMMAND_TYPE_GET_INKEY:
+               return parse_get_inkey(command, iter);
+       case STK_COMMAND_TYPE_GET_INPUT:
+               return parse_get_input(command, iter);
+       case STK_COMMAND_TYPE_MORE_TIME:
+               return parse_more_time(command, iter);
+       case STK_COMMAND_TYPE_PLAY_TONE:
+               return parse_play_tone(command, iter);
+       case STK_COMMAND_TYPE_POLL_INTERVAL:
+               return parse_poll_interval(command, iter);
+       case STK_COMMAND_TYPE_SETUP_MENU:
+               return parse_setup_menu(command, iter);
+       case STK_COMMAND_TYPE_SELECT_ITEM:
+               return parse_select_item(command, iter);
+       case STK_COMMAND_TYPE_SEND_SMS:
+               return parse_send_sms(command, iter);
+       case STK_COMMAND_TYPE_SEND_SS:
+               return parse_send_ss(command, iter);
+       case STK_COMMAND_TYPE_SEND_USSD:
+               return parse_send_ussd(command, iter);
+       case STK_COMMAND_TYPE_SETUP_CALL:
+               return parse_setup_call(command, iter);
+       case STK_COMMAND_TYPE_REFRESH:
+               return parse_refresh(command, iter);
+       case STK_COMMAND_TYPE_POLLING_OFF:
+               return parse_polling_off(command, iter);
+       case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO:
+               return parse_provide_local_info(command, iter);
+       case STK_COMMAND_TYPE_SETUP_EVENT_LIST:
+               return parse_setup_event_list(command, iter);
+       case STK_COMMAND_TYPE_PERFORM_CARD_APDU:
+               return parse_perform_card_apdu(command, iter);
+       case STK_COMMAND_TYPE_POWER_OFF_CARD:
+               return parse_power_off_card(command, iter);
+       case STK_COMMAND_TYPE_POWER_ON_CARD:
+               return parse_power_on_card(command, iter);
+       case STK_COMMAND_TYPE_GET_READER_STATUS:
+               return parse_get_reader_status(command, iter);
+       case STK_COMMAND_TYPE_TIMER_MANAGEMENT:
+               return parse_timer_mgmt(command, iter);
+       case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT:
+               return parse_setup_idle_mode_text(command, iter);
+       case STK_COMMAND_TYPE_RUN_AT_COMMAND:
+               return parse_run_at_command(command, iter);
+       case STK_COMMAND_TYPE_SEND_DTMF:
+               return parse_send_dtmf(command, iter);
+       case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION:
+               return parse_language_notification(command, iter);
+       case STK_COMMAND_TYPE_LAUNCH_BROWSER:
+               return parse_launch_browser(command, iter);
+       case STK_COMMAND_TYPE_OPEN_CHANNEL:
+               return parse_open_channel(command, iter);
+       case STK_COMMAND_TYPE_CLOSE_CHANNEL:
+               return parse_close_channel(command, iter);
+       case STK_COMMAND_TYPE_RECEIVE_DATA:
+               return parse_receive_data(command, iter);
+       case STK_COMMAND_TYPE_SEND_DATA:
+               return parse_send_data(command, iter);
+       case STK_COMMAND_TYPE_GET_CHANNEL_STATUS:
+               return parse_get_channel_status(command, iter);
+       case STK_COMMAND_TYPE_SERVICE_SEARCH:
+               return parse_service_search(command, iter);
+       case STK_COMMAND_TYPE_GET_SERVICE_INFO:
+               return parse_get_service_info(command, iter);
+       case STK_COMMAND_TYPE_DECLARE_SERVICE:
+               return parse_declare_service(command, iter);
+       case STK_COMMAND_TYPE_SET_FRAMES:
+               return parse_set_frames(command, iter);
+       case STK_COMMAND_TYPE_GET_FRAMES_STATUS:
+               return parse_get_frames_status(command, iter);
+       case STK_COMMAND_TYPE_RETRIEVE_MMS:
+               return parse_retrieve_mms(command, iter);
+       case STK_COMMAND_TYPE_SUBMIT_MMS:
+               return parse_submit_mms(command, iter);
+       case STK_COMMAND_TYPE_DISPLAY_MMS:
+               return parse_display_mms(command, iter);
+       case STK_COMMAND_TYPE_ACTIVATE:
+               return parse_activate(command, iter);
+       default:
+               return STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD;
+       };
+}
+
+struct stk_command *stk_command_new_from_pdu(const unsigned char *pdu,
+                                               unsigned int len)
+{
+       struct ber_tlv_iter ber;
+       struct comprehension_tlv_iter iter;
+       const unsigned char *data;
+       struct stk_command *command;
+
+       ber_tlv_iter_init(&ber, pdu, len);
+
+       if (ber_tlv_iter_next(&ber) != TRUE)
+               return NULL;
+
+       /* We should be wrapped in a Proactive UICC Command Tag 0xD0 */
+       if (ber_tlv_iter_get_short_tag(&ber) != 0xD0)
+               return NULL;
+
+       ber_tlv_iter_recurse_comprehension(&ber, &iter);
+
+       /*
+        * Now parse actual command details, they come in order with
+        * Command Details TLV first, followed by Device Identities TLV
+        */
+       if (comprehension_tlv_iter_next(&iter) != TRUE)
+               return NULL;
+
+       if (comprehension_tlv_iter_get_tag(&iter) !=
+                       STK_DATA_OBJECT_TYPE_COMMAND_DETAILS)
+               return NULL;
+
+       if (comprehension_tlv_iter_get_length(&iter) != 0x03)
+               return NULL;
+
+       data = comprehension_tlv_iter_get_data(&iter);
+
+       command = g_new0(struct stk_command, 1);
+
+       command->number = data[0];
+       command->type = data[1];
+       command->qualifier = data[2];
+
+       if (comprehension_tlv_iter_next(&iter) != TRUE) {
+               command->status = STK_PARSE_RESULT_MISSING_VALUE;
+               goto out;
+       }
+
+       if (comprehension_tlv_iter_get_tag(&iter) !=
+                       STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES) {
+               command->status = STK_PARSE_RESULT_MISSING_VALUE;
+               goto out;
+       }
+
+       if (comprehension_tlv_iter_get_length(&iter) != 0x02) {
+               command->status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
+               goto out;
+       }
+
+       data = comprehension_tlv_iter_get_data(&iter);
+
+       command->src = data[0];
+       command->dst = data[1];
+
+       command->status = parse_command_body(command, &iter);
+
+out:
+       return command;
+}
+
+void stk_command_free(struct stk_command *command)
+{
+       if (command->destructor)
+               command->destructor(command);
+
+       g_free(command);
+}
+
+static gboolean stk_tlv_builder_init(struct stk_tlv_builder *iter,
+                                               unsigned char *pdu,
+                                               unsigned int size)
+{
+       iter->value = NULL;
+       iter->len = 0;
+
+       return comprehension_tlv_builder_init(&iter->ctlv, pdu, size);
+}
+
+static gboolean stk_tlv_builder_recurse(struct stk_tlv_builder *iter,
+                                       struct ber_tlv_builder *btlv,
+                                       unsigned char tag)
+{
+       iter->value = NULL;
+       iter->len = 0;
+
+       if (ber_tlv_builder_next(btlv, tag >> 6, (tag >> 5) & 1,
+                                       tag & 0x1f) != TRUE)
+               return FALSE;
+
+       return ber_tlv_builder_recurse_comprehension(btlv, &iter->ctlv);
+}
+
+static gboolean stk_tlv_builder_open_container(struct stk_tlv_builder *iter,
+                                               gboolean cr,
+                                               unsigned char shorttag,
+                                               gboolean relocatable)
+{
+       if (comprehension_tlv_builder_next(&iter->ctlv, cr, shorttag) != TRUE)
+               return FALSE;
+
+       iter->len = 0;
+       iter->max_len = relocatable ? 0xff : 0x7f;
+       if (comprehension_tlv_builder_set_length(&iter->ctlv, iter->max_len) !=
+                       TRUE)
+               return FALSE;
+
+       iter->value = comprehension_tlv_builder_get_data(&iter->ctlv);
+
+       return TRUE;
+}
+
+static gboolean stk_tlv_builder_close_container(struct stk_tlv_builder *iter)
+{
+       return comprehension_tlv_builder_set_length(&iter->ctlv, iter->len);
+}
+
+static unsigned int stk_tlv_builder_get_length(struct stk_tlv_builder *iter)
+{
+       return comprehension_tlv_builder_get_data(&iter->ctlv) -
+               iter->ctlv.pdu + iter->len;
+}
+
+static gboolean stk_tlv_builder_append_byte(struct stk_tlv_builder *iter,
+                                               unsigned char num)
+{
+       if (iter->len >= iter->max_len)
+               return FALSE;
+
+       iter->value[iter->len++] = num;
+       return TRUE;
+}
+
+static gboolean stk_tlv_builder_append_short(struct stk_tlv_builder *iter,
+                                               unsigned short num)
+{
+       if (iter->len + 2 > iter->max_len)
+               return FALSE;
+
+       iter->value[iter->len++] = num >> 8;
+       iter->value[iter->len++] = num & 0xff;
+       return TRUE;
+}
+
+static gboolean stk_tlv_builder_append_gsm_packed(struct stk_tlv_builder *iter,
+                                                       const char *text)
+{
+       unsigned int len;
+       unsigned char *gsm;
+       long written = 0;
+
+       if (text == NULL)
+               return TRUE;
+
+       len = strlen(text);
+
+       gsm = convert_utf8_to_gsm(text, len, NULL, &written, 0);
+       if (gsm == NULL && len > 0)
+               return FALSE;
+
+       if (iter->len + (written * 7 + 7) / 8 >= iter->max_len) {
+               g_free(gsm);
+               return FALSE;
+       }
+
+       pack_7bit_own_buf(gsm, len, 0, FALSE, &written, 0,
+                               iter->value + iter->len + 1);
+       g_free(gsm);
+
+       if (written < 1 && len > 0)
+               return FALSE;
+
+       iter->value[iter->len++] = 0x00;
+       iter->len += written;
+
+       return TRUE;
+}
+
+static gboolean stk_tlv_builder_append_gsm_unpacked(
+                                               struct stk_tlv_builder *iter,
+                                               const char *text)
+{
+       unsigned int len;
+       unsigned char *gsm;
+       long written = 0;
+
+       if (text == NULL)
+               return TRUE;
+
+       len = strlen(text);
+
+       gsm = convert_utf8_to_gsm(text, len, NULL, &written, 0);
+       if (gsm == NULL && len > 0)
+               return FALSE;
+
+       if (iter->len + written >= iter->max_len) {
+               g_free(gsm);
+               return FALSE;
+       }
+
+       iter->value[iter->len++] = 0x04;
+       memcpy(iter->value + iter->len, gsm, written);
+       iter->len += written;
+
+       g_free(gsm);
+
+       return TRUE;
+}
+
+static gboolean stk_tlv_builder_append_ucs2(struct stk_tlv_builder *iter,
+                                               const char *text)
+{
+       unsigned char *ucs2;
+       gsize gwritten;
+
+       ucs2 = (unsigned char *) g_convert((const gchar *) text, -1,
+                                               "UCS-2BE", "UTF-8//TRANSLIT",
+                                               NULL, &gwritten, NULL);
+       if (ucs2 == NULL)
+               return FALSE;
+
+       if (iter->len + gwritten >= iter->max_len) {
+               g_free(ucs2);
+               return FALSE;
+       }
+
+       iter->value[iter->len++] = 0x08;
+
+       memcpy(iter->value + iter->len, ucs2, gwritten);
+       iter->len += gwritten;
+
+       g_free(ucs2);
+
+       return TRUE;
+}
+
+static gboolean stk_tlv_builder_append_text(struct stk_tlv_builder *iter,
+                                               int dcs, const char *text)
+{
+       gboolean ret;
+
+       switch (dcs) {
+       case 0x00:
+               return stk_tlv_builder_append_gsm_packed(iter, text);
+       case 0x04:
+               return stk_tlv_builder_append_gsm_unpacked(iter, text);
+       case 0x08:
+               return stk_tlv_builder_append_ucs2(iter, text);
+       case -1:
+               ret = stk_tlv_builder_append_gsm_unpacked(iter, text);
+
+               if (ret == TRUE)
+                       return ret;
+
+               return stk_tlv_builder_append_ucs2(iter, text);
+       }
+
+       return FALSE;
+}
+
+static inline gboolean stk_tlv_builder_append_bytes(struct stk_tlv_builder *iter,
+                                               const unsigned char *data,
+                                               unsigned int length)
+{
+       if (iter->len + length > iter->max_len)
+               return FALSE;
+
+       memcpy(iter->value + iter->len, data, length);
+       iter->len += length;
+
+       return TRUE;
+}
+
+/* Described in TS 102.223 Section 8.1 */
+static gboolean build_dataobj_address(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_address *addr = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_ADDRESS;
+       unsigned int len;
+       unsigned char number[128];
+
+       if (addr->number == NULL)
+               return TRUE;
+
+       len = (strlen(addr->number) + 1) / 2;
+       sim_encode_bcd_number(addr->number, number);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, addr->ton_npi) &&
+               stk_tlv_builder_append_bytes(tlv, number, len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.2 */
+static gboolean build_dataobj_alpha_id(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       unsigned char tag = STK_DATA_OBJECT_TYPE_ALPHA_ID;
+       int len;
+       unsigned char *string;
+
+       if (data == NULL)
+               return TRUE;
+
+       if (strlen(data) == 0)
+               return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+                       stk_tlv_builder_close_container(tlv);
+
+       string = utf8_to_sim_string(data, -1, &len);
+       if (string == NULL)
+               return FALSE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_bytes(tlv, string, len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.3 */
+static gboolean build_dataobj_subaddress(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_subaddress *sa = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_SUBADDRESS;
+
+       if (sa->has_subaddr == FALSE)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, sa->subaddr, sa->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.4 */
+static gboolean build_dataobj_ccp(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_ccp *ccp = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CCP;
+
+       if (ccp->len == 0)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, ccp->len) &&
+               stk_tlv_builder_append_bytes(tlv, ccp->ccp, ccp->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.5 */
+static gboolean build_dataobj_cbs_page(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct cbs *page = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CBS_PAGE;
+       unsigned char pdu[88];
+
+       if (cbs_encode(page, NULL, pdu) == FALSE)
+               return FALSE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_bytes(tlv, pdu, 88) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.6 */
+static gboolean build_dataobj_item_id(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const unsigned char *item_id = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_ITEM_ID;
+
+       if (*item_id == 0)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *item_id) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.8 */
+static gboolean build_dataobj_duration(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_duration *duration = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_DURATION;
+
+       if (duration->interval == 0x00)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, duration->unit) &&
+               stk_tlv_builder_append_byte(tlv, duration->interval) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.12 */
+static gboolean build_dataobj_result(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_result *result = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_RESULT;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE)
+               return FALSE;
+
+       if (stk_tlv_builder_append_byte(tlv, result->type) == FALSE)
+               return FALSE;
+
+       if (result->additional_len > 0)
+               if (stk_tlv_builder_append_bytes(tlv, result->additional,
+                                       result->additional_len) == FALSE)
+                       return FALSE;
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.13 */
+static gboolean build_dataobj_gsm_sms_tpdu(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct sms_deliver *msg = data;
+       struct sms sms;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU;
+       unsigned char tpdu[165];
+       int tpdu_len;
+
+       sms.type = SMS_TYPE_DELIVER;
+       memset(&sms.sc_addr, 0, sizeof(sms.sc_addr));
+       memcpy(&sms.deliver, msg, sizeof(sms.deliver));
+
+       if (sms_encode(&sms, NULL, &tpdu_len, tpdu) == FALSE)
+               return FALSE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_bytes(tlv, tpdu + 1, tpdu_len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.14 */
+static gboolean build_dataobj_ss_string(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_address *addr = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_SS_STRING;
+       unsigned int len;
+       unsigned char number[128];
+
+       if (addr->number == NULL)
+               return TRUE;
+
+       len = (strlen(addr->number) + 1) / 2;
+       sim_encode_bcd_number(addr->number, number);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, addr->ton_npi) &&
+               stk_tlv_builder_append_bytes(tlv, number, len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Defined in TS 102.223 Section 8.15 */
+static gboolean build_dataobj_text(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_answer_text *text = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_TEXT;
+       gboolean ret;
+
+       if (text->text == NULL && !text->yesno)
+               return TRUE;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE)
+               return FALSE;
+
+       if (text->yesno == TRUE) {
+               /*
+                * Section 6.8.5:
+                * When the terminal issues [...] command qualifier set
+                * to "Yes/No", it shall supply the value "01" when the
+                * answer is "positive" and the value '00' when the
+                * answer is "negative" in the text string data object.
+                */
+               if (stk_tlv_builder_append_byte(tlv, 0x04) != TRUE)
+                       return FALSE;
+
+               ret = stk_tlv_builder_append_byte(tlv,
+                                               text->text ? 0x01 : 0x00);
+       } else if (text->packed)
+               ret = stk_tlv_builder_append_gsm_packed(tlv, text->text);
+       else
+               ret = stk_tlv_builder_append_text(tlv, -1, text->text);
+
+       if (ret != TRUE)
+               return ret;
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Defined in TS 102.223 Section 8.15 - USSD specific case*/
+static gboolean build_dataobj_ussd_text(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_ussd_text *text = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_TEXT;
+
+       if (text->has_text == FALSE)
+               return TRUE;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE)
+               return FALSE;
+
+       if (text->len > 0) {
+               if (stk_tlv_builder_append_byte(tlv, text->dcs) != TRUE)
+                       return FALSE;
+
+               if (stk_tlv_builder_append_bytes(tlv, text->text,
+                                                       text->len) != TRUE)
+                       return FALSE;
+       }
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.17 */
+static gboolean build_dataobj_ussd_string(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_ussd_string *ussd = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_USSD_STRING;
+
+       if (ussd->string == NULL)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, ussd->dcs) &&
+               stk_tlv_builder_append_bytes(tlv, ussd->string, ussd->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.18 */
+static gboolean build_dataobj_file_list(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       GSList *l = (void *) data;
+       const struct stk_file *file;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_FILE_LIST;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE)
+               return FALSE;
+
+       if (stk_tlv_builder_append_byte(tlv, g_slist_length(l)) != TRUE)
+               return FALSE;
+
+       for (; l; l = l->next) {
+               file = l->data;
+
+               if (stk_tlv_builder_append_bytes(tlv, file->file,
+                                                       file->len) != TRUE)
+                       return FALSE;
+       }
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Shortcut for a single File element */
+static gboolean build_dataobj_file(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       GSList l = {
+               .data = (void *) data,
+               .next = NULL,
+       };
+
+       return build_dataobj_file_list(tlv, &l, cr);
+}
+
+/* Described in TS 102.223 Section 8.19 */
+static gboolean build_dataobj_location_info(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_location_info *li = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_LOCATION_INFO;
+       guint8 mccmnc[3];
+
+       if (li->mcc[0] == '\0')
+               return TRUE;
+
+       sim_encode_mcc_mnc(mccmnc, li->mcc, li->mnc);
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE)
+               return FALSE;
+
+       if (stk_tlv_builder_append_bytes(tlv, mccmnc, 3) == FALSE)
+               return FALSE;
+
+       if (stk_tlv_builder_append_short(tlv, li->lac_tac) == FALSE)
+               return FALSE;
+
+       if (li->has_ci && stk_tlv_builder_append_short(tlv, li->ci) == FALSE)
+               return FALSE;
+
+       if (li->has_ext_ci &&
+                       stk_tlv_builder_append_short(tlv, li->ext_ci) == FALSE)
+               return FALSE;
+
+       if (li->has_eutran_ci) {
+               if (stk_tlv_builder_append_short(tlv,
+                                       li->eutran_ci >> 12) == FALSE)
+                       return FALSE;
+
+               if (stk_tlv_builder_append_short(tlv,
+                                       (li->eutran_ci << 4) | 0xf) == FALSE)
+                       return FALSE;
+       }
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+static gboolean build_empty_dataobj_location_info(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       unsigned char tag = STK_DATA_OBJECT_TYPE_LOCATION_INFO;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/*
+ * Described in TS 102.223 Section 8.20
+ *
+ * See format note in parse_dataobj_imei.
+ */
+static gboolean build_dataobj_imei(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       char byte0[3];
+       const char *imei = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_IMEI;
+       unsigned char value[8];
+
+       if (imei == NULL)
+               return TRUE;
+
+       if (strlen(imei) != 15)
+               return FALSE;
+
+       byte0[0] = '*';
+       byte0[1] = imei[0];
+       byte0[2] = '\0';
+       sim_encode_bcd_number(byte0, value);
+       sim_encode_bcd_number(imei + 1, value + 1);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, value, 8) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.21 */
+static gboolean build_dataobj_help_request(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const ofono_bool_t *help = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_HELP_REQUEST;
+
+       if (*help != TRUE)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.22 */
+static gboolean build_dataobj_network_measurement_results(
+                                               struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_common_byte_array *nmr = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE)
+               return FALSE;
+
+       if (nmr->len > 0 && stk_tlv_builder_append_bytes(tlv,
+                                       nmr->array, nmr->len) == FALSE)
+               return FALSE;
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.25 */
+static gboolean build_dataobj_event_list(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_event_list *list = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_EVENT_LIST;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, list->list, list->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Shortcut for a single Event type */
+static gboolean build_dataobj_event_type(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_event_list list = {
+               .list = { *(enum stk_event_type *) data },
+               .len = 1,
+       };
+
+       return build_dataobj_event_list(tlv, &list, cr);
+}
+
+/* Described in TS 102.223 Section 8.26 */
+static gboolean build_dataobj_cause(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_cause *cause = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CAUSE;
+
+       if (cause->has_cause == FALSE)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, cause->cause, cause->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.27 */
+static gboolean build_dataobj_location_status(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const enum stk_service_state *state = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_LOCATION_STATUS;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *state) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.28 */
+static gboolean build_dataobj_transaction_ids(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_transaction_id *id = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_TRANSACTION_ID;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, id->list, id->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Shortcut for a single Transaction ID */
+static gboolean build_dataobj_transaction_id(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_transaction_id ids = {
+               .list = { *(uint8_t *) data },
+               .len = 1,
+       };
+
+       return build_dataobj_transaction_ids(tlv, &ids, cr);
+}
+
+/* Described in 3GPP 31.111 Section 8.29 */
+static gboolean build_dataobj_bcch_channel_list(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_bcch_channel_list *list = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST;
+       unsigned int i, bytes, pos, shift;
+       unsigned char value;
+
+       if (list->has_list == FALSE)
+               return TRUE;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE)
+               return FALSE;
+
+       bytes = (list->num * 10 + 7) / 8;
+       for (i = 0; i < bytes; i++) {
+               pos = (i * 8 + 7) / 10;
+               shift = pos * 10 + 10 - i * 8 - 8;
+
+               value = 0;
+               if (pos < list->num)
+                       value |= list->channels[pos] >> shift;
+               if (shift > 2)
+                       value |= list->channels[pos - 1] << (10 - shift);
+
+               if (stk_tlv_builder_append_byte(tlv, value) != TRUE)
+                       return FALSE;
+       }
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.30 */
+static gboolean build_dataobj_cc_requested_action(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_common_byte_array *action = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION;
+
+       if (action->array == NULL)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, action->array, action->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.33 */
+static gboolean build_dataobj_card_reader_status(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_reader_status *status = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CARD_READER_STATUS;
+       unsigned char byte;
+
+       byte = status->id |
+               (status->removable << 3) |
+               (status->present << 4) |
+               (status->id1_size << 5) |
+               (status->card_present << 6) |
+               (status->card_powered << 7);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, byte) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.37 */
+static gboolean build_dataobj_timer_id(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const unsigned char *id = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_TIMER_ID;
+
+       if (id[0] == 0)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, id[0]) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.38 */
+static gboolean build_dataobj_timer_value(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_timer_value *value = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_TIMER_VALUE;
+
+       if (value->has_value == FALSE)
+               return TRUE;
+
+#define TO_BCD(bin) ((((bin) / 10) & 0xf) | (((bin) % 10) << 4))
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, TO_BCD(value->hour)) &&
+               stk_tlv_builder_append_byte(tlv, TO_BCD(value->minute)) &&
+               stk_tlv_builder_append_byte(tlv, TO_BCD(value->second)) &&
+               stk_tlv_builder_close_container(tlv);
+#undef TO_BCD
+}
+
+/* Described in TS 102.223 Section 8.39 */
+static gboolean build_dataobj_datetime_timezone(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct sms_scts *scts = data;
+       unsigned char value[7];
+       int offset = 0;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE;
+
+       if (scts->month == 0 && scts->day == 0)
+               return TRUE;
+
+       if (sms_encode_scts(scts, value, &offset) != TRUE)
+               return FALSE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, value, 7) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.41 */
+static gboolean build_dataobj_at_response(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       unsigned char tag = STK_DATA_OBJECT_TYPE_AT_RESPONSE;
+       int len;
+
+       if (data == NULL)
+               return TRUE;
+
+       /*
+        * "If the AT Response string is longer than the maximum length
+        * capable of being transmitted to the UICC then the AT Response
+        * string shall be truncated to this length by the terminal."
+        */
+       len = strlen(data);
+       if (len > 240) /* Safe pick */
+               len = 240;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_bytes(tlv, data, len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.42 */
+static gboolean build_dataobj_bc_repeat(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR;
+       const struct stk_bc_repeat *bcr = data;
+
+       if (bcr->has_bc_repeat == FALSE)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_byte(tlv, bcr->value) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.45 */
+static gboolean build_dataobj_language(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       unsigned char tag = STK_DATA_OBJECT_TYPE_LANGUAGE;
+
+       if (data == NULL)
+               return TRUE;
+
+       /*
+        * Coded as two GSM 7-bit characters with eighth bit clear.  Since
+        * ISO 639-2 codes use only english alphabet letters, no conversion
+        * from UTF-8 to GSM is needed.
+        */
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, data, 2) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in 3GPP TS 31.111 Section 8.46 */
+static gboolean build_dataobj_timing_advance(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_timing_advance *tadv = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_TIMING_ADVANCE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, tadv->status) &&
+               stk_tlv_builder_append_byte(tlv, tadv->advance) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.51 */
+static gboolean build_dataobj_browser_termination_cause(
+                                               struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const enum stk_browser_termination_cause *cause = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *cause) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.52 */
+static gboolean build_dataobj_bearer_description(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_bearer_description *bd = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION;
+
+       if (bd->type != STK_BEARER_TYPE_GPRS_UTRAN)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, bd->type) &&
+               stk_tlv_builder_append_byte(tlv,
+                       bd->gprs.precedence) &&
+               stk_tlv_builder_append_byte(tlv,
+                       bd->gprs.delay) &&
+               stk_tlv_builder_append_byte(tlv,
+                       bd->gprs.reliability) &&
+               stk_tlv_builder_append_byte(tlv,
+                       bd->gprs.peak) &&
+               stk_tlv_builder_append_byte(tlv,
+                       bd->gprs.mean) &&
+               stk_tlv_builder_append_byte(tlv,
+                       bd->gprs.pdp_type) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.53 */
+static gboolean build_dataobj_channel_data(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_common_byte_array *cd = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CHANNEL_DATA;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_bytes(tlv, cd->array, cd->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.54 */
+static gboolean build_dataobj_channel_data_length(
+                                               struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const unsigned short *length = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, MIN(*length, 255)) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.55 */
+static gboolean build_dataobj_buffer_size(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const unsigned short *buf_size = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BUFFER_SIZE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_short(tlv, *buf_size) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.56 */
+static gboolean build_dataobj_channel_status(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_channel *channel = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_CHANNEL_STATUS;
+       unsigned char byte[2];
+
+       switch (channel->status) {
+       case STK_CHANNEL_PACKET_DATA_SERVICE_NOT_ACTIVATED:
+       case STK_CHANNEL_TCP_IN_CLOSED_STATE:
+               byte[0] = channel->id;
+               byte[1] = 0x00;
+               break;
+       case STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED:
+       case STK_CHANNEL_TCP_IN_ESTABLISHED_STATE:
+               byte[0] = channel->id | 0x80;
+               byte[1] = 0x00;
+               break;
+       case STK_CHANNEL_TCP_IN_LISTEN_STATE:
+               byte[0] = channel->id | 0x40;
+               byte[1] = 0x00;
+               break;
+       case STK_CHANNEL_LINK_DROPPED:
+               byte[0] = channel->id;
+               byte[1] = 0x05;
+               break;
+       }
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+                       stk_tlv_builder_append_bytes(tlv, byte, 2) &&
+                       stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.58 */
+static gboolean build_dataobj_other_address(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_other_address *addr = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_OTHER_ADDRESS;
+       gboolean ok = FALSE;
+
+       if (!addr->type)
+               return TRUE;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE)
+               return FALSE;
+
+       switch (addr->type) {
+       case STK_ADDRESS_AUTO:
+               ok = TRUE;
+               break;
+       case STK_ADDRESS_IPV4:
+               ok = stk_tlv_builder_append_byte(tlv, addr->type) &&
+                       stk_tlv_builder_append_bytes(tlv,
+                                       (const guint8 *) &addr->addr.ipv4, 4);
+               break;
+       case STK_ADDRESS_IPV6:
+               ok = stk_tlv_builder_append_byte(tlv, addr->type) &&
+                       stk_tlv_builder_append_bytes(tlv, addr->addr.ipv6, 16);
+               break;
+       }
+
+       if (!ok)
+               return FALSE;
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.59 */
+static gboolean build_dataobj_uicc_te_interface(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_uicc_te_interface *iface = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE;
+
+       if (iface->protocol == 0 && iface->port == 0)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, iface->protocol) &&
+               stk_tlv_builder_append_short(tlv, iface->port) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.61 */
+static gboolean build_dataobj_access_technologies(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_access_technologies *techs = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY;
+       int i;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) != TRUE)
+               return FALSE;
+
+       for (i = 0; i < techs->length; i++)
+               if (stk_tlv_builder_append_byte(tlv, techs->techs[i]) != TRUE)
+                       return FALSE;
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Shortcut for a single Access Technology */
+static gboolean build_dataobj_access_technology(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_access_technologies techs = {
+               .techs = data,
+               .length = 1,
+       };
+
+       return build_dataobj_access_technologies(tlv, &techs, cr);
+}
+
+/* Described in TS 102.223 Section 8.62 */
+static gboolean build_dataobj_display_parameters(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_display_parameters *params = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, params->height) &&
+               stk_tlv_builder_append_byte(tlv, params->width) &&
+               stk_tlv_builder_append_byte(tlv, params->effects) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.63 */
+static gboolean build_dataobj_service_record(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_service_record *rec = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_SERVICE_RECORD;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_byte(tlv, rec->tech_id) &&
+               stk_tlv_builder_append_byte(tlv, rec->serv_id) &&
+               stk_tlv_builder_append_bytes(tlv, rec->serv_rec, rec->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.68 */
+static gboolean build_dataobj_remote_entity_address(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_remote_entity_address *addr = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS;
+       gboolean ok = FALSE;
+
+       if (addr->has_address != TRUE)
+               return TRUE;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE)
+               return FALSE;
+
+       if (stk_tlv_builder_append_byte(tlv, addr->coding_type) != TRUE)
+               return FALSE;
+
+       switch (addr->coding_type) {
+       case 0x00:
+               ok = stk_tlv_builder_append_bytes(tlv, addr->addr.ieee802, 6);
+               break;
+       case 0x01:
+               ok = stk_tlv_builder_append_bytes(tlv, addr->addr.irda, 4);
+               break;
+       }
+
+       if (!ok)
+               return FALSE;
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.69 */
+static gboolean build_dataobj_esn(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const guint32 *esn = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_ESN;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_short(tlv, *esn >> 16) &&
+               stk_tlv_builder_append_short(tlv, *esn >> 0) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.72, 3GPP 24.008 Section 9.5.7 */
+static gboolean build_dataobj_pdp_context_params(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_common_byte_array *params = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER;
+
+       if (params->len < 1)
+               return TRUE;
+
+       if (params->len > 0x7f)
+               return FALSE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, params->array, params->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/*
+ * Described in TS 102.223 Section 8.74
+ *
+ * See format note in parse_dataobj_imeisv.
+ */
+static gboolean build_dataobj_imeisv(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       char byte0[3];
+       const char *imeisv = data;
+       unsigned char value[9];
+       unsigned char tag = STK_DATA_OBJECT_TYPE_IMEISV;
+
+       if (imeisv == NULL)
+               return TRUE;
+
+       if (strlen(imeisv) != 16)
+               return FALSE;
+
+       byte0[0] = '3';
+       byte0[1] = imeisv[0];
+       byte0[2] = '\0';
+       sim_encode_bcd_number(byte0, value);
+       sim_encode_bcd_number(imeisv + 1, value + 1);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, value, 9) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.75 */
+static gboolean build_dataobj_network_search_mode(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const enum stk_network_search_mode *mode = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *mode) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.76 */
+static gboolean build_dataobj_battery_state(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const enum stk_battery_state *state = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BATTERY_STATE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *state) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.77 */
+static gboolean build_dataobj_browsing_status(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_common_byte_array *bs = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BROWSING_STATUS;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_bytes(tlv, bs->array, bs->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.79 */
+static gboolean build_dataobj_frames_information(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_frames_info *info = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_FRAMES_INFO;
+       unsigned int i;
+
+       if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) != TRUE)
+               return FALSE;
+
+       if (stk_tlv_builder_append_byte(tlv, info->id) != TRUE)
+               return FALSE;
+
+       for (i = 0; i < info->len; i++) {
+               if (stk_tlv_builder_append_byte(tlv,
+                                               info->list[i].height) != TRUE)
+                       return FALSE;
+               if (stk_tlv_builder_append_byte(tlv,
+                                               info->list[i].width) != TRUE)
+                       return FALSE;
+       }
+
+       return stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.81 */
+static gboolean build_dataobj_meid(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const char *meid = data;
+       unsigned char value[8];
+       unsigned char tag = STK_DATA_OBJECT_TYPE_MEID;
+
+       if (meid == NULL)
+               return TRUE;
+
+       if (strlen(meid) != 16)
+               return FALSE;
+
+       sim_encode_bcd_number(meid, value);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, value, 8) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.83 */
+static gboolean build_dataobj_mms_id(struct stk_tlv_builder *tlv,
+                                       const void *data, gboolean cr)
+{
+       const struct stk_mms_id *id = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_MMS_ID;
+
+       /* Assume the length is never 0 for a valid ID, however the whole
+        * data object's presence is conditional.  */
+       if (id->len == 0)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, id->id, id->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.84 */
+static gboolean build_dataobj_mms_transfer_status(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_mms_transfer_status *mts = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS;
+
+       /*
+        * Assume the length is never 0 for a valid Result message, however
+        * the whole data object's presence is conditional.
+        */
+       if (mts->len == 0)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, mts->status, mts->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.84 */
+static gboolean build_dataobj_i_wlan_access_status(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const enum stk_i_wlan_access_status *status = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_I_WLAN_ACCESS_STATUS;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *status) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.86 */
+static gboolean build_dataobj_mms_notification(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_common_byte_array *msg = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_bytes(tlv, msg->array, msg->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.87 */
+static gboolean build_dataobj_last_envelope(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const ofono_bool_t *last = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_LAST_ENVELOPE;
+
+       if (!*last)
+               return TRUE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.88 */
+static gboolean build_dataobj_registry_application_data(
+                                               struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_registry_application_data *rad = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA;
+       guint8 dcs, *name;
+       gsize len;
+       long gsmlen;
+
+       name = convert_utf8_to_gsm(rad->name, -1, NULL, &gsmlen, 0);
+       len = gsmlen;
+       dcs = 0x04;
+       if (name == NULL) {
+               name = (guint8 *) g_convert((const gchar *) rad->name, -1,
+                                               "UCS-2BE", "UTF-8//TRANSLIT",
+                                               NULL, &len, NULL);
+               dcs = 0x08;
+
+               if (name == NULL)
+                       return FALSE;
+       }
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) &&
+               stk_tlv_builder_append_short(tlv, rad->port) &&
+               stk_tlv_builder_append_byte(tlv, dcs) &&
+               stk_tlv_builder_append_byte(tlv, rad->type) &&
+               stk_tlv_builder_append_bytes(tlv, name, len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 102.223 Section 8.90 */
+static gboolean build_dataobj_broadcast_network_information(
+                                               struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_broadcast_network_information *bni = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, bni->tech) &&
+               stk_tlv_builder_append_bytes(tlv, bni->loc_info, bni->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.91 / 3GPP 24.008 Section 10.5.5.15 */
+static gboolean build_dataobj_routing_area_id(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_routing_area_info *rai = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_ROUTING_AREA_INFO;
+       guint8 mccmnc[3];
+
+       if (rai->mcc[0] == 0)
+               return TRUE;
+
+       sim_encode_mcc_mnc(mccmnc, rai->mcc, rai->mnc);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, mccmnc, 3) &&
+               stk_tlv_builder_append_short(tlv, rai->lac) &&
+               stk_tlv_builder_append_byte(tlv, rai->rac) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.92 */
+static gboolean build_dataobj_update_attach_type(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const enum stk_update_attach_type *type = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_UPDATE_ATTACH_TYPE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *type) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.93 */
+static gboolean build_dataobj_rejection_cause_code(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const enum stk_rejection_cause_code *cause = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_REJECTION_CAUSE_CODE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, *cause) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.98, 3GPP 24.301 Section 6.5.1 */
+static gboolean build_dataobj_eps_pdn_conn_params(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_common_byte_array *params = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_EPS_PDN_CONN_ACTIVATION_REQ;
+
+       if (params->len < 1)
+               return TRUE;
+
+       if (params->len > 0x7f)
+               return FALSE;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, params->array, params->len) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+/* Described in TS 131.111 Section 8.99 / 3GPP 24.301 Section 9.9.3.32 */
+static gboolean build_dataobj_tracking_area_id(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_tracking_area_id *tai = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_TRACKING_AREA_ID;
+       guint8 mccmnc[3];
+
+       if (tai->mcc[0] == 0)
+               return TRUE;
+
+       sim_encode_mcc_mnc(mccmnc, tai->mcc, tai->mnc);
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_bytes(tlv, mccmnc, 3) &&
+               stk_tlv_builder_append_short(tlv, tai->tac) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+static gboolean build_dataobj(struct stk_tlv_builder *tlv,
+                               dataobj_writer builder_func, ...)
+{
+       va_list args;
+
+       va_start(args, builder_func);
+
+       while (builder_func) {
+               unsigned int flags = va_arg(args, enum stk_data_object_flag);
+               const void *data = va_arg(args, const void *);
+               gboolean cr = (flags & DATAOBJ_FLAG_CR) ? TRUE : FALSE;
+
+               if (builder_func(tlv, data, cr) != TRUE)
+                       return FALSE;
+
+               builder_func = va_arg(args, dataobj_writer);
+       }
+
+       va_end(args);
+
+       return TRUE;
+}
+
+static gboolean build_setup_call(struct stk_tlv_builder *builder,
+                                       const struct stk_response *response)
+{
+       if (response->set_up_call.modified_result.cc_modified)
+               return build_dataobj(builder,
+                               build_dataobj_cc_requested_action,
+                               DATAOBJ_FLAG_CR,
+                               &response->set_up_call.cc_requested_action,
+                               build_dataobj_result,
+                               DATAOBJ_FLAG_CR,
+                               &response->set_up_call.modified_result.result,
+                               NULL);
+       else
+               return build_dataobj(builder,
+                               build_dataobj_cc_requested_action,
+                               DATAOBJ_FLAG_CR,
+                               &response->set_up_call.cc_requested_action,
+                               NULL);
+}
+
+static gboolean build_local_info(struct stk_tlv_builder *builder,
+                                       const struct stk_response *response)
+{
+       const struct stk_response_local_info *info =
+               &response->provide_local_info;
+       int i;
+
+       switch (response->qualifier) {
+       case 0x00: /* Location Information according to current NAA */
+               return build_dataobj(builder,
+                                       build_dataobj_location_info,
+                                       DATAOBJ_FLAG_CR, &info->location,
+                                       NULL);
+
+       case 0x01: /* IMEI of the terminal */
+               return build_dataobj(builder,
+                                       build_dataobj_imei,
+                                       DATAOBJ_FLAG_CR, info->imei,
+                                       NULL);
+
+       case 0x02: /* Network Measurement results according to current NAA */
+               return build_dataobj(builder,
+                               build_dataobj_network_measurement_results,
+                               DATAOBJ_FLAG_CR, &info->nmr.nmr,
+                               build_dataobj_bcch_channel_list,
+                               DATAOBJ_FLAG_CR, &info->nmr.bcch_ch_list,
+                               NULL);
+
+       case 0x03: /* Date, time and time zone */
+               return build_dataobj(builder,
+                                       build_dataobj_datetime_timezone,
+                                       DATAOBJ_FLAG_CR, &info->datetime,
+                                       NULL);
+
+       case 0x04: /* Language setting */
+               return build_dataobj(builder,
+                                       build_dataobj_language,
+                                       DATAOBJ_FLAG_CR, info->language,
+                                       NULL);
+
+       case 0x05: /* Timing Advance */
+               return build_dataobj(builder,
+                                       build_dataobj_timing_advance,
+                                       DATAOBJ_FLAG_CR, &info->tadv,
+                                       NULL);
+
+       case 0x06: /* Access Technology (single access technology) */
+               return build_dataobj(builder,
+                                       build_dataobj_access_technology,
+                                       0, &info->access_technology,
+                                       NULL);
+
+       case 0x07: /* ESN of the terminal */
+               return build_dataobj(builder,
+                                       build_dataobj_esn,
+                                       DATAOBJ_FLAG_CR, &info->esn,
+                                       NULL);
+
+       case 0x08: /* IMEISV of the terminal */
+               return build_dataobj(builder,
+                                       build_dataobj_imeisv,
+                                       DATAOBJ_FLAG_CR, info->imeisv,
+                                       NULL);
+
+       case 0x09: /* Search Mode */
+               return build_dataobj(builder,
+                                       build_dataobj_network_search_mode,
+                                       0, &info->search_mode,
+                                       NULL);
+
+       case 0x0a: /* Charge State of the Battery */
+               return build_dataobj(builder,
+                                       build_dataobj_battery_state,
+                                       DATAOBJ_FLAG_CR, &info->battery_charge,
+                                       NULL);
+
+       case 0x0b: /* MEID of the terminal */
+               return build_dataobj(builder,
+                                       build_dataobj_meid,
+                                       0, info->meid,
+                                       NULL);
+
+       case 0x0d: /* Broadcast Network Information according to current tech */
+               return build_dataobj(builder,
+                               build_dataobj_broadcast_network_information,
+                               0, &info->broadcast_network_info,
+                               NULL);
+
+       case 0x0e: /* Multiple Access Technologies */
+               return build_dataobj(builder,
+                                       build_dataobj_access_technologies,
+                                       0, &info->access_technologies,
+                                       NULL);
+
+       case 0x0f: /* Location Information for multiple NAAs */
+               if (build_dataobj(builder,
+                                       build_dataobj_access_technologies,
+                                       0, &info->location_infos.access_techs,
+                                       NULL) != TRUE)
+                       return FALSE;
+
+               for (i = 0; i < info->location_infos.access_techs.length; i++) {
+                       dataobj_writer location = build_dataobj_location_info;
+                       /*
+                        * "If no location information is available for an
+                        * access technology, the respective data object
+                        * shall have length zero."
+                        */
+                       if (info->location_infos.locations[i].mcc[0] == '\0')
+                               location = build_empty_dataobj_location_info;
+
+                       if (build_dataobj(builder,
+                                       location,
+                                       0, &info->location_infos.locations[i],
+                                       NULL) != TRUE)
+                               return FALSE;
+               }
+
+               return TRUE;
+
+       case 0x10: /* Network Measurement results for multiple NAAs */
+               if (build_dataobj(builder,
+                                       build_dataobj_access_technologies,
+                                       0, &info->nmrs.access_techs,
+                                       NULL) != TRUE)
+                       return FALSE;
+
+               for (i = 0; i < info->nmrs.access_techs.length; i++)
+                       if (build_dataobj(builder,
+                               build_dataobj_network_measurement_results,
+                               0, &info->nmrs.nmrs[i].nmr,
+                               build_dataobj_bcch_channel_list,
+                               0, &info->nmrs.nmrs[i].bcch_ch_list,
+                               NULL) != TRUE)
+                               return FALSE;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean build_open_channel(struct stk_tlv_builder *builder,
+                                       const struct stk_response *response)
+{
+       const struct stk_response_open_channel *open_channel =
+               &response->open_channel;
+
+       /* insert channel identifier only in case of success */
+       if (response->result.type == STK_RESULT_TYPE_SUCCESS) {
+               if (build_dataobj(builder, build_dataobj_channel_status,
+                                               0, &open_channel->channel,
+                                               NULL) != TRUE)
+                       return FALSE;
+       }
+
+       return build_dataobj(builder,
+                               build_dataobj_bearer_description,
+                               0, &open_channel->bearer_desc,
+                               build_dataobj_buffer_size,
+                               0, &open_channel->buf_size,
+                               NULL);
+}
+
+static gboolean build_receive_data(struct stk_tlv_builder *builder,
+                                       const struct stk_response *response)
+{
+       const struct stk_response_receive_data *receive_data =
+               &response->receive_data;
+
+       if (response->result.type != STK_RESULT_TYPE_SUCCESS &&
+                       response->result.type != STK_RESULT_TYPE_MISSING_INFO)
+               return TRUE;
+
+       if (receive_data->rx_data.len) {
+               if (build_dataobj(builder, build_dataobj_channel_data,
+                                       DATAOBJ_FLAG_CR,
+                                       &response->receive_data.rx_data,
+                                       NULL) != TRUE)
+               return FALSE;
+       }
+
+       return build_dataobj(builder, build_dataobj_channel_data_length,
+                               DATAOBJ_FLAG_CR,
+                               &response->receive_data.rx_remaining,
+                               NULL);
+}
+
+static gboolean build_send_data(struct stk_tlv_builder *builder,
+                                       const struct stk_response *response)
+{
+       if (response->result.type != STK_RESULT_TYPE_SUCCESS)
+               return TRUE;
+
+       return build_dataobj(builder, build_dataobj_channel_data_length,
+                               DATAOBJ_FLAG_CR,
+                               &response->send_data.tx_avail,
+                               NULL);
+}
+
+const unsigned char *stk_pdu_from_response(const struct stk_response *response,
+                                               unsigned int *out_length)
+{
+       struct stk_tlv_builder builder;
+       gboolean ok = TRUE;
+       unsigned char tag;
+       static unsigned char pdu[512];
+
+       stk_tlv_builder_init(&builder, pdu, sizeof(pdu));
+
+       /*
+        * Encode command details, they come in order with
+        * Command Details TLV first, followed by Device Identities TLV
+        * and the Result TLV.  Comprehension required everywhere.
+        */
+       tag = STK_DATA_OBJECT_TYPE_COMMAND_DETAILS;
+       if (stk_tlv_builder_open_container(&builder, TRUE, tag, FALSE) == FALSE)
+               return NULL;
+
+       if (stk_tlv_builder_append_byte(&builder, response->number) == FALSE)
+               return NULL;
+
+       if (stk_tlv_builder_append_byte(&builder, response->type) == FALSE)
+               return NULL;
+
+       if (stk_tlv_builder_append_byte(&builder, response->qualifier) == FALSE)
+               return NULL;
+
+       if (stk_tlv_builder_close_container(&builder) == FALSE)
+               return NULL;
+
+       /*
+        * TS 102 223 section 6.8 states:
+        * "For all COMPREHENSION-TLV objects with Min = N, the terminal
+        * should set the CR flag to comprehension not required."
+        * All the data objects except "Command Details" and "Result" have
+        * Min = N.
+        *
+        * However comprehension required is set for many of the TLVs in
+        * TS 102 384 conformance tests so we set it per command and per
+        * data object type.
+        */
+       tag = STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES;
+       if (stk_tlv_builder_open_container(&builder, TRUE, tag, FALSE) == FALSE)
+               return NULL;
+
+       if (stk_tlv_builder_append_byte(&builder, response->src) == FALSE)
+               return NULL;
+
+       if (stk_tlv_builder_append_byte(&builder, response->dst) == FALSE)
+               return NULL;
+
+       if (stk_tlv_builder_close_container(&builder) == FALSE)
+               return NULL;
+
+       if (build_dataobj_result(&builder, &response->result, TRUE) != TRUE)
+               return NULL;
+
+       switch (response->type) {
+       case STK_COMMAND_TYPE_DISPLAY_TEXT:
+               break;
+       case STK_COMMAND_TYPE_GET_INKEY:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_text, DATAOBJ_FLAG_CR,
+                                       &response->get_inkey.text,
+                                       build_dataobj_duration, 0,
+                                       &response->get_inkey.duration,
+                                       NULL);
+               break;
+       case STK_COMMAND_TYPE_GET_INPUT:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_text, DATAOBJ_FLAG_CR,
+                                       &response->get_input.text,
+                                       NULL);
+               break;
+       case STK_COMMAND_TYPE_MORE_TIME:
+       case STK_COMMAND_TYPE_SEND_SMS:
+       case STK_COMMAND_TYPE_PLAY_TONE:
+               break;
+       case STK_COMMAND_TYPE_POLL_INTERVAL:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_duration, DATAOBJ_FLAG_CR,
+                                       &response->poll_interval.max_interval,
+                                       NULL);
+               break;
+       case STK_COMMAND_TYPE_REFRESH:
+       case STK_COMMAND_TYPE_SETUP_MENU:
+               break;
+       case STK_COMMAND_TYPE_SELECT_ITEM:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_item_id, DATAOBJ_FLAG_CR,
+                                       &response->select_item.item_id,
+                                       NULL);
+               break;
+       case STK_COMMAND_TYPE_SEND_SS:
+               break;
+       case STK_COMMAND_TYPE_SETUP_CALL:
+               ok = build_setup_call(&builder, response);
+               break;
+       case STK_COMMAND_TYPE_POLLING_OFF:
+               break;
+       case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO:
+               ok = build_local_info(&builder, response);
+               break;
+       case STK_COMMAND_TYPE_SETUP_EVENT_LIST:
+               break;
+       case STK_COMMAND_TYPE_TIMER_MANAGEMENT:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_timer_id,
+                                       DATAOBJ_FLAG_CR,
+                                       &response->timer_mgmt.id,
+                                       build_dataobj_timer_value,
+                                       DATAOBJ_FLAG_CR,
+                                       &response->timer_mgmt.value,
+                                       NULL);
+               break;
+       case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT:
+               break;
+       case STK_COMMAND_TYPE_RUN_AT_COMMAND:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_at_response,
+                                       DATAOBJ_FLAG_CR,
+                                       response->run_at_command.at_response,
+                                       NULL);
+               break;
+       case STK_COMMAND_TYPE_SEND_DTMF:
+       case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION:
+       case STK_COMMAND_TYPE_LAUNCH_BROWSER:
+       case STK_COMMAND_TYPE_CLOSE_CHANNEL:
+               break;
+       case STK_COMMAND_TYPE_SEND_USSD:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_ussd_text,
+                                       DATAOBJ_FLAG_CR,
+                                       &response->send_ussd.text,
+                                       NULL);
+               break;
+       case STK_COMMAND_TYPE_OPEN_CHANNEL:
+               ok = build_open_channel(&builder, response);
+               break;
+       case STK_COMMAND_TYPE_RECEIVE_DATA:
+               ok = build_receive_data(&builder, response);
+               break;
+       case STK_COMMAND_TYPE_SEND_DATA:
+               ok = build_send_data(&builder, response);
+               break;
+       case STK_COMMAND_TYPE_GET_CHANNEL_STATUS:
+               ok = build_dataobj(&builder,
+                                       build_dataobj_channel_status,
+                                       DATAOBJ_FLAG_CR,
+                                       &response->channel_status.channel,
+                                       NULL);
+               break;
+       default:
+               return NULL;
+       };
+
+       if (ok != TRUE)
+               return NULL;
+
+       if (out_length)
+               *out_length = stk_tlv_builder_get_length(&builder);
+
+       return pdu;
+}
+
+/* Described in TS 102.223 Section 8.7 */
+static gboolean build_envelope_dataobj_device_ids(struct stk_tlv_builder *tlv,
+                                               const void *data, gboolean cr)
+{
+       const struct stk_envelope *envelope = data;
+       unsigned char tag = STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES;
+
+       return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) &&
+               stk_tlv_builder_append_byte(tlv, envelope->src) &&
+               stk_tlv_builder_append_byte(tlv, envelope->dst) &&
+               stk_tlv_builder_close_container(tlv);
+}
+
+static gboolean build_envelope_call_control(
+                                       struct stk_tlv_builder *builder,
+                                       const struct stk_envelope *envelope)
+{
+       const struct stk_envelope_call_control *cc = &envelope->call_control;
+       gboolean ok = FALSE;
+
+       if (build_dataobj(builder, build_envelope_dataobj_device_ids,
+                               DATAOBJ_FLAG_CR, envelope, NULL) != TRUE)
+               return FALSE;
+
+       switch (cc->type) {
+       case STK_CC_TYPE_CALL_SETUP:
+               ok = build_dataobj(builder, build_dataobj_address,
+                                       DATAOBJ_FLAG_CR, &cc->address, NULL);
+               break;
+       case STK_CC_TYPE_SUPPLEMENTARY_SERVICE:
+               ok = build_dataobj(builder, build_dataobj_ss_string,
+                                       DATAOBJ_FLAG_CR, &cc->ss_string, NULL);
+               break;
+       case STK_CC_TYPE_USSD_OP:
+               ok = build_dataobj(builder, build_dataobj_ussd_string,
+                                       DATAOBJ_FLAG_CR, &cc->ussd_string,
+                                       NULL);
+               break;
+       case STK_CC_TYPE_PDP_CTX_ACTIVATION:
+               ok = build_dataobj(builder, build_dataobj_pdp_context_params,
+                                       DATAOBJ_FLAG_CR, &cc->pdp_ctx_params,
+                                       NULL);
+               break;
+       case STK_CC_TYPE_EPS_PDN_CONNECTION_ACTIVATION:
+               ok = build_dataobj(builder, build_dataobj_eps_pdn_conn_params,
+                                       DATAOBJ_FLAG_CR, &cc->eps_pdn_params,
+                                       NULL);
+               break;
+       }
+
+       if (ok != TRUE)
+               return FALSE;
+
+       return build_dataobj(builder,
+                               build_dataobj_ccp, 0, &cc->ccp1,
+                               build_dataobj_subaddress, 0, &cc->subaddress,
+                               build_dataobj_location_info, 0, &cc->location,
+                               build_dataobj_ccp, 0, &cc->ccp2,
+                               build_dataobj_alpha_id, 0, cc->alpha_id,
+                               build_dataobj_bc_repeat, 0, &cc->bc_repeat,
+                               NULL);
+}
+
+static gboolean build_envelope_event_download(struct stk_tlv_builder *builder,
+                                       const struct stk_envelope *envelope)
+{
+       const struct stk_envelope_event_download *evt =
+               &envelope->event_download;
+
+       if (build_dataobj(builder,
+                               build_dataobj_event_type, DATAOBJ_FLAG_CR,
+                               &evt->type,
+                               build_envelope_dataobj_device_ids,
+                               DATAOBJ_FLAG_CR,
+                               envelope,
+                               NULL) == FALSE)
+               return FALSE;
+
+       switch (evt->type) {
+       case STK_EVENT_TYPE_MT_CALL:
+               return build_dataobj(builder,
+                                       build_dataobj_transaction_id,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->mt_call.transaction_id,
+                                       build_dataobj_address, 0,
+                                       &evt->mt_call.caller_address,
+                                       build_dataobj_subaddress, 0,
+                                       &evt->mt_call.caller_subaddress,
+                                       NULL);
+       case STK_EVENT_TYPE_CALL_CONNECTED:
+               return build_dataobj(builder,
+                                       build_dataobj_transaction_id,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->call_connected.transaction_id,
+                                       NULL);
+       case STK_EVENT_TYPE_CALL_DISCONNECTED:
+               return build_dataobj(builder,
+                                       build_dataobj_transaction_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->call_disconnected.transaction_ids,
+                                       build_dataobj_cause, 0,
+                                       &evt->call_disconnected.cause,
+                                       NULL);
+       case STK_EVENT_TYPE_LOCATION_STATUS:
+               return build_dataobj(builder,
+                                       build_dataobj_location_status,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->location_status.state,
+                                       build_dataobj_location_info, 0,
+                                       &evt->location_status.info,
+                                       NULL);
+       case STK_EVENT_TYPE_USER_ACTIVITY:
+       case STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE:
+               return TRUE;
+       case STK_EVENT_TYPE_CARD_READER_STATUS:
+               return build_dataobj(builder,
+                                       build_dataobj_card_reader_status,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->card_reader_status,
+                                       NULL);
+       case STK_EVENT_TYPE_LANGUAGE_SELECTION:
+               return build_dataobj(builder,
+                                       build_dataobj_language, DATAOBJ_FLAG_CR,
+                                       evt->language_selection,
+                                       NULL);
+       case STK_EVENT_TYPE_BROWSER_TERMINATION:
+               return build_dataobj(builder,
+                                       build_dataobj_browser_termination_cause,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->browser_termination.cause,
+                                       NULL);
+       case STK_EVENT_TYPE_DATA_AVAILABLE:
+               return build_dataobj(builder,
+                                       build_dataobj_channel_status,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->data_available.channel,
+                                       build_dataobj_channel_data_length,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->data_available.channel_data_len,
+                                       NULL);
+       case STK_EVENT_TYPE_CHANNEL_STATUS:
+               return build_dataobj(builder,
+                                       build_dataobj_channel_status,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->channel_status.channel,
+                                       build_dataobj_bearer_description,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->channel_status.bearer_desc,
+                                       build_dataobj_other_address,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->channel_status.address,
+                                       NULL);
+       case STK_EVENT_TYPE_SINGLE_ACCESS_TECHNOLOGY_CHANGE:
+               return build_dataobj(builder,
+                                       build_dataobj_access_technology,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->access_technology_change,
+                                       NULL);
+       case STK_EVENT_TYPE_DISPLAY_PARAMETERS_CHANGED:
+               return build_dataobj(builder,
+                                       build_dataobj_display_parameters,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->display_params_changed,
+                                       NULL);
+       case STK_EVENT_TYPE_LOCAL_CONNECTION:
+               return build_dataobj(builder,
+                                       build_dataobj_service_record,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->local_connection.service_record,
+                                       build_dataobj_remote_entity_address, 0,
+                                       &evt->local_connection.remote_addr,
+                                       build_dataobj_uicc_te_interface, 0,
+                                       &evt->local_connection.transport_level,
+                                       build_dataobj_other_address,
+                                       0,
+                                       &evt->local_connection.transport_addr,
+                                       NULL);
+       case STK_EVENT_TYPE_NETWORK_SEARCH_MODE_CHANGE:
+               return build_dataobj(builder,
+                                       build_dataobj_network_search_mode,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->network_search_mode_change,
+                                       NULL);
+       case STK_EVENT_TYPE_BROWSING_STATUS:
+               return build_dataobj(builder,
+                                       build_dataobj_browsing_status,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->browsing_status,
+                                       NULL);
+       case STK_EVENT_TYPE_FRAMES_INFORMATION_CHANGE:
+               return build_dataobj(builder,
+                                       build_dataobj_frames_information,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->frames_information_change,
+                                       NULL);
+       case STK_EVENT_TYPE_I_WLAN_ACCESS_STATUS:
+               return build_dataobj(builder,
+                                       build_dataobj_i_wlan_access_status,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->i_wlan_access_status,
+                                       NULL);
+       case STK_EVENT_TYPE_NETWORK_REJECTION:
+               return build_dataobj(builder,
+                                       build_dataobj_location_info, 0,
+                                       &evt->network_rejection.location,
+                                       build_dataobj_routing_area_id, 0,
+                                       &evt->network_rejection.rai,
+                                       build_dataobj_tracking_area_id, 0,
+                                       &evt->network_rejection.tai,
+                                       build_dataobj_access_technology,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->network_rejection.access_tech,
+                                       build_dataobj_update_attach_type,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->network_rejection.update_attach,
+                                       build_dataobj_rejection_cause_code,
+                                       DATAOBJ_FLAG_CR,
+                                       &evt->network_rejection.cause,
+                                       NULL);
+       case STK_EVENT_TYPE_HCI_CONNECTIVITY_EVENT:
+               return TRUE;
+       default:
+               return FALSE;
+       }
+}
+
+static gboolean build_envelope_terminal_apps(struct stk_tlv_builder *builder,
+                                       const struct stk_envelope *envelope)
+{
+       const struct stk_envelope_terminal_apps *ta = &envelope->terminal_apps;
+       int i;
+
+       if (build_dataobj(builder,
+                               build_envelope_dataobj_device_ids,
+                               DATAOBJ_FLAG_CR, envelope, NULL) == FALSE)
+               return FALSE;
+
+       for (i = 0; i < ta->count; i++)
+               if (build_dataobj(builder,
+                                       build_dataobj_registry_application_data,
+                                       0, &ta->list[i], NULL) == FALSE)
+                       return FALSE;
+
+       return build_dataobj(builder,
+                               build_dataobj_last_envelope,
+                               0, &ta->last, NULL);
+}
+
+const unsigned char *stk_pdu_from_envelope(const struct stk_envelope *envelope,
+                                               unsigned int *out_length)
+{
+       struct ber_tlv_builder btlv;
+       struct stk_tlv_builder builder;
+       gboolean ok = TRUE;
+       static unsigned char buffer[512];
+       unsigned char *pdu;
+
+       if (ber_tlv_builder_init(&btlv, buffer, sizeof(buffer)) != TRUE)
+               return NULL;
+
+       if (stk_tlv_builder_recurse(&builder, &btlv, envelope->type) != TRUE)
+               return NULL;
+
+       switch (envelope->type) {
+       case STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD:
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       envelope,
+                                       build_dataobj_address, 0,
+                                       &envelope->sms_pp_download.address,
+                                       build_dataobj_gsm_sms_tpdu,
+                                       DATAOBJ_FLAG_CR,
+                                       &envelope->sms_pp_download.message,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD:
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       envelope,
+                                       build_dataobj_cbs_page,
+                                       DATAOBJ_FLAG_CR,
+                                       &envelope->cbs_pp_download.page,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_MENU_SELECTION:
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       envelope,
+                                       build_dataobj_item_id, DATAOBJ_FLAG_CR,
+                                       &envelope->menu_selection.item_id,
+                                       build_dataobj_help_request, 0,
+                                       &envelope->menu_selection.help_request,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_CALL_CONTROL:
+               ok = build_envelope_call_control(&builder, envelope);
+               break;
+       case STK_ENVELOPE_TYPE_MO_SMS_CONTROL:
+               /*
+                * Comprehension Required according to the specs but not
+                * enabled in conformance tests in 3GPP 31.124.
+                */
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids, 0,
+                                       envelope,
+                                       build_dataobj_address, 0,
+                                       &envelope->sms_mo_control.sc_address,
+                                       build_dataobj_address, 0,
+                                       &envelope->sms_mo_control.dest_address,
+                                       build_dataobj_location_info, 0,
+                                       &envelope->sms_mo_control.location,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_EVENT_DOWNLOAD:
+               ok = build_envelope_event_download(&builder, envelope);
+               break;
+       case STK_ENVELOPE_TYPE_TIMER_EXPIRATION:
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       envelope,
+                                       build_dataobj_timer_id,
+                                       DATAOBJ_FLAG_CR,
+                                       &envelope->timer_expiration.id,
+                                       build_dataobj_timer_value,
+                                       DATAOBJ_FLAG_CR,
+                                       &envelope->timer_expiration.value,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_USSD_DOWNLOAD:
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       envelope,
+                                       build_dataobj_ussd_string,
+                                       DATAOBJ_FLAG_CR,
+                                       &envelope->ussd_data_download.string,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_MMS_TRANSFER_STATUS:
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       envelope,
+                                       build_dataobj_file, DATAOBJ_FLAG_CR,
+                                       &envelope->mms_status.transfer_file,
+                                       build_dataobj_mms_id, 0,
+                                       &envelope->mms_status.id,
+                                       build_dataobj_mms_transfer_status, 0,
+                                       &envelope->mms_status.transfer_status,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_MMS_NOTIFICATION:
+               ok = build_dataobj(&builder,
+                                       build_envelope_dataobj_device_ids,
+                                       DATAOBJ_FLAG_CR,
+                                       envelope,
+                                       build_dataobj_mms_notification,
+                                       DATAOBJ_FLAG_CR,
+                                       &envelope->mms_notification.msg,
+                                       build_dataobj_last_envelope, 0,
+                                       &envelope->mms_notification.last,
+                                       NULL);
+               break;
+       case STK_ENVELOPE_TYPE_TERMINAL_APP:
+               ok = build_envelope_terminal_apps(&builder, envelope);
+               break;
+       default:
+               return NULL;
+       };
+
+       if (ok != TRUE)
+               return NULL;
+
+       ber_tlv_builder_optimize(&btlv, &pdu, out_length);
+
+       return pdu;
+}
+
+static const char *html_colors[] = {
+       "#000000", /* Black */
+       "#808080", /* Dark Grey */
+       "#C11B17", /* Dark Red */
+       "#FBB117", /* Dark Yellow */
+       "#347235", /* Dark Green */
+       "#307D7E", /* Dark Cyan */
+       "#0000A0", /* Dark Blue */
+       "#C031C7", /* Dark Magenta */
+       "#C0C0C0", /* Grey */
+       "#FFFFFF", /* White */
+       "#FF0000", /* Bright Red */
+       "#FFFF00", /* Bright Yellow */
+       "#00FF00", /* Bright Green */
+       "#00FFFF", /* Bright Cyan */
+       "#0000FF", /* Bright Blue */
+       "#FF00FF", /* Bright Magenta */
+};
+
+#define STK_TEXT_FORMAT_ALIGN_MASK 0x03
+#define STK_TEXT_FORMAT_FONT_MASK 0x0C
+#define STK_TEXT_FORMAT_STYLE_MASK 0xF0
+#define STK_DEFAULT_TEXT_ALIGNMENT 0x00
+#define STK_TEXT_FORMAT_INIT 0x9003
+
+/* Defined in ETSI 123 40 9.2.3.24.10.1.1 */
+enum stk_text_format_code {
+       STK_TEXT_FORMAT_LEFT_ALIGN = 0x00,
+       STK_TEXT_FORMAT_CENTER_ALIGN = 0x01,
+       STK_TEXT_FORMAT_RIGHT_ALIGN = 0x02,
+       STK_TEXT_FORMAT_NO_ALIGN = 0x03,
+       STK_TEXT_FORMAT_FONT_SIZE_LARGE = 0x04,
+       STK_TEXT_FORMAT_FONT_SIZE_SMALL = 0x08,
+       STK_TEXT_FORMAT_FONT_SIZE_RESERVED = 0x0c,
+       STK_TEXT_FORMAT_STYLE_BOLD = 0x10,
+       STK_TEXT_FORMAT_STYLE_ITALIC = 0x20,
+       STK_TEXT_FORMAT_STYLE_UNDERLINED = 0x40,
+       STK_TEXT_FORMAT_STYLE_STRIKETHROUGH = 0x80,
+};
+
+static void end_format(GString *string, guint16 attr)
+{
+       guint code = attr & 0xFF;
+       guint color = (attr >> 8) & 0xFF;
+
+       if ((code & ~STK_TEXT_FORMAT_ALIGN_MASK) || color)
+               g_string_append(string, "</span>");
+
+       if ((code & STK_TEXT_FORMAT_ALIGN_MASK) != STK_TEXT_FORMAT_NO_ALIGN)
+               g_string_append(string, "</div>");
+}
+
+static void start_format(GString *string, guint16 attr)
+{
+       guint8 code = attr & 0xFF;
+       guint8 color = (attr >> 8) & 0xFF;
+       guint8 align = code & STK_TEXT_FORMAT_ALIGN_MASK;
+       guint8 font = code & STK_TEXT_FORMAT_FONT_MASK;
+       guint8 style = code & STK_TEXT_FORMAT_STYLE_MASK;
+       int fg = color & 0x0f;
+       int bg = (color >> 4) & 0x0f;
+
+       /* align formatting applies to a block of text */
+       if (align != STK_TEXT_FORMAT_NO_ALIGN)
+               g_string_append(string, "<div style=\"");
+
+       switch (align) {
+       case STK_TEXT_FORMAT_RIGHT_ALIGN:
+               g_string_append(string, "text-align: right;\">");
+               break;
+       case STK_TEXT_FORMAT_CENTER_ALIGN:
+               g_string_append(string, "text-align: center;\">");
+               break;
+       case STK_TEXT_FORMAT_LEFT_ALIGN:
+               g_string_append(string, "text-align: left;\">");
+               break;
+       }
+
+       if ((font == 0) && (style == 0) && (color == 0))
+               return;
+
+       /* font, style, and color are inline */
+       g_string_append(string, "<span style=\"");
+
+       switch (font) {
+       case STK_TEXT_FORMAT_FONT_SIZE_LARGE:
+               g_string_append(string, "font-size: big;");
+               break;
+       case STK_TEXT_FORMAT_FONT_SIZE_SMALL:
+               g_string_append(string, "font-size: small;");
+               break;
+       }
+
+       if (style & STK_TEXT_FORMAT_STYLE_BOLD)
+               g_string_append(string, "font-weight: bold;");
+       if (style & STK_TEXT_FORMAT_STYLE_ITALIC)
+               g_string_append(string, "font-style: italic;");
+       if (style & STK_TEXT_FORMAT_STYLE_UNDERLINED)
+               g_string_append(string, "text-decoration: underline;");
+       if (style & STK_TEXT_FORMAT_STYLE_STRIKETHROUGH)
+               g_string_append(string, "text-decoration: line-through;");
+
+       /* add any color */
+       g_string_append_printf(string, "color: %s;", html_colors[fg]);
+       g_string_append_printf(string, "background-color: %s;",
+                                               html_colors[bg]);
+       g_string_append(string, "\">");
+}
+
+char *stk_text_to_html(const char *utf8,
+                               const unsigned short *attrs, int num_attrs)
+{
+       long text_len = g_utf8_strlen(utf8, -1);
+       GString *string = g_string_sized_new(strlen(utf8) + 1);
+       short *formats;
+       int pos, i, j;
+       guint16 start, end, len, attr, prev_attr;
+       guint8 code, color, align;
+       const char *text = utf8;
+       int attrs_len = num_attrs * 4;
+
+       formats = g_try_new0(gint16, (text_len + 1));
+       if (formats == NULL) {
+               g_string_free(string, TRUE);
+               return NULL;
+       }
+
+       /* we will need formatting at the position beyond the last char */
+       for (i = 0; i <= text_len; i++)
+               formats[i] = STK_TEXT_FORMAT_INIT;
+
+       for (i = 0; i < attrs_len; i += 4) {
+               start = attrs[i];
+               len = attrs[i + 1];
+               code = attrs[i + 2] & 0xFF;
+               color = attrs[i + 3] & 0xFF;
+
+               if (len == 0)
+                       end = text_len;
+               else
+                       end = start + len;
+
+               /* sanity check values */
+               if (start > end || end > text_len)
+                       continue;
+
+               /*
+                * if the alignment is the same as either the default
+                * or the last alignment used, don't set any alignment
+                * value.
+                */
+               if (start == 0)
+                       align = STK_TEXT_FORMAT_NO_ALIGN;
+               else {
+                       align = formats[start - 1] &
+                                       STK_TEXT_FORMAT_ALIGN_MASK;
+               }
+
+               if ((code & STK_TEXT_FORMAT_ALIGN_MASK) == align)
+                       code |= STK_TEXT_FORMAT_NO_ALIGN;
+
+               attr = code | (color << 8);
+
+               for (j = start; j < end; j++)
+                       formats[j] = attr;
+       }
+
+       prev_attr = STK_TEXT_FORMAT_INIT;
+
+       for (pos = 0; pos <= text_len; pos++) {
+               attr = formats[pos];
+               if (attr != prev_attr) {
+                       if (prev_attr != STK_TEXT_FORMAT_INIT)
+                               end_format(string, prev_attr);
+
+                       if (attr != STK_TEXT_FORMAT_INIT)
+                               start_format(string, attr);
+
+                       prev_attr = attr;
+               }
+
+               if (pos == text_len)
+                       break;
+
+               switch (g_utf8_get_char(text)) {
+               case '\n':
+                       g_string_append(string, "<br/>");
+                       break;
+               case '\r':
+               {
+                       char *next = g_utf8_next_char(text);
+                       gunichar c = g_utf8_get_char(next);
+
+                       g_string_append(string, "<br/>");
+
+                       if ((pos + 1 < text_len) && (c == '\n')) {
+                               text = g_utf8_next_char(text);
+                               pos++;
+                       }
+                       break;
+               }
+               case '<':
+                       g_string_append(string, "&lt;");
+                       break;
+               case '>':
+                       g_string_append(string, "&gt;");
+                       break;
+               case '&':
+                       g_string_append(string, "&amp;");
+                       break;
+               default:
+                       g_string_append_unichar(string, g_utf8_get_char(text));
+               }
+
+               text = g_utf8_next_char(text);
+       }
+
+       g_free(formats);
+
+       /* return characters from string. Caller must free char data */
+       return g_string_free(string, FALSE);
+}
+
+static const char chars_table[] = {
+       '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
+       'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+       'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
+       'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+       'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '+', '.' };
+
+char *stk_image_to_xpm(const unsigned char *img, unsigned int len,
+                       enum stk_img_scheme scheme, const unsigned char *clut,
+                       unsigned short clut_len)
+{
+       guint8 width, height;
+       unsigned int ncolors, nbits, entry, cpp;
+       unsigned int i, j;
+       int bit, k;
+       GString *xpm;
+       unsigned int pos = 0;
+       const char xpm_header[] = "/* XPM */\n";
+       const char declaration[] = "static char *xpm[] = {\n";
+       char c[3];
+
+       if (img == NULL)
+               return NULL;
+
+       /* sanity check length */
+       if (len < 3)
+               return NULL;
+
+       width = img[pos++];
+       height = img[pos++];
+
+       if (scheme == STK_IMG_SCHEME_BASIC) {
+               nbits = 1;
+               ncolors = 2;
+       } else {
+               /* sanity check length */
+               if ((pos + 4 > len) || (clut == NULL))
+                       return NULL;
+
+               nbits = img[pos++];
+               ncolors = img[pos++];
+
+               /* the value of zero should be interpreted as 256 */
+               if (ncolors == 0)
+                       ncolors = 256;
+
+               /* skip clut offset bytes */
+               pos += 2;
+
+               if ((ncolors * 3) > clut_len)
+                       return NULL;
+       }
+
+       if (pos + ((width * height + 7) / 8) > len)
+               return NULL;
+
+       /* determine the number of chars need to represent the pixel */
+       cpp = ncolors > 64 ? 2 : 1;
+
+       /*
+        * space needed:
+        *      header line
+        *      declaration and beginning of assignment line
+        *      values - max length of 19
+        *      colors - ncolors * (cpp + whitespace + deliminators + color)
+        *      pixels - width * height * cpp + height deliminators "",\n
+        *      end of assignment - 2 chars "};"
+        */
+       xpm = g_string_sized_new(strlen(xpm_header) + strlen(declaration) +
+                               19 + ((cpp + 14) * ncolors) +
+                               (width * height * cpp) + (4 * height) + 2);
+       if (xpm == NULL)
+               return NULL;
+
+       /* add header, declaration, values */
+       g_string_append(xpm, xpm_header);
+       g_string_append(xpm, declaration);
+       g_string_append_printf(xpm, "\"%d %d %d %d\",\n", width, height,
+                               ncolors, cpp);
+
+       /* create colors */
+       if (scheme == STK_IMG_SCHEME_BASIC) {
+               g_string_append(xpm, "\"0\tc #000000\",\n");
+               g_string_append(xpm, "\"1\tc #FFFFFF\",\n");
+       } else {
+               for (i = 0; i < ncolors; i++) {
+                       /* lookup char representation of this number */
+                       if (ncolors > 64) {
+                               c[0] = chars_table[i / 64];
+                               c[1] = chars_table[i % 64];
+                               c[2] = '\0';
+                       } else {
+                               c[0] = chars_table[i % 64];
+                               c[1] = '\0';
+                       }
+
+                       if ((i == (ncolors - 1)) &&
+                                       scheme == STK_IMG_SCHEME_TRANSPARENCY)
+                               g_string_append_printf(xpm,
+                                       "\"%s\tc None\",\n", c);
+                       else
+                               g_string_append_printf(xpm,
+                                       "\"%s\tc #%02hhX%02hhX%02hhX\",\n",
+                                       c, clut[0], clut[1], clut[2]);
+                       clut += 3;
+               }
+       }
+
+       /* height rows of width pixels */
+       k = 7;
+       for (i = 0; i < height; i++) {
+               g_string_append(xpm, "\"");
+               for (j = 0; j < width; j++) {
+                       entry = 0;
+                       for (bit = nbits - 1; bit >= 0; bit--) {
+                               entry |= (img[pos] >> k & 0x1) << bit;
+                               k--;
+
+                               /* see if we crossed a byte boundary */
+                               if (k < 0) {
+                                       k = 7;
+                                       pos++;
+                               }
+                       }
+
+                       /* lookup char representation of this number */
+                       if (ncolors > 64) {
+                               c[0] = chars_table[entry / 64];
+                               c[1] = chars_table[entry % 64];
+                               c[2] = '\0';
+                       } else {
+                               c[0] = chars_table[entry % 64];
+                               c[1] = '\0';
+                       }
+
+                       g_string_append_printf(xpm, "%s", c);
+               }
+
+               g_string_append(xpm, "\",\n");
+       }
+
+       g_string_append(xpm, "};");
+
+       /* Caller must free char data */
+       return g_string_free(xpm, FALSE);
+}
diff --git a/src/stkutil.h b/src/stkutil.h
new file mode 100644 (file)
index 0000000..fae3e53
--- /dev/null
@@ -0,0 +1,1786 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*
+ * TS 101.220, Section 7.2, Card Application Toolkit assigned templates,
+ * These are the same as 3GPP 11.14 Sections 13.1 and 13.2
+ */
+enum stk_envelope_type {
+       STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD =     0xD1,
+       STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD =     0xD2,
+       STK_ENVELOPE_TYPE_MENU_SELECTION =      0xD3,
+       STK_ENVELOPE_TYPE_CALL_CONTROL =        0xD4,
+       STK_ENVELOPE_TYPE_MO_SMS_CONTROL =      0xD5,
+       STK_ENVELOPE_TYPE_EVENT_DOWNLOAD =      0xD6,
+       STK_ENVELOPE_TYPE_TIMER_EXPIRATION =    0xD7,
+       STK_ENVELOPE_TYPE_USSD_DOWNLOAD =       0xD9,
+       STK_ENVELOPE_TYPE_MMS_TRANSFER_STATUS = 0xDA,
+       STK_ENVELOPE_TYPE_MMS_NOTIFICATION =    0xDB,
+       STK_ENVELOPE_TYPE_TERMINAL_APP =        0xDC,
+       STK_ENVELOPE_TYPE_GEOLOCATION_REPORT =  0xDD,
+};
+
+/* TS 102.223 Section 9.4 */
+enum stk_command_type {
+       STK_COMMAND_TYPE_REFRESH =                      0x01,
+       STK_COMMAND_TYPE_MORE_TIME =                    0x02,
+       STK_COMMAND_TYPE_POLL_INTERVAL =                0x03,
+       STK_COMMAND_TYPE_POLLING_OFF =                  0x04,
+       STK_COMMAND_TYPE_SETUP_EVENT_LIST =             0x05,
+       STK_COMMAND_TYPE_SETUP_CALL =                   0x10,
+       STK_COMMAND_TYPE_SEND_SS =                      0x11,
+       STK_COMMAND_TYPE_SEND_USSD =                    0x12,
+       STK_COMMAND_TYPE_SEND_SMS =                     0x13,
+       STK_COMMAND_TYPE_SEND_DTMF =                    0x14,
+       STK_COMMAND_TYPE_LAUNCH_BROWSER =               0x15,
+       STK_COMMAND_TYPE_GEOGRAPICAL_LOCATION_REQUEST = 0x16,
+       STK_COMMAND_TYPE_PLAY_TONE =                    0x20,
+       STK_COMMAND_TYPE_DISPLAY_TEXT =                 0x21,
+       STK_COMMAND_TYPE_GET_INKEY =                    0x22,
+       STK_COMMAND_TYPE_GET_INPUT =                    0x23,
+       STK_COMMAND_TYPE_SELECT_ITEM =                  0x24,
+       STK_COMMAND_TYPE_SETUP_MENU =                   0x25,
+       STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO =           0x26,
+       STK_COMMAND_TYPE_TIMER_MANAGEMENT =             0x27,
+       STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT =         0x28,
+       STK_COMMAND_TYPE_PERFORM_CARD_APDU =            0x30,
+       STK_COMMAND_TYPE_POWER_ON_CARD =                0x31,
+       STK_COMMAND_TYPE_POWER_OFF_CARD =               0x32,
+       STK_COMMAND_TYPE_GET_READER_STATUS =            0x33,
+       STK_COMMAND_TYPE_RUN_AT_COMMAND =               0x34,
+       STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION =        0x35,
+       STK_COMMAND_TYPE_OPEN_CHANNEL =                 0x40,
+       STK_COMMAND_TYPE_CLOSE_CHANNEL =                0x41,
+       STK_COMMAND_TYPE_RECEIVE_DATA =                 0x42,
+       STK_COMMAND_TYPE_SEND_DATA =                    0x43,
+       STK_COMMAND_TYPE_GET_CHANNEL_STATUS =           0x44,
+       STK_COMMAND_TYPE_SERVICE_SEARCH =               0x45,
+       STK_COMMAND_TYPE_GET_SERVICE_INFO =             0x46,
+       STK_COMMAND_TYPE_DECLARE_SERVICE =              0x47,
+       STK_COMMAND_TYPE_SET_FRAMES =                   0x50,
+       STK_COMMAND_TYPE_GET_FRAMES_STATUS =            0x51,
+       STK_COMMAND_TYPE_RETRIEVE_MMS =                 0x60,
+       STK_COMMAND_TYPE_SUBMIT_MMS =                   0x61,
+       STK_COMMAND_TYPE_DISPLAY_MMS =                  0x62,
+       STK_COMMAND_TYPE_ACTIVATE =                     0x70,
+       STK_COMMAND_TYPE_END_SESSION =                  0x81,
+};
+
+enum stk_data_object_type {
+       STK_DATA_OBJECT_TYPE_INVALID =                          0x00,
+       STK_DATA_OBJECT_TYPE_COMMAND_DETAILS =                  0x01,
+       STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES =                0x02,
+       STK_DATA_OBJECT_TYPE_RESULT =                           0x03,
+       STK_DATA_OBJECT_TYPE_DURATION =                         0x04,
+       STK_DATA_OBJECT_TYPE_ALPHA_ID =                         0x05,
+       STK_DATA_OBJECT_TYPE_ADDRESS =                          0x06,
+       STK_DATA_OBJECT_TYPE_CCP =                              0x07,
+       STK_DATA_OBJECT_TYPE_SUBADDRESS =                       0x08,
+       STK_DATA_OBJECT_TYPE_SS_STRING =                        0x09,
+       STK_DATA_OBJECT_TYPE_USSD_STRING =                      0x0A,
+       STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU =                     0x0B,
+       STK_DATA_OBJECT_TYPE_CBS_PAGE =                         0x0C,
+       STK_DATA_OBJECT_TYPE_TEXT =                             0x0D,
+       STK_DATA_OBJECT_TYPE_TONE =                             0x0E,
+       STK_DATA_OBJECT_TYPE_ITEM =                             0x0F,
+       STK_DATA_OBJECT_TYPE_ITEM_ID =                          0x10,
+       STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH =                  0x11,
+       STK_DATA_OBJECT_TYPE_FILE_LIST =                        0x12,
+       STK_DATA_OBJECT_TYPE_LOCATION_INFO =                    0x13,
+       STK_DATA_OBJECT_TYPE_IMEI =                             0x14,
+       STK_DATA_OBJECT_TYPE_HELP_REQUEST =                     0x15,
+       STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS =      0x16,
+       STK_DATA_OBJECT_TYPE_DEFAULT_TEXT =                     0x17,
+       STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR =      0x18,
+       STK_DATA_OBJECT_TYPE_EVENT_LIST =                       0x19,
+       STK_DATA_OBJECT_TYPE_CAUSE =                            0x1A,
+       STK_DATA_OBJECT_TYPE_LOCATION_STATUS =                  0x1B,
+       STK_DATA_OBJECT_TYPE_TRANSACTION_ID =                   0x1C,
+       STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST =                0x1D,
+       STK_DATA_OBJECT_TYPE_ICON_ID =                          0x1E,
+       STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST =                0x1F,
+       STK_DATA_OBJECT_TYPE_CARD_READER_STATUS =               0x20,
+       STK_DATA_OBJECT_TYPE_CARD_ATR =                         0x21,
+       STK_DATA_OBJECT_TYPE_C_APDU =                           0x22,
+       STK_DATA_OBJECT_TYPE_R_APDU =                           0x23,
+       STK_DATA_OBJECT_TYPE_TIMER_ID =                         0x24,
+       STK_DATA_OBJECT_TYPE_TIMER_VALUE =                      0x25,
+       STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE =                0x26,
+       STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION =    0x27,
+       STK_DATA_OBJECT_TYPE_AT_COMMAND =                       0x28,
+       STK_DATA_OBJECT_TYPE_AT_RESPONSE =                      0x29,
+       STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR =              0x2A,
+       STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE =               0x2B,
+       STK_DATA_OBJECT_TYPE_DTMF_STRING =                      0x2C,
+       STK_DATA_OBJECT_TYPE_LANGUAGE =                         0x2D,
+       STK_DATA_OBJECT_TYPE_TIMING_ADVANCE =                   0x2E,
+       STK_DATA_OBJECT_TYPE_AID =                              0x2F,
+       STK_DATA_OBJECT_TYPE_BROWSER_ID =                       0x30,
+       STK_DATA_OBJECT_TYPE_URL =                              0x31,
+       STK_DATA_OBJECT_TYPE_BEARER =                           0x32,
+       STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF =            0x33,
+       STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE =        0x34,
+       STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION =               0x35,
+       STK_DATA_OBJECT_TYPE_CHANNEL_DATA =                     0x36,
+       STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH =              0x37,
+       STK_DATA_OBJECT_TYPE_CHANNEL_STATUS =                   0x38,
+       STK_DATA_OBJECT_TYPE_BUFFER_SIZE =                      0x39,
+       STK_DATA_OBJECT_TYPE_CARD_READER_ID =                   0x3A,
+       STK_DATA_OBJECT_TYPE_FILE_UPDATE_INFO =                 0x3B,
+       STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE =                0x3C,
+       STK_DATA_OBJECT_TYPE_OTHER_ADDRESS =                    0x3E,
+       STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY =                0x3F,
+       STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS =               0x40,
+       STK_DATA_OBJECT_TYPE_SERVICE_RECORD =                   0x41,
+       STK_DATA_OBJECT_TYPE_DEVICE_FILTER =                    0x42,
+       STK_DATA_OBJECT_TYPE_SERVICE_SEARCH =                   0x43,
+       STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO =                   0x44,
+       STK_DATA_OBJECT_TYPE_SERVICE_AVAILABILITY =             0x45,
+       STK_DATA_OBJECT_TYPE_ESN =                              0x46,
+       STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME =              0x47,
+       STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU =                    0x48,
+       STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS =            0x49,
+       STK_DATA_OBJECT_TYPE_I_WLAN_ID_TAG =                    0x4A,
+       STK_DATA_OBJECT_TYPE_I_WLAN_ACCESS_STATUS =             0x4B,
+       STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE =                   0x50,
+       STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST =         0x51,
+       STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER =         0x52,
+       STK_DATA_OBJECT_TYPE_IMEISV =                           0x62,
+       STK_DATA_OBJECT_TYPE_BATTERY_STATE =                    0x63,
+       STK_DATA_OBJECT_TYPE_BROWSING_STATUS =                  0x64,
+       STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE =              0x65,
+       STK_DATA_OBJECT_TYPE_FRAME_LAYOUT =                     0x66,
+       STK_DATA_OBJECT_TYPE_FRAMES_INFO =                      0x67,
+       STK_DATA_OBJECT_TYPE_FRAME_ID =                         0x68,
+       STK_DATA_OBJECT_TYPE_UTRAN_MEASUREMENT_QUALIFIER =      0x69,
+       STK_DATA_OBJECT_TYPE_MMS_REFERENCE =                    0x6A,
+       STK_DATA_OBJECT_TYPE_MMS_ID =                           0x6B,
+       STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS =              0x6C,
+       STK_DATA_OBJECT_TYPE_MEID =                             0x6D,
+       STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID =                   0x6E,
+       STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION =                 0x6F,
+       STK_DATA_OBJECT_TYPE_LAST_ENVELOPE =                    0x70,
+       STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA =        0x71,
+       STK_DATA_OBJECT_TYPE_ROUTING_AREA_INFO =                0x73,
+       STK_DATA_OBJECT_TYPE_UPDATE_ATTACH_TYPE =               0x74,
+       STK_DATA_OBJECT_TYPE_REJECTION_CAUSE_CODE =             0x75,
+       STK_DATA_OBJECT_TYPE_NMEA_SENTENCE =                    0x78,
+       STK_DATA_OBJECT_TYPE_PLMN_LIST =                        0x79,
+       STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO =           0x7A,
+       STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR =              0x7B,
+       STK_DATA_OBJECT_TYPE_EPS_PDN_CONN_ACTIVATION_REQ =      0x7C,
+       STK_DATA_OBJECT_TYPE_TRACKING_AREA_ID =                 0x7D,
+};
+
+enum stk_device_identity_type {
+       STK_DEVICE_IDENTITY_TYPE_KEYPAD =               0x01,
+       STK_DEVICE_IDENTITY_TYPE_DISPLAY =              0x02,
+       STK_DEVICE_IDENTITY_TYPE_EARPIECE =             0x03,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_0 =        0x10,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_1 =        0x11,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_2 =        0x12,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_3 =        0x13,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_4 =        0x14,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_5 =        0x15,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_6 =        0x16,
+       STK_DEVICE_IDENTITY_TYPE_CARD_READER_7 =        0x17,
+       STK_DEVICE_IDENTITY_TYPE_CHANNEL_1 =            0x21,
+       STK_DEVICE_IDENTITY_TYPE_CHANNEL_7 =            0x27,
+       STK_DEVICE_IDENTITY_TYPE_UICC =                 0x81,
+       STK_DEVICE_IDENTITY_TYPE_TERMINAL =             0x82,
+       STK_DEVICE_IDENTITY_TYPE_NETWORK =              0x83,
+};
+
+enum stk_qualifier_get_reader_status_type {
+       STK_QUALIFIER_TYPE_CARD_READER_STATUS =         0x00,
+       STK_QUALIFIER_TYPE_CARD_READER_ID =             0x01,
+};
+
+enum stk_duration_type {
+       STK_DURATION_TYPE_MINUTES =             0x00,
+       STK_DURATION_TYPE_SECONDS =             0x01,
+       STK_DURATION_TYPE_SECOND_TENTHS =       0x02,
+};
+
+/* Defined according to TS 102.223 Section 8.12 */
+enum stk_result_type {
+       /* 0x00 to 0x1F are used to indicate that command has been performed */
+       STK_RESULT_TYPE_SUCCESS =                       0x00,
+       STK_RESULT_TYPE_PARTIAL =                       0x01,
+       STK_RESULT_TYPE_MISSING_INFO =                  0x02,
+       STK_RESULT_TYPE_REFRESH_WITH_EFS =              0x03,
+       STK_RESULT_TYPE_NO_ICON =                       0x04,
+       STK_RESULT_TYPE_CALL_CONTROL =                  0x05,
+       STK_RESULT_TYPE_NO_SERVICE =                    0x06,
+       STK_RESULT_TYPE_MODIFED =                       0x07,
+       STK_RESULT_TYPE_REFRES_NO_NAA =                 0x08,
+       STK_RESULT_TYPE_NO_TONE =                       0x09,
+       STK_RESULT_TYPE_USER_TERMINATED =               0x10,
+       STK_RESULT_TYPE_GO_BACK =                       0x11,
+       STK_RESULT_TYPE_NO_RESPONSE =                   0x12,
+       STK_RESULT_TYPE_HELP_REQUESTED =                0x13,
+       STK_RESULT_TYPE_USSD_OR_SS_USER_TERMINATION =   0x14,
+
+       /* 0x20 to 0x2F are used to indicate that SIM should retry */
+       STK_RESULT_TYPE_TERMINAL_BUSY =                 0x20,
+       STK_RESULT_TYPE_NETWORK_UNAVAILABLE =           0x21,
+       STK_RESULT_TYPE_USER_REJECT =                   0x22,
+       STK_RESULT_TYPE_USER_CANCEL =                   0x23,
+       STK_RESULT_TYPE_TIMER_CONFLICT =                0x24,
+       STK_RESULT_TYPE_CALL_CONTROL_TEMPORARY =        0x25,
+       STK_RESULT_TYPE_BROWSER_TEMPORARY =             0x26,
+       STK_RESULT_TYPE_MMS_TEMPORARY =                 0x27,
+
+       /* 0x30 to 0x3F are used to indicate permanent problems */
+       STK_RESULT_TYPE_NOT_CAPABLE =                   0x30,
+       STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD =        0x31,
+       STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD =           0x32,
+       STK_RESULT_TYPE_COMMAND_ID_UNKNOWN =            0x33,
+       STK_RESULT_TYPE_SS_RETURN_ERROR =               0x34,
+       STK_RESULT_TYPE_SMS_RP_ERROR =                  0x35,
+       STK_RESULT_TYPE_MINIMUM_NOT_MET =               0x36,
+       STK_RESULT_TYPE_USSD_RETURN_ERROR =             0x37,
+       STK_RESULT_TYPE_CALL_CONTROL_PERMANENT =        0x39,
+       STK_RESULT_TYPE_BIP_ERROR =                     0x3A,
+       STK_RESULT_TYPE_ACCESS_TECHNOLOGY_ERROR =       0x3B,
+       STK_RESULT_TYPE_FRAMES_ERROR =                  0x3C,
+       STK_RESULT_TYPE_MMS_ERROR =                     0x3D,
+};
+
+/* Defined according to TS 102.223 Section 8.12.2 */
+enum stk_result_addnl_me_pb {
+       STK_RESULT_ADDNL_ME_PB_NO_SPECIFIC_CAUSE =      0x00,
+       STK_RESULT_ADDNL_ME_PB_SCREEN_BUSY =            0x01,
+       STK_RESULT_ADDNL_ME_PB_BUSY_ON_CALL =           0x02,
+       STK_RESULT_ADDNL_ME_PB_SS_BUSY =                0x03,
+       STK_RESULT_ADDNL_ME_PB_NO_SERVICE =             0x04,
+       STK_RESULT_ADDNL_ME_PB_NO_ACCESS =              0x05,
+       STK_RESULT_ADDNL_ME_PB_NO_RADIO_RESOURCE =      0x06,
+       STK_RESULT_ADDNL_ME_PB_NOT_IN_SPEECH_CALL =     0x07,
+       STK_RESULT_ADDNL_ME_PB_USSD_BUSY =              0x08,
+       STK_RESULT_ADDNL_ME_PB_BUSY_ON_SEND_DTMF =      0x09,
+       STK_RESULT_ADDNL_ME_PB_NO_NAA_ACTIVE =          0x0A
+};
+
+/* Defined according to TS 31.111 Section 8.12.4 */
+enum stk_result_addnl_ss_pb {
+       STK_RESULT_ADDNL_SS_PB_NO_SPECIFIC_CAUSE =      0x00
+};
+
+/* Defined according to TS 31.111 Section 8.12.4 */
+enum stk_result_addnl_bip_pb {
+       STK_RESULT_ADDNL_BIP_PB_NO_SPECIFIC_CAUSE =             0x00,
+       STK_RESULT_ADDNL_BIP_PB_NO_CHANNEL_AVAIL =              0x01,
+       STK_RESULT_ADDNL_BIP_PB_CHANNEL_CLOSED =                0x02,
+       STK_RESULT_ADDNL_BIP_PB_CHANNEL_ID_NOT_VALID =          0x03,
+       STK_RESULT_ADDNL_BIP_PB_BUFFER_SIZE_NOT_AVAIL =         0x04,
+       STK_RESULT_ADDNL_BIP_PB_SECURITY_ERROR =                0x05,
+       STK_RESULT_ADDNL_BIP_PB_INTERFACE_NOT_AVAIL =           0x06,
+       STK_RESULT_ADDNL_BIP_PB_DEVICE_NOT_REACHABLE =          0x07,
+       STK_RESULT_ADDNL_BIP_PB_SERVICE_ERROR =                 0x08,
+       STK_RESULT_ADDNL_BIP_PB_SERVICE_ID_UNKNOWN =            0x09,
+       STK_RESULT_ADDNL_BIP_PB_PORT_NOT_AVAIL =                0x10,
+       STK_RESULT_ADDNL_BIP_PB_LAUNCH_PARAMETERS_MISSING =     0x11,
+       STK_RESULT_ADDNL_BIP_PB_APPLICATION_LAUNCH_FAILED =     0x12
+};
+
+enum stk_tone_type {
+       STK_TONE_TYPE_DIAL_TONE =       0x01,
+       STK_TONE_TYPE_BUSY_TONE =       0x02,
+       STK_TONE_TYPE_CONGESTION =      0x03,
+       STK_TONE_TYPE_RP_ACK =          0x04,
+       STK_TONE_TYPE_CALL_DROPPED =    0x05,
+       STK_TONE_TYPE_ERROR =           0x06,
+       STK_TONE_TYPE_CALL_WAITING =    0x07,
+       STK_TONE_TYPE_RINGING =         0x08,
+       STK_TONE_TYPE_GENERAL_BEEP =    0x10,
+       STK_TONE_TYPE_POSITIVE_ACK =    0x11,
+       STK_TONE_TYPE_NEGATIVE_ACK =    0x12,
+       STK_TONE_TYPE_INCOMING_CALL =   0x13,
+       STK_TONE_TYPE_INCOMING_SMS =    0x14,
+       STK_TONE_TYPE_CRITICAL_ALERT =  0x15,
+       STK_TONE_TYPE_VIBRATE =         0x20,
+       STK_TONE_TYPE_HAPPY_TONE =      0x31,
+       STK_TONE_TYPE_SAD_TONE =        0x32,
+       STK_TONE_TYPE_URGENT_TONE =     0x33,
+       STK_TONE_TYPE_QUESTION_TONE =   0x34,
+       STK_TONE_TYPE_MESSAGE_TONE =    0x35,
+       STK_TONE_TYPE_MELODY_1 =        0x40,
+       STK_TONE_TYPE_MELODY_2 =        0x41,
+       STK_TONE_TYPE_MELODY_3 =        0x42,
+       STK_TONE_TYPE_MELODY_4 =        0x43,
+       STK_TONE_TYPE_MELODY_5 =        0x44,
+       STK_TONE_TYPE_MELODY_6 =        0x45,
+       STK_TONE_TYPE_MELODY_7 =        0x46,
+       STK_TONE_TYPE_MELODY_8 =        0x47
+};
+
+enum stk_event_type {
+       STK_EVENT_TYPE_MT_CALL =                                0x00,
+       STK_EVENT_TYPE_CALL_CONNECTED =                         0x01,
+       STK_EVENT_TYPE_CALL_DISCONNECTED =                      0x02,
+       STK_EVENT_TYPE_LOCATION_STATUS =                        0x03,
+       STK_EVENT_TYPE_USER_ACTIVITY =                          0x04,
+       STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE =                  0x05,
+       STK_EVENT_TYPE_CARD_READER_STATUS =                     0x06,
+       STK_EVENT_TYPE_LANGUAGE_SELECTION =                     0x07,
+       STK_EVENT_TYPE_BROWSER_TERMINATION =                    0x08,
+       STK_EVENT_TYPE_DATA_AVAILABLE =                         0x09,
+       STK_EVENT_TYPE_CHANNEL_STATUS =                         0x0A,
+       STK_EVENT_TYPE_SINGLE_ACCESS_TECHNOLOGY_CHANGE =        0x0B,
+       STK_EVENT_TYPE_DISPLAY_PARAMETERS_CHANGED =             0x0C,
+       STK_EVENT_TYPE_LOCAL_CONNECTION =                       0x0D,
+       STK_EVENT_TYPE_NETWORK_SEARCH_MODE_CHANGE =             0x0E,
+       STK_EVENT_TYPE_BROWSING_STATUS =                        0x0F,
+       STK_EVENT_TYPE_FRAMES_INFORMATION_CHANGE =              0x10,
+       STK_EVENT_TYPE_I_WLAN_ACCESS_STATUS =                   0x11,
+       STK_EVENT_TYPE_NETWORK_REJECTION =                      0x12,
+       STK_EVENT_TYPE_HCI_CONNECTIVITY_EVENT =                 0x13,
+       STK_EVENT_TYPE_MULTIPLE_ACCESS_TECHNOLOGIES_CHANGE =    0x14
+};
+
+enum stk_service_state {
+       STK_NORMAL_SERVICE =    0x00,
+       STK_LIMITED_SERVICE =   0x01,
+       STK_NO_SERVICE =        0x02
+};
+
+enum stk_icon_qualifier {
+       STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY =      0x00,
+       STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY =  0x01
+};
+
+enum stk_ins {
+       STK_INS_DEACTIVATE_FILE =                       0x04,
+       STK_INS_ERASE_RECORDS =                         0x0C,
+       STK_INS_ERASE_BINARY_0E =                       0x0E,
+       STK_INS_ERASE_BINARY_0F =                       0x0F,
+       STK_INS_PERFORM_SCQL_OPERATION =                0x10,
+       STK_INS_PERFORM_TRANSACTION_OPERATION =         0x12,
+       STK_INS_PERFORM_USER_OPERATION =                0x14,
+       STK_INS_VERIFY_20 =                             0x20,
+       STK_INS_VERIFY_21 =                             0x21,
+       STK_INS_MANAGE_SECURITY_ENVIRONMENT =           0x22,
+       STK_INS_CHANGE_REFERENCE_DATA =                 0x24,
+       STK_INS_DISABLE_VERIFICATION_REQUIREMENT =      0x26,
+       STK_INS_ENABLE_VERIFICATION_REQUIREMENT =       0x28,
+       STK_INS_PERFORM_SECURITY_OPERATION =            0x2A,
+       STK_INS_RESET_RETRY_COUNTER =                   0x2C,
+       STK_INS_ACTIVATE_FILE =                         0x44,
+       STK_INS_GENERATE_ASYMMETRIC_KEY_PAIR =          0x46,
+       STK_INS_MANAGE_CHANNEL =                        0x70,
+       STK_INS_EXTERNAL_AUTHENTICATE =                 0x82,
+       STK_INS_GET_CHALLENGE =                         0x84,
+       STK_INS_GENERAL_AUTHENTICATE_86 =               0x86,
+       STK_INS_GENERAL_AUTHENTICATE_87 =               0x87,
+       STK_INS_INTERNAL_AUTHENTICATE =                 0x88,
+       STK_INS_SEARCH_BINARY_A0 =                      0xA0,
+       STK_INS_SEARCH_BINARY_A1 =                      0xA1,
+       STK_INS_SEARCH_RECORD =                         0xA2,
+       STK_INS_SELECT =                                0xA4,
+       STK_INS_READ_BINARY_B0 =                        0xB0,
+       STK_INS_READ_BINARY_B1 =                        0xB1,
+       STK_INS_READ_RECORDS_B2 =                       0xB2,
+       STK_INS_READ_RECORDS_B3 =                       0xB3,
+       STK_INS_GET_RESPONSE =                          0xC0,
+       STK_INS_ENVELOPE_C2 =                           0xC2,
+       STK_INS_ENVELOPE_C3 =                           0xC3,
+       STK_INS_GET_DATA_CA =                           0xCA,
+       STK_INS_GET_DATA_CB =                           0xCB,
+       STK_INS_WRITE_BINARY_D0 =                       0xD0,
+       STK_INS_WRITE_BINARY_D1 =                       0xD1,
+       STK_INS_WRITE_RECORD =                          0xD2,
+       STK_INS_UPDATE_BINARY_D6 =                      0xD6,
+       STK_INS_UPDATE_BINARY_D7 =                      0xD7,
+       STK_INS_PUT_DATA_DA =                           0xDA,
+       STK_INS_PUT_DATA_DB =                           0xDB,
+       STK_INS_UPDATE_RECORD_DC =                      0xDC,
+       STK_INS_UPDATE_RECORD_DD =                      0xDD,
+       STK_INS_CREATE_FILE =                           0xE0,
+       STK_INS_APPEND_RECORD =                         0xE2,
+       STK_INS_DELETE_FILE =                           0xE4,
+       STK_INS_TERMINATE_DF =                          0xE6,
+       STK_INS_TERMINATE_EF =                          0xE8,
+       STK_INS_TERMINATE_CARD_USAGE =                  0xFE
+};
+
+enum stk_browser_id {
+       STK_BROWSER_ID_DEFAULT =        0x00,
+       STK_BROWSER_ID_WML =            0x01,
+       STK_BROWSER_ID_HTML =           0x02,
+       STK_BROWSER_ID_XHTML =          0x03,
+       STK_BROWSER_ID_CHTML =          0x04
+};
+
+enum stk_bearer {
+       STK_BEARER_SMS =        0x00,
+       STK_BEARER_CS_DATA =    0x01,
+       STK_BEARER_GSM_3G =     0x02,
+       STK_BEARER_PS =         0x03
+};
+
+enum stk_browser_termination_cause {
+       STK_BROWSER_USER_TERMINATION =          0x00,
+       STK_BROWSER_ERROR_TERMINATION =         0x01
+};
+
+/* Defined in TS 31.111 Section 8.52 */
+enum stk_bearer_type {
+       STK_BEARER_TYPE_CS =                    0x01,
+       STK_BEARER_TYPE_GPRS_UTRAN =            0x02,
+       STK_BEARER_TYPE_DEFAULT =               0x03,
+       STK_BEARER_TYPE_INDEPENDENT =           0x04,
+       STK_BEARER_TYPE_BLUETOOTH =             0x05,
+       STK_BEARER_TYPE_IRDA =                  0x06,
+       STK_BEARER_TYPE_RS232 =                 0x07,
+       STK_BEARER_TYPE_TIA_EIA_IS_820 =        0x08,
+       STK_BEARER_TYPE_UTRAN_WITH_EXT_PARAMS = 0x09,
+       STK_BEARER_TYPE_I_WLAN =                0x0A,
+       STK_BEARER_TYPE_EUTRAN_MAPPED_UTRAN =   0x0B,
+       STK_BEARER_TYPE_USB =                   0x10
+};
+
+enum stk_address_type {
+       STK_ADDRESS_AUTO =      -1,
+       STK_ADDRESS_IPV4 =      0x21,
+       STK_ADDRESS_IPV6 =      0x57
+};
+
+enum stk_access_technology_type {
+       STK_ACCESS_TECHNOLOGY_GSM =             0x00,
+       STK_ACCESS_TECHNOLOGY_TIA_EIA_553 =     0x01,
+       STK_ACCESS_TECHNOLOGY_TIA_EIA_136_C =   0x02,
+       STK_ACCESS_TECHNOLOGY_UTRAN =           0x03,
+       STK_ACCESS_TECHNOLOGY_TETRA =           0x04,
+       STK_ACCESS_TECHNOLOGY_TIA_EIA_95 =      0x05,
+       STK_ACCESS_TECHNOLOGY_CDMA2000_1X =     0x06,
+       STK_ACCESS_TECHNOLOGY_CDMA2000_HRPD =   0x07,
+       STK_ACCESS_TECHNOLOGY_EUTRAN =          0x08
+};
+
+enum stk_technology_id {
+       STK_TECHNOLOGY_INDEPENDENT =    0x00,
+       STK_TECHNOLOGY_BLUETOOTH =      0x01,
+       STK_TECHNOLOGY_IRDA =           0x02,
+       STK_TECHNOLOGY_RS232 =          0x03,
+       STK_TECHNOLOGY_USB =            0x04
+};
+
+enum stk_battery_state {
+       STK_BATTERY_VERY_LOW =  0x00,
+       STK_BATTERY_LOW =       0x01,
+       STK_BATTERY_AVERAGE =   0x02,
+       STK_BATTERY_GOOD =      0x03,
+       STK_BATTERY_FULL =      0x04
+};
+
+enum stk_frame_layout_type {
+       STK_LAYOUT_HORIZONTAL =         0x01,
+       STK_LAYOUT_VERTICAL =           0x02
+};
+
+enum stk_broadcast_network_technology {
+       STK_BROADCAST_NETWORK_DVB_H = 0x00,
+       STK_BROADCAST_NETWORK_DVB_T = 0x01,
+       STK_BROADCAST_NETWORK_DVB_SH = 0x02,
+       STK_BROADCAST_NETWORK_T_DMB = 0x03
+};
+
+enum stk_i_wlan_access_status {
+       STK_I_WLAN_STATUS_NO_COVERAGE           = 0x00,
+       STK_I_WLAN_STATUS_NOT_CONNECTED         = 0x01,
+       STK_I_WLAN_STATUS_CONNECTED             = 0x02,
+};
+
+enum stk_update_attach_type {
+       STK_UPDATE_ATTACH_NORMAL_LOCATION_UPDATING      = 0x00,
+       STK_UPDATE_ATTACH_PERIODIC_UPDATING             = 0x01,
+       STK_UPDATE_ATTACH_IMSI_ATTACH                   = 0x02,
+       STK_UPDATE_ATTACH_GPRS_ATTACH                   = 0x03,
+       STK_UPDATE_ATTACH_GPRS_IMSI_ATTACH              = 0x04,
+       STK_UPDATE_ATTACH_RA_UPDATING                   = 0x05,
+       STK_UPDATE_ATTACH_RA_LA_UPDATING                = 0x06,
+       STK_UPDATE_ATTACH_RA_LA_UPDATING_IMSI_ATTACH    = 0x07,
+       STK_UPDATE_ATTACH_PERIODIC_RA_UPDATING          = 0x08,
+       STK_UPDATE_ATTACH_EPS_ATTACH                    = 0x09,
+       STK_UPDATE_ATTACH_EPS_IMSI_ATTACH               = 0x0a,
+       STK_UPDATE_ATTACH_TA_UPDATING                   = 0x0b,
+       STK_UPDATE_ATTACH_TA_LA_UPDATING                = 0x0c,
+       STK_UPDATE_ATTACH_TA_LA_UPDATING_IMSI_ATTACH    = 0x0d,
+       STK_UPDATE_ATTACH_PERIDIC_TA_UPDATING           = 0x0e,
+};
+
+enum stk_rejection_cause_code {
+       /* MM and GMM codes (GERAN/UTRAN) */
+       STK_CAUSE_GMM_IMSI_UNKNOWN_IN_HLR               = 0x02,
+       STK_CAUSE_GMM_ILLEGAL_MS                        = 0x03,
+       STK_CAUSE_GMM_IMSI_UNKNOWN_IN_VLR               = 0x04,
+       STK_CAUSE_GMM_IMEI_NOT_ACCEPTED                 = 0x05,
+       STK_CAUSE_GMM_ILLEGAL_ME                        = 0x06,
+       STK_CAUSE_GMM_GPRS_NOT_ALLOWED                  = 0x07,
+       STK_CAUSE_GMM_GPRS_AND_NON_GPRS_NOT_ALLOWED     = 0x08,
+       STK_CAUSE_GMM_IMEI_NOT_DERIVED_BY_NETWORK       = 0x09,
+       STK_CAUSE_GMM_IMPLICITLY_DETACHED               = 0x0a,
+       STK_CAUSE_GMM_PLMN_NOT_ALLOWED                  = 0x0b,
+       STK_CAUSE_GMM_LAC_NOT_ALLOWED                   = 0x0c,
+       STK_CAUSE_GMM_ROAMING_NOT_ALLOWED               = 0x0d,
+       STK_CAUSE_GMM_GPRS_NOT_ALLOWED_IN_PLMN          = 0x0e,
+       STK_CAUSE_GMM_NO_SUITABLE_CELLS                 = 0x0f,
+       STK_CAUSE_GMM_MSC_TEMPORARILY_UNREACHABLE       = 0x10,
+       STK_CAUSE_GMM_NETWORK_FAILURE                   = 0x11,
+       STK_CAUSE_GMM_MAC_FAILURE                       = 0x14,
+       STK_CAUSE_GMM_SYNCH_FAILURE                     = 0x15,
+       STK_CAUSE_GMM_CONGESTION                        = 0x16,
+       STK_CAUSE_GMM_GSM_AUTHENTICATION_UNACCEPTABLE   = 0x17,
+       STK_CAUSE_GMM_NOT_AUTHORISED_FOR_CSG            = 0x19,
+       STK_CAUSE_GMM_SERVICE_OPTION_NOT_SUPPORTED      = 0x20,
+       STK_CAUSE_GMM_SERVICE_OPTION_NOT_SUBSCRIBED     = 0x21,
+       STK_CAUSE_GMM_SERVICE_OPTION_TEMPORARY_DEFUNC   = 0x22,
+       STK_CAUSE_GMM_CALL_NOT_IDENTIFIED               = 0x26,
+       STK_CAUSE_GMM_NO_PDP_CONTEXT_ACTIVATED          = 0x28,
+       STK_CAUSE_GMM_RETRY_ON_NEW_CELL                 = 0x30, /* to 0x3f */
+       STK_CAUSE_GMM_SEMANTICALLY_INCORRECT_MESSAGE    = 0x5f,
+       STK_CAUSE_GMM_INVALID_MANDATORY_INFO            = 0x60,
+       STK_CAUSE_GMM_MESSAGE_TYPE_UNKNOWN              = 0x61,
+       STK_CAUSE_GMM_MESSAGE_TYPE_INCOMPATIBLE_STATE   = 0x62,
+       STK_CAUSE_GMM_IE_UNKNOWN                        = 0x63,
+       STK_CAUSE_GMM_CONDITIONAL_IE_ERROR              = 0x64,
+       STK_CAUSE_GMM_MESSAGE_INCOMPATIBLE_WITH_STATE   = 0x65,
+       STK_CAUSE_GMM_PROTOCOL_ERROR                    = 0x6f,
+       /* EMM codes (E-UTRAN) */
+       STK_CAUSE_EMM_IMSI_UNKNOWN_IN_HSS               = 0x02,
+       STK_CAUSE_EMM_ILLEGAL_UE                        = 0x03,
+       STK_CAUSE_EMM_ILLEGAL_ME                        = 0x06,
+       STK_CAUSE_EMM_EPS_NOT_ALLOWED                   = 0x07,
+       STK_CAUSE_EMM_EPS_AND_NON_EPS_NOT_ALLOWED       = 0x08,
+       STK_CAUSE_EMM_IMEI_NOT_DERIVED_BY_NETWORK       = 0x09,
+       STK_CAUSE_EMM_IMPLICITLY_DETACHED               = 0x0a,
+       STK_CAUSE_EMM_PLMN_NOT_ALLOWED                  = 0x0b,
+       STK_CAUSE_EMM_TAC_NOT_ALLOWED                   = 0x0c,
+       STK_CAUSE_EMM_ROAMING_NOT_ALLOWED               = 0x0d,
+       STK_CAUSE_EMM_EPS_NOT_ALLOWED_IN_PLMN           = 0x0e,
+       STK_CAUSE_EMM_NO_SUITABLE_CELLS                 = 0x0f,
+       STK_CAUSE_EMM_MSC_TEMPORARILY_UNREACHABLE       = 0x10,
+       STK_CAUSE_EMM_NETWORK_FAILURE                   = 0x11,
+       STK_CAUSE_EMM_MAC_FAILURE                       = 0x14,
+       STK_CAUSE_EMM_SYNCH_FAILURE                     = 0x15,
+       STK_CAUSE_EMM_CONGESTION                        = 0x16,
+       STK_CAUSE_EMM_SECURITY_MODE_REJECTED            = 0x18,
+       STK_CAUSE_EMM_NOT_AUTHORISED_FOR_CSG            = 0x19,
+       STK_CAUSE_EMM_CS_FALLBACK_NOT_ALLOWED           = 0x26,
+       STK_CAUSE_EMM_CS_DOMAIN_TEMPORARY_UNAVAILABLE   = 0x27,
+       STK_CAUSE_EMM_NO_EPS_BEARER_CONTEXT_ACTIVATED   = 0x28,
+       STK_CAUSE_EMM_SEMANTICALLY_INCORRECT_MESSAGE    = 0x5f,
+       STK_CAUSE_EMM_INVALID_MANDATORY_INFO            = 0x60,
+       STK_CAUSE_EMM_MESSAGE_TYPE_UNKNOWN              = 0x61,
+       STK_CAUSE_EMM_MESSAGE_TYPE_INCOMPATIBLE_STATE   = 0x62,
+       STK_CAUSE_EMM_IE_UNKNOWN                        = 0x63,
+       STK_CAUSE_EMM_CONDITIONAL_IE_ERROR              = 0x64,
+       STK_CAUSE_EMM_MESSAGE_INCOMPATIBLE_WITH_STATE   = 0x65,
+       STK_CAUSE_EMM_PROTOCOL_ERROR                    = 0x6f,
+};
+
+enum stk_me_status {
+       STK_ME_STATUS_IDLE =            0x00,
+       STK_ME_STATUS_NOT_IDLE =        0x01
+};
+
+enum stk_img_scheme {
+       STK_IMG_SCHEME_BASIC =          0x11,
+       STK_IMG_SCHEME_COLOR =          0x21,
+       STK_IMG_SCHEME_TRANSPARENCY =   0x22,
+};
+
+/* Defined in TS 102.223 Section 8.6 */
+enum stk_qualifier_open_channel {
+       STK_OPEN_CHANNEL_FLAG_IMMEDIATE =               0x01,
+       STK_OPEN_CHANNEL_FLAG_AUTO_RECONNECT =          0x02,
+       STK_OPEN_CHANNEL_FLAG_BACKGROUND =              0x04,
+};
+
+/* Defined in TS 102.223 Section 8.6 */
+enum stk_qualifier_send_data {
+       STK_SEND_DATA_STORE_DATA =      0x00,
+       STK_SEND_DATA_IMMEDIATELY =     0x01,
+};
+
+/* Defined in TS 102.223 Section 8.56 */
+enum stk_channel_status {
+       STK_CHANNEL_PACKET_DATA_SERVICE_NOT_ACTIVATED = 0x00,
+       STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED =     0x01,
+       STK_CHANNEL_TCP_IN_CLOSED_STATE =               0x02,
+       STK_CHANNEL_TCP_IN_LISTEN_STATE =               0x03,
+       STK_CHANNEL_TCP_IN_ESTABLISHED_STATE =          0x04,
+       STK_CHANNEL_LINK_DROPPED =                      0x05,
+};
+
+/* Defined in TS 102.223 Section 8.59 */
+enum stk_transport_protocol_type {
+       STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE =      0x01,
+       STK_TRANSPORT_PROTOCOL_TCP_CLIENT_REMOTE =      0x02,
+       STK_TRANSPORT_PROTOCOL_TCP_SERVER =             0x03,
+       STK_TRANSPORT_PROTOCOL_UDP_CLIENT_LOCAL =       0x04,
+       STK_TRANSPORT_PROTOCOL_TCP_CLIENT_LOCAL =       0x05,
+       STK_TRANSPORT_PROTOCOL_DIRECT =                 0x06,
+};
+
+/* For data object that only has a byte array with undetermined length */
+struct stk_common_byte_array {
+       unsigned char *array;
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.1 */
+struct stk_address {
+       unsigned char ton_npi;
+       char *number;
+};
+
+/*
+ * Defined in TS 102.223 Section 8.3
+ *
+ * The maximum size of the subaddress is different depending on the referenced
+ * specification.  According to TS 24.008 Section 10.5.4.8: "The called party
+ * subaddress is a type 4 information element with a minimum length of 2 octets
+ * and a maximum length of 23 octets"
+ *
+ * According to TS 31.102 Section 4.4.2.4: "The subaddress data contains
+ * information as defined for this purpose in TS 24.008 [9]. All information
+ * defined in TS 24.008, except the information element identifier, shall be
+ * stored in the USIM. The length of this subaddress data can be up to 22
+ * bytes."
+ */
+struct stk_subaddress {
+       ofono_bool_t has_subaddr;
+       unsigned char len;
+       unsigned char subaddr[23];
+};
+
+/*
+ * Defined in TS 102.223 Section 8.4
+ *
+ * According to 24.008 Section 10.5.4.5 "The bearer capability is a type 4
+ * information element with a minimum length of 3 octets and a maximum length
+ * of 16 octets."
+ *
+ * According to TS 31.102 Section 4.2.38 the CCP length is 15 bytes.
+ *
+ * The CCP structure is not decoded, but stored as is from the CTLV
+ */
+struct stk_ccp {
+       unsigned char len;
+       unsigned char ccp[16];
+};
+
+/* Defined in TS 31.111 Section 8.5 */
+struct stk_cbs_page {
+       unsigned char len;
+       unsigned char page[88];
+};
+
+/*
+ * According to 102.223 Section 8.8 interval values of 0x00 are reserved.
+ * We use this to denote empty duration objects.
+ */
+struct stk_duration {
+       enum stk_duration_type unit;
+       unsigned char interval;
+};
+
+/* Defined in TS 102.223 Section 8.9 */
+struct stk_item {
+       unsigned char id;
+       char *text;
+};
+
+/*
+ * According to 102.223 Section 8.11, the maximum length should never be set
+ * to 0.
+ */
+struct stk_response_length {
+       unsigned char min;
+       unsigned char max;
+};
+
+/* Defined in TS 102.223 Section 8.12 */
+struct stk_result {
+       enum stk_result_type type;
+       unsigned int additional_len;
+       unsigned char *additional;
+};
+
+/* Defined in TS 102.223 Section 8.14 */
+struct stk_ss {
+       unsigned char ton_npi;
+       char *ss;
+};
+
+/* Defined in TS 131.111 Section 8.17.  Length limit of 160 chars in 23.028 */
+struct stk_ussd_string {
+       unsigned char dcs;
+       unsigned char string[160];
+       int len;
+};
+
+/*
+ * Define the struct of single file in TS102.223 Section 8.18.
+ * According to TS 11.11 Section 6.2, each file id has two bytes, and the
+ * maximum Dedicated File level is 2. So the maximum size of file is 8, which
+ * contains two bytes of Master File, 2 bytes of 1st level Dedicated File,
+ * 2 bytes of 2nd level Dedicated File and 2 bytes of Elementary File.
+ */
+struct stk_file {
+       unsigned char file[8];
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.19 */
+struct stk_location_info {
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       unsigned short lac_tac;
+       ofono_bool_t has_ci;
+       unsigned short ci;
+       ofono_bool_t has_ext_ci;
+       unsigned short ext_ci;
+       ofono_bool_t has_eutran_ci;
+       guint32 eutran_ci;
+};
+
+/*
+ * According to 102.223 Section 8.24 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_items_next_action_indicator {
+       unsigned char list[127];
+       unsigned int len;
+};
+
+/*
+ * According to 102.223 Section 8.25, there are 21 kinds of event type and no
+ * one should appear more than once.
+ */
+struct stk_event_list {
+       unsigned char list[21];
+       unsigned int len;
+};
+
+/*
+ * According to 102.223 Section 8.26, the maximum length of cause is 30.
+ */
+struct stk_cause {
+       unsigned char cause[30];
+       unsigned int len;
+       ofono_bool_t has_cause;
+};
+
+/*
+ * According to 102.223 Section 8.28 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_transaction_id {
+       unsigned char list[127];
+       unsigned int len;
+};
+
+/*
+ * According to 31.111 Section 8.29 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs. Each channel
+ * is represented as 10 bits, so the maximum number of channel is 127*8/10=101.
+ */
+struct stk_bcch_channel_list {
+       unsigned short channels[101];
+       unsigned int num;
+       ofono_bool_t has_list;
+};
+
+/*
+ * Defined in TS 102.223 Section 8.31
+ * Icon ID denotes a file on the SIM filesystem.  Since EF cannot have record
+ * ids of 0, we use icon_id with 0 to denote empty icon_identifier objects
+ */
+struct stk_icon_id {
+       unsigned char qualifier;
+       unsigned char id;
+};
+
+/*
+ * According to 102.223 Section 8.32 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs. This size also
+ * includes icon list qualifier for 1 byte, so the maxmimum size of icon
+ * identifier list is 126.
+ */
+struct stk_item_icon_id_list {
+       unsigned char qualifier;
+       unsigned char list[126];
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.33 */
+struct stk_reader_status {
+       int id;
+       ofono_bool_t removable;
+       ofono_bool_t present;
+       ofono_bool_t id1_size;
+       ofono_bool_t card_present;
+       ofono_bool_t card_powered;
+};
+
+/*
+ * According to 102.223 Section 8.34 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_card_atr {
+       unsigned char atr[127];
+       unsigned int len;
+};
+
+/*
+ * Defined in TS 102.223 Section 8.35. According to it, the maximum size
+ * of data is 236.
+ */
+struct stk_c_apdu {
+       unsigned char cla;
+       unsigned char ins;
+       unsigned char p1;
+       unsigned char p2;
+       unsigned char lc;
+       unsigned char data[236];
+       ofono_bool_t has_le;
+       unsigned char le;
+};
+
+/* Defined in TS 102.223 Section 8.36. According to it, the maximum size
+ * of data is 237.
+ */
+struct stk_r_apdu {
+       unsigned char sw1;
+       unsigned char sw2;
+       unsigned char data[237];
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.38 */
+struct stk_timer_value {
+       ofono_bool_t has_value;
+       unsigned char hour;
+       unsigned char minute;
+       unsigned char second;
+};
+
+/* Defined in TS 102.223 Section 8.42 */
+struct stk_bc_repeat {
+       ofono_bool_t has_bc_repeat;
+       unsigned char value;
+};
+
+/* Defined in TS 31.111 Section 8.46 */
+struct stk_timing_advance {
+       ofono_bool_t has_value;
+       enum stk_me_status status;
+       /*
+        * Contains bit periods number according to 3GPP TS
+        * 44.118 Section 9.3.106 / 3GPP TS 44.018 Section
+        * 10.5.2.40.1, not microseconds
+        */
+       unsigned char advance;
+};
+
+/* Bearer parameters for GPRS/UTRAN Packet Service/E-UTRAN */
+struct stk_gprs_bearer_parameters {
+       unsigned char precedence;
+       unsigned char delay;
+       unsigned char reliability;
+       unsigned char peak;
+       unsigned char mean;
+       unsigned char pdp_type;
+};
+
+/* Defined in TS 31.111 Section 8.52 */
+struct stk_bearer_description {
+       enum stk_bearer_type type;
+       struct stk_gprs_bearer_parameters gprs;
+};
+
+/*
+ * According to 102.223 Section 8.57 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_card_reader_id {
+       unsigned char id[127];
+       unsigned char len;
+};
+
+/*
+ * According to 102.223 Section 8.58 the address can be either ipv4 or ipv6.
+ * So the maximum size is 16 (for ipv6).
+ */
+struct stk_other_address {
+       union {
+               /* Network Byte Order */
+               guint32 ipv4;
+               unsigned char ipv6[16];
+       } addr;
+       enum stk_address_type type;
+};
+
+/* Defined in TS 102.223 Section 8.59 */
+struct stk_uicc_te_interface {
+       enum stk_transport_protocol_type protocol;
+       unsigned short port;
+};
+
+/*
+ * Defined in TS 102.223 Section 8.60.
+ * According to 101.220, Section 4, aid contains two fields RID and PIX.
+ * RID has 5 bytes, while PIX contains information between 7 to 11 bytes.
+ * So the maximum size of aid is 16 bytes.
+ */
+struct stk_aid {
+       unsigned char aid[16];
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.62 */
+struct stk_display_parameters {
+       unsigned char height;
+       unsigned char width;
+       unsigned char effects;
+};
+
+/* Defined in TS 102.223 Section 8.63 */
+struct stk_service_record {
+       unsigned char tech_id;
+       unsigned char serv_id;
+       unsigned char *serv_rec;
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.64 */
+struct stk_device_filter {
+       unsigned char tech_id;
+       unsigned char *dev_filter;
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.65 */
+struct stk_service_search {
+       unsigned char tech_id;
+       unsigned char *ser_search;
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.66 */
+struct stk_attribute_info {
+       unsigned char tech_id;
+       unsigned char *attr_info;
+       unsigned int len;
+};
+
+/*
+ * According to TS 102.223 Section 8.68, remote entity address can be either
+ * 6-bytes IEEE-802 address, or 4-bytes IrDA device address.
+ */
+struct stk_remote_entity_address {
+       unsigned char coding_type;
+       ofono_bool_t has_address;
+       union {
+               unsigned char ieee802[6];
+               unsigned char irda[4];
+       } addr;
+};
+
+/*
+ * According to 102.223 Section 8.72 the length of text attribute CTLV is 1
+ * byte.  This means that the maximum size is 127 according to the rules
+ * of CTLVs.  Empty attribute options will have len of 0.
+ */
+struct stk_text_attribute {
+       unsigned char attributes[127];
+       unsigned char len;
+};
+
+/* Defined in TS 31.111 Section 8.72 */
+struct stk_pdp_act_par {
+       unsigned char par[127];
+       unsigned char len;
+};
+
+/*
+ * According to 102.223 Section 8.73 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs. In addition,
+ * the length should be also the number multiplied by 4, so the maximum number
+ * is 124.
+ */
+struct stk_item_text_attribute_list {
+       unsigned char list[124];
+       unsigned char len;
+};
+
+/*
+ * According to 102.223 Section 8.78 the length of CTLV is 1 byte. This means
+ * that the maximum length is 127 bytes for the total length of layout and
+ * relative-sized frame. Thus the maximum length of relative size is 126 bytes.
+ */
+struct stk_frame_layout {
+       unsigned char layout;
+       unsigned char size[126];
+       unsigned int len;
+};
+
+/*
+ * According to 102.223 Section 8.79 the length of CTLV is 1 byte. This means
+ * that the maximum length is 127 bytes for the total length of default frame
+ * id and frame information list. Thus the maximum length of frame information
+ * list is 126 bytes.
+ */
+struct stk_frames_info {
+       unsigned char id;
+       struct {
+               unsigned char width, height;
+       } list[63];
+       unsigned int len;
+};
+
+/* Defined in TS 102.223 Section 8.80 */
+struct stk_frame_id {
+       ofono_bool_t has_id;
+       unsigned char id;
+};
+
+/*
+ * According to 102.223 Section 8.82 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_mms_reference {
+       unsigned char ref[127];
+       unsigned char len;
+};
+
+/*
+ * According to 102.223 Section 8.83 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_mms_id {
+       unsigned char id[127];
+       unsigned char len;
+};
+
+/*
+ * According to 102.223 Section 8.84 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_mms_transfer_status {
+       unsigned char status[127];
+       unsigned char len;
+};
+
+/*
+ * According to 102.223 Section 8.85 the length of CTLV is 1 byte. This means
+ * that the maximum size is 127 according to the rules of CTLVs.
+ */
+struct stk_mms_content_id {
+       unsigned char id[127];
+       unsigned char len;
+};
+
+/* Defined in TS 102.223 Section 8.88 */
+struct stk_registry_application_data {
+       unsigned short port;
+       unsigned char type;
+       char *name;
+};
+
+/*
+ * According to 102.223 Section 8.90 the length of CTLV is 1 byte. This means
+ * that the maximum length is 127 bytes for the total length of broadcast
+ * network technology and location information. Thus the maximum length of
+ * location information is 126 bytes.
+ */
+struct stk_broadcast_network_information {
+       unsigned char tech;
+       unsigned char loc_info[126];
+       unsigned int len;
+};
+
+/* Defined in TS 131.111 Section 8.91 */
+struct stk_routing_area_info {
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       unsigned short lac;
+       unsigned char rac;
+};
+
+/* Defined in TS 131.111 Section 8.99 */
+struct stk_tracking_area_id {
+       char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       char mcc[OFONO_MAX_MCC_LENGTH + 1];
+       unsigned short tac;
+};
+
+struct stk_command_display_text {
+       char *text;
+       struct stk_icon_id icon_id;
+       ofono_bool_t immediate_response;
+       struct stk_duration duration;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_get_inkey {
+       char *text;
+       struct stk_icon_id icon_id;
+       struct stk_duration duration;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_get_input {
+       char *text;
+       struct stk_response_length resp_len;
+       char *default_text;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_play_tone {
+       char *alpha_id;
+       unsigned char tone;
+       struct stk_duration duration;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_poll_interval {
+       struct stk_duration duration;
+};
+
+struct stk_command_setup_menu {
+       char *alpha_id;
+       GSList *items;
+       struct stk_items_next_action_indicator next_act;
+       struct stk_icon_id icon_id;
+       struct stk_item_icon_id_list item_icon_id_list;
+       struct stk_text_attribute text_attr;
+       struct stk_item_text_attribute_list item_text_attr_list;
+};
+
+struct stk_command_select_item {
+       char *alpha_id;
+       GSList *items;
+       struct stk_items_next_action_indicator next_act;
+       unsigned char item_id;
+       struct stk_icon_id icon_id;
+       struct stk_item_icon_id_list item_icon_id_list;
+       struct stk_text_attribute text_attr;
+       struct stk_item_text_attribute_list item_text_attr_list;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_send_sms {
+       char *alpha_id;
+       struct sms gsm_sms;
+       struct stk_common_byte_array cdma_sms;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_send_ss {
+       char *alpha_id;
+       struct stk_ss ss;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_send_ussd {
+       char *alpha_id;
+       struct stk_ussd_string ussd_string;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_setup_call {
+       char *alpha_id_usr_cfm;
+       struct stk_address addr;
+       struct stk_ccp ccp;
+       struct stk_subaddress subaddr;
+       struct stk_duration duration;
+       struct stk_icon_id icon_id_usr_cfm;
+       char *alpha_id_call_setup;
+       struct stk_icon_id icon_id_call_setup;
+       struct stk_text_attribute text_attr_usr_cfm;
+       struct stk_text_attribute text_attr_call_setup;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_refresh {
+       GSList *file_list;
+       struct stk_aid aid;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_setup_event_list {
+       struct stk_event_list event_list;
+};
+
+struct stk_command_perform_card_apdu {
+       struct stk_c_apdu c_apdu;
+};
+
+struct stk_command_timer_mgmt {
+       unsigned char timer_id;
+       struct stk_timer_value timer_value;
+};
+
+struct stk_command_setup_idle_mode_text {
+       char *text;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_run_at_command {
+       char *alpha_id;
+       char *at_command;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_send_dtmf {
+       char *alpha_id;
+       char *dtmf;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_language_notification {
+       char language[3];
+};
+
+struct stk_command_launch_browser {
+       unsigned char browser_id;
+       char *url;
+       struct stk_common_byte_array bearer;
+       GSList *prov_file_refs;
+       char *text_gateway_proxy_id;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       struct stk_common_byte_array network_name;
+       char *text_usr;
+       char *text_passwd;
+};
+
+struct stk_command_open_channel {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_bearer_description bearer_desc;
+       unsigned short buf_size;
+       char *apn;
+       struct stk_other_address local_addr;
+       char *text_usr;
+       char *text_passwd;
+       struct stk_uicc_te_interface uti;
+       struct stk_other_address data_dest_addr;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_close_channel {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_receive_data {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       unsigned char data_len;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_send_data {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_common_byte_array data;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_service_search {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_service_search serv_search;
+       struct stk_device_filter dev_filter;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_get_service_info {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_attribute_info attr_info;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_declare_service {
+       struct stk_service_record serv_rec;
+       struct stk_uicc_te_interface intf;
+};
+
+struct stk_command_set_frames {
+       struct stk_frame_id frame_id;
+       struct stk_frame_layout frame_layout;
+       struct stk_frame_id frame_id_default;
+};
+
+struct stk_command_retrieve_mms {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_mms_reference mms_ref;
+       GSList *mms_rec_files;
+       struct stk_mms_content_id mms_content_id;
+       struct stk_mms_id mms_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_submit_mms {
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       GSList *mms_subm_files;
+       struct stk_mms_id mms_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_display_mms {
+       GSList *mms_subm_files;
+       struct stk_mms_id mms_id;
+       ofono_bool_t imd_resp;
+       struct stk_frame_id frame_id;
+};
+
+struct stk_command_activate {
+       unsigned char actv_desc;
+};
+
+enum stk_command_parse_result {
+       STK_PARSE_RESULT_OK,
+       STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD,
+       STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD,
+       STK_PARSE_RESULT_MISSING_VALUE,
+};
+
+struct stk_command {
+       unsigned char number;
+       unsigned char type;
+       unsigned char qualifier;
+       enum stk_device_identity_type src;
+       enum stk_device_identity_type dst;
+       enum stk_command_parse_result status;
+
+       union {
+               struct stk_command_display_text display_text;
+               struct stk_command_get_inkey get_inkey;
+               struct stk_command_get_input get_input;
+               struct stk_command_play_tone play_tone;
+               struct stk_command_poll_interval poll_interval;
+               struct stk_command_refresh refresh;
+               struct stk_command_setup_menu setup_menu;
+               struct stk_command_select_item select_item;
+               struct stk_command_send_sms send_sms;
+               struct stk_command_send_ss send_ss;
+               struct stk_command_send_ussd send_ussd;
+               struct stk_command_setup_call setup_call;
+               struct stk_command_setup_event_list setup_event_list;
+               struct stk_command_perform_card_apdu perform_card_apdu;
+               struct stk_command_timer_mgmt timer_mgmt;
+               struct stk_command_setup_idle_mode_text setup_idle_mode_text;
+               struct stk_command_run_at_command run_at_command;
+               struct stk_command_send_dtmf send_dtmf;
+               struct stk_command_language_notification language_notification;
+               struct stk_command_launch_browser launch_browser;
+               struct stk_command_open_channel open_channel;
+               struct stk_command_close_channel close_channel;
+               struct stk_command_receive_data receive_data;
+               struct stk_command_send_data send_data;
+               struct stk_command_service_search service_search;
+               struct stk_command_get_service_info get_service_info;
+               struct stk_command_declare_service declare_service;
+               struct stk_command_set_frames set_frames;
+               struct stk_command_retrieve_mms retrieve_mms;
+               struct stk_command_submit_mms submit_mms;
+               struct stk_command_display_mms display_mms;
+               struct stk_command_activate activate;
+       };
+
+       void (*destructor)(struct stk_command *command);
+};
+
+/* TERMINAL RESPONSEs defined in TS 102.223 Section 6.8 */
+struct stk_response_generic {
+};
+
+struct stk_answer_text {
+       char *text;
+       ofono_bool_t packed;
+       ofono_bool_t yesno;
+       /*
+        * If a "Yes/No" answer was requested in a GET INKEY command,
+        * .yesno must be TRUE and text should be non-NULL to indicate
+        * a Yes response or NULL to indicate a No response.
+        */
+};
+
+struct stk_ussd_text {
+       ofono_bool_t has_text;
+       const unsigned char *text;
+       int dcs;
+       int len;
+};
+
+struct stk_channel {
+       unsigned char id;
+       enum stk_channel_status status;
+};
+
+struct stk_response_get_inkey {
+       struct stk_answer_text text;
+       struct stk_duration duration;
+};
+
+struct stk_response_get_input {
+       struct stk_answer_text text;
+};
+
+struct stk_response_poll_interval {
+       struct stk_duration max_interval;
+};
+
+struct stk_response_select_item {
+       unsigned char item_id;
+};
+
+struct stk_response_set_up_call {
+       struct stk_common_byte_array cc_requested_action;
+       struct {
+               ofono_bool_t cc_modified;
+               struct stk_result result;
+       } modified_result;
+};
+
+struct stk_response_local_info {
+       union {
+               struct stk_location_info location;
+               const char *imei;
+               struct stk_network_measurement_results {
+                       struct stk_common_byte_array nmr;
+                       struct stk_bcch_channel_list bcch_ch_list;
+               } nmr;
+               struct sms_scts datetime;
+               const char *language;
+               enum stk_battery_state battery_charge;
+               enum stk_access_technology_type access_technology;
+               struct stk_timing_advance tadv;
+               /* Bits[31:24]: manufacturer, bits[23:0]: serial number */
+               guint32 esn;
+               const char *imeisv;
+               enum stk_network_search_mode {
+                       STK_NETWORK_SEARCH_MODE_MANUAL = 0x00,
+                       STK_NETWORK_SEARCH_MODE_AUTOMATIC = 0x01,
+               } search_mode;
+               const char *meid;
+               struct stk_broadcast_network_information broadcast_network_info;
+               struct stk_access_technologies {
+                       const enum stk_access_technology_type *techs;
+                       int length;
+               } access_technologies;
+               struct {
+                       struct stk_access_technologies access_techs;
+                       struct stk_location_info *locations;
+               } location_infos;
+               struct {
+                       struct stk_access_technologies access_techs;
+                       struct stk_network_measurement_results *nmrs;
+               } nmrs;
+       };
+};
+
+struct stk_response_timer_mgmt {
+       unsigned char id;
+       struct stk_timer_value value;
+};
+
+struct stk_response_run_at_command {
+       const char *at_response;
+};
+
+struct stk_response_send_ussd {
+       struct stk_ussd_text text;
+};
+
+struct stk_response_open_channel {
+       struct stk_channel channel;
+       struct stk_bearer_description bearer_desc;
+       unsigned short buf_size;
+};
+
+struct stk_response_receive_data {
+       struct stk_common_byte_array rx_data;
+       unsigned short rx_remaining;
+};
+
+struct stk_response_send_data {
+       unsigned short tx_avail;
+};
+
+struct stk_response_channel_status {
+       struct stk_channel channel;
+};
+
+struct stk_response {
+       unsigned char number;
+       unsigned char type;
+       unsigned char qualifier;
+       enum stk_device_identity_type src;
+       enum stk_device_identity_type dst;
+       struct stk_result result;
+
+       union {
+               struct stk_response_generic display_text;
+               struct stk_response_get_inkey get_inkey;
+               struct stk_response_get_input get_input;
+               struct stk_response_generic more_time;
+               struct stk_response_generic play_tone;
+               struct stk_response_poll_interval poll_interval;
+               struct stk_response_generic refresh;
+               struct stk_response_generic set_up_menu;
+               struct stk_response_select_item select_item;
+               struct stk_response_generic send_sms;
+               struct stk_response_set_up_call set_up_call;
+               struct stk_response_generic polling_off;
+               struct stk_response_local_info provide_local_info;
+               struct stk_response_generic set_up_event_list;
+               struct stk_response_timer_mgmt timer_mgmt;
+               struct stk_response_generic set_up_idle_mode_text;
+               struct stk_response_run_at_command run_at_command;
+               struct stk_response_generic send_dtmf;
+               struct stk_response_generic language_notification;
+               struct stk_response_generic launch_browser;
+               struct stk_response_send_ussd send_ussd;
+               struct stk_response_open_channel open_channel;
+               struct stk_response_receive_data receive_data;
+               struct stk_response_send_data send_data;
+               struct stk_response_channel_status channel_status;
+       };
+
+       void (*destructor)(struct stk_response *response);
+};
+
+/* ENVELOPEs defined in TS 102.223 Section 7 */
+struct stk_envelope_sms_pp_download {
+       struct stk_address address;
+       struct sms_deliver message;
+};
+
+struct stk_envelope_cbs_pp_download {
+       struct cbs page;
+};
+
+struct stk_envelope_menu_selection {
+       unsigned char item_id;
+       ofono_bool_t help_request;
+};
+
+struct stk_envelope_sms_mo_control {
+       struct stk_address sc_address;
+       struct stk_address dest_address;
+       struct stk_location_info location;
+};
+
+enum stk_call_control_type {
+       STK_CC_TYPE_CALL_SETUP,
+       STK_CC_TYPE_SUPPLEMENTARY_SERVICE,
+       STK_CC_TYPE_USSD_OP,
+       STK_CC_TYPE_PDP_CTX_ACTIVATION,
+       STK_CC_TYPE_EPS_PDN_CONNECTION_ACTIVATION,
+};
+
+/* Used both in the ENVELOPE message to UICC and response from UICC */
+struct stk_envelope_call_control {
+       enum stk_call_control_type type;
+       union {
+               struct stk_address address;
+               struct stk_address ss_string;
+               struct stk_ussd_string ussd_string;
+               struct stk_common_byte_array pdp_ctx_params;
+               struct stk_common_byte_array eps_pdn_params;
+       };
+       /*
+        * At least one of the following two fields must be present in a
+        * response indicating modification of the call.
+        * In an EVELOPE message, only allowed for a call setup.
+        */
+       struct stk_ccp ccp1;
+       struct stk_subaddress subaddress;
+       struct stk_location_info location;
+       /* Only allowed when ccp1 is present */
+       struct stk_ccp ccp2;
+       char *alpha_id;
+       /* Only allowed when both ccp1 and ccp2 are present */
+       struct stk_bc_repeat bc_repeat;
+};
+
+struct stk_envelope_event_download {
+       enum stk_event_type type;
+       union {
+               struct {
+                       unsigned char transaction_id;
+                       struct stk_address caller_address;
+                       struct stk_subaddress caller_subaddress;
+               } mt_call;
+               struct {
+                       unsigned char transaction_id;
+               } call_connected;
+               struct {
+                       struct stk_transaction_id transaction_ids;
+                       struct stk_cause cause;
+               } call_disconnected;
+               struct {
+                       enum stk_service_state state;
+                       /* Present when state indicated Normal Service */
+                       struct stk_location_info info;
+               } location_status;
+               struct stk_reader_status card_reader_status;
+               char language_selection[3];
+               struct {
+                       enum stk_browser_termination_cause cause;
+               } browser_termination;
+               struct {
+                       struct stk_channel channel;
+                       unsigned short channel_data_len;
+               } data_available;
+               struct {
+                       struct stk_channel channel;
+                       struct stk_bearer_description bearer_desc;
+                       struct stk_other_address address;
+               } channel_status;
+               struct stk_access_technologies access_technology_change;
+               struct stk_display_parameters display_params_changed;
+               struct {
+                       /*
+                        * Note the service record subfield is not required,
+                        * only the Technology id and Service id.
+                        */
+                       struct stk_service_record service_record;
+                       struct stk_remote_entity_address remote_addr;
+                       struct stk_uicc_te_interface transport_level;
+                       /* Only present if transport_level present */
+                       struct stk_other_address transport_addr;
+               } local_connection;
+               enum stk_network_search_mode network_search_mode_change;
+               struct stk_common_byte_array browsing_status;
+               struct stk_frames_info frames_information_change;
+               enum stk_i_wlan_access_status i_wlan_access_status;
+               struct {
+                       struct stk_location_info location;
+                       struct stk_routing_area_info rai;
+                       struct stk_tracking_area_id tai;
+                       enum stk_access_technology_type access_tech;
+                       enum stk_update_attach_type update_attach;
+                       enum stk_rejection_cause_code cause;
+               } network_rejection;
+       };
+};
+
+struct stk_envelope_timer_expiration {
+       unsigned char id;
+       struct stk_timer_value value;
+};
+
+struct stk_envelope_ussd_data_download {
+       struct stk_ussd_string string;
+};
+
+struct stk_envelope_mms_transfer_status {
+       struct stk_file transfer_file;
+       struct stk_mms_id id;
+       struct stk_mms_transfer_status transfer_status;
+};
+
+struct stk_envelope_mms_notification_download {
+       struct stk_common_byte_array msg;
+       ofono_bool_t last;
+};
+
+struct stk_envelope_terminal_apps {
+       struct stk_registry_application_data *list;
+       int count;
+       ofono_bool_t last;
+};
+
+struct stk_envelope {
+       enum stk_envelope_type type;
+       enum stk_device_identity_type src;
+       enum stk_device_identity_type dst;
+       union {
+               struct stk_envelope_sms_pp_download sms_pp_download;
+               struct stk_envelope_cbs_pp_download cbs_pp_download;
+               struct stk_envelope_menu_selection menu_selection;
+               struct stk_envelope_call_control call_control;
+               struct stk_envelope_sms_mo_control sms_mo_control;
+               struct stk_envelope_event_download event_download;
+               struct stk_envelope_timer_expiration timer_expiration;
+               struct stk_envelope_ussd_data_download ussd_data_download;
+               struct stk_envelope_mms_transfer_status mms_status;
+               struct stk_envelope_mms_notification_download mms_notification;
+               struct stk_envelope_terminal_apps terminal_apps;
+       };
+};
+
+struct stk_command *stk_command_new_from_pdu(const unsigned char *pdu,
+                                               unsigned int len);
+void stk_command_free(struct stk_command *command);
+
+const unsigned char *stk_pdu_from_response(const struct stk_response *response,
+                                               unsigned int *out_length);
+const unsigned char *stk_pdu_from_envelope(const struct stk_envelope *envelope,
+                                               unsigned int *out_length);
+char *stk_text_to_html(const char *text,
+                               const unsigned short *attrs, int num_attrs);
+char *stk_image_to_xpm(const unsigned char *img, unsigned int len,
+                       enum stk_img_scheme scheme, const unsigned char *clut,
+                       unsigned short clut_len);
diff --git a/src/storage.c b/src/storage.c
new file mode 100644 (file)
index 0000000..bde0bea
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "storage.h"
+
+int create_dirs(const char *filename, const mode_t mode)
+{
+       struct stat st;
+       char *dir;
+       const char *prev, *next;
+       int err;
+
+       if (filename[0] != '/')
+               return -1;
+
+       err = stat(filename, &st);
+       if (!err && S_ISREG(st.st_mode))
+               return 0;
+
+       dir = g_try_malloc(strlen(filename) + 1);
+       if (dir == NULL)
+               return -1;
+
+       strcpy(dir, "/");
+
+       for (prev = filename; (next = strchr(prev + 1, '/')); prev = next) {
+               /* Skip consecutive '/' characters */
+               if (next - prev == 1)
+                       continue;
+
+               strncat(dir, prev + 1, next - prev);
+
+               if (mkdir(dir, mode) == -1 && errno != EEXIST) {
+                       g_free(dir);
+                       return -1;
+               }
+       }
+
+       g_free(dir);
+       return 0;
+}
+
+ssize_t read_file(unsigned char *buffer, size_t len,
+                       const char *path_fmt, ...)
+{
+       va_list ap;
+       char *path;
+       ssize_t r;
+       int fd;
+
+       va_start(ap, path_fmt);
+       path = g_strdup_vprintf(path_fmt, ap);
+       va_end(ap);
+
+       fd = TFR(open(path, O_RDONLY));
+
+       g_free(path);
+
+       if (fd == -1)
+               return -1;
+
+       r = TFR(read(fd, buffer, len));
+
+       TFR(close(fd));
+
+       return r;
+}
+
+/*
+ * Write a buffer to a file in a transactionally safe form
+ *
+ * Given a buffer, write it to a file named after
+ * @path_fmt+args. However, to make sure the file contents are
+ * consistent (ie: a crash right after opening or during write()
+ * doesn't leave a file half baked), the contents are written to a
+ * file with a temporary name and when closed, it is renamed to the
+ * specified name (@path_fmt+args).
+ */
+ssize_t write_file(const unsigned char *buffer, size_t len, mode_t mode,
+                       const char *path_fmt, ...)
+{
+       va_list ap;
+       char *tmp_path, *path;
+       ssize_t r;
+       int fd;
+
+       va_start(ap, path_fmt);
+       path = g_strdup_vprintf(path_fmt, ap);
+       va_end(ap);
+
+       tmp_path = g_strdup_printf("%s.XXXXXX.tmp", path);
+
+       r = -1;
+       if (create_dirs(path, mode | S_IXUSR) != 0)
+               goto error_create_dirs;
+
+       fd = TFR(g_mkstemp_full(tmp_path, O_WRONLY | O_CREAT | O_TRUNC, mode));
+       if (fd == -1)
+               goto error_mkstemp_full;
+
+       r = TFR(write(fd, buffer, len));
+
+       TFR(close(fd));
+
+       if (r != (ssize_t) len) {
+               r = -1;
+               goto error_write;
+       }
+
+       /*
+        * Now that the file contents are written, rename to the real
+        * file name; this way we are uniquely sure that the whole
+        * thing is there.
+        */
+       unlink(path);
+
+       /* conserve @r's value from 'write' */
+       if (link(tmp_path, path) == -1)
+               r = -1;
+
+error_write:
+       unlink(tmp_path);
+error_mkstemp_full:
+error_create_dirs:
+       g_free(tmp_path);
+       g_free(path);
+       return r;
+}
+
+GKeyFile *storage_open(const char *imsi, const char *store)
+{
+       GKeyFile *keyfile;
+       char *path;
+
+       if (store == NULL)
+               return NULL;
+
+       if (imsi)
+               path = g_strdup_printf(STORAGEDIR "/%s/%s", imsi, store);
+       else
+               path = g_strdup_printf(STORAGEDIR "/%s", store);
+
+       keyfile = g_key_file_new();
+
+       if (path) {
+               g_key_file_load_from_file(keyfile, path, 0, NULL);
+               g_free(path);
+       }
+
+       return keyfile;
+}
+
+void storage_sync(const char *imsi, const char *store, GKeyFile *keyfile)
+{
+       char *path;
+       char *data;
+       gsize length = 0;
+
+       if (imsi)
+               path = g_strdup_printf(STORAGEDIR "/%s/%s", imsi, store);
+       else
+               path = g_strdup_printf(STORAGEDIR "/%s", store);
+
+       if (path == NULL)
+               return;
+
+       if (create_dirs(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
+               g_free(path);
+               return;
+       }
+
+       data = g_key_file_to_data(keyfile, &length, NULL);
+
+       g_file_set_contents(path, data, length, NULL);
+
+       g_free(data);
+       g_free(path);
+}
+
+void storage_close(const char *imsi, const char *store, GKeyFile *keyfile,
+                       gboolean save)
+{
+       if (save == TRUE)
+               storage_sync(imsi, store, keyfile);
+
+       g_key_file_free(keyfile);
+}
diff --git a/src/storage.h b/src/storage.h
new file mode 100644 (file)
index 0000000..c455bae
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef TEMP_FAILURE_RETRY
+#define TFR TEMP_FAILURE_RETRY
+#else
+#define TFR
+#endif
+
+#include <fcntl.h>
+
+int create_dirs(const char *filename, const mode_t mode);
+
+ssize_t read_file(unsigned char *buffer, size_t len,
+                       const char *path_fmt, ...)
+       __attribute__((format(printf, 3, 4)));
+
+ssize_t write_file(const unsigned char *buffer, size_t len, mode_t mode,
+                       const char *path_fmt, ...)
+       __attribute__((format(printf, 4, 5)));
+
+GKeyFile *storage_open(const char *imsi, const char *store);
+void storage_sync(const char *imsi, const char *store, GKeyFile *keyfile);
+void storage_close(const char *imsi, const char *store, GKeyFile *keyfile,
+                       gboolean save);
diff --git a/src/ussd.c b/src/ussd.c
new file mode 100644 (file)
index 0000000..d2d3621
--- /dev/null
@@ -0,0 +1,919 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "smsutil.h"
+#include "util.h"
+
+#define MAX_USSD_LENGTH 160
+
+static GSList *g_drivers = NULL;
+
+enum ussd_state {
+       USSD_STATE_IDLE = 0,
+       USSD_STATE_ACTIVE = 1,
+       USSD_STATE_USER_ACTION = 2,
+       USSD_STATE_RESPONSE_SENT,
+};
+
+struct ussd_request {
+       ofono_ussd_request_cb_t cb;
+       void *user_data;
+};
+
+struct ofono_ussd {
+       int state;
+       DBusMessage *pending;
+       DBusMessage *cancel;
+       int flags;
+       GSList *ss_control_list;
+       GSList *ss_passwd_list;
+       const struct ofono_ussd_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       struct ussd_request *req;
+};
+
+struct ssc_entry {
+       char *service;
+       void *cb;
+       void *user;
+       ofono_destroy_func destroy;
+};
+
+gboolean __ofono_ussd_is_busy(struct ofono_ussd *ussd)
+{
+       if (ussd == NULL)
+               return FALSE;
+
+       if (ussd->pending || ussd->state != USSD_STATE_IDLE || ussd->req)
+               return TRUE;
+
+       return FALSE;
+}
+
+static struct ssc_entry *ssc_entry_create(const char *sc, void *cb, void *data,
+                                               ofono_destroy_func destroy)
+{
+       struct ssc_entry *r;
+
+       r = g_try_new0(struct ssc_entry, 1);
+
+       if (r == NULL)
+               return r;
+
+       r->service = g_strdup(sc);
+       r->cb = cb;
+       r->user = data;
+       r->destroy = destroy;
+
+       return r;
+}
+
+static void ssc_entry_destroy(struct ssc_entry *ca)
+{
+       if (ca->destroy)
+               ca->destroy(ca->user);
+
+       g_free(ca->service);
+       g_free(ca);
+}
+
+static gint ssc_entry_find_by_service(gconstpointer a, gconstpointer b)
+{
+       const struct ssc_entry *ca = a;
+
+       return strcmp(ca->service, b);
+}
+
+gboolean __ofono_ussd_ssc_register(struct ofono_ussd *ussd, const char *sc,
+                                       ofono_ussd_ssc_cb_t cb, void *data,
+                                       ofono_destroy_func destroy)
+{
+       struct ssc_entry *entry;
+
+       if (ussd == NULL)
+               return FALSE;
+
+       entry = ssc_entry_create(sc, cb, data, destroy);
+       if (entry == NULL)
+               return FALSE;
+
+       ussd->ss_control_list = g_slist_prepend(ussd->ss_control_list, entry);
+
+       return TRUE;
+}
+
+void __ofono_ussd_ssc_unregister(struct ofono_ussd *ussd, const char *sc)
+{
+       GSList *l;
+
+       if (ussd == NULL)
+               return;
+
+       l = g_slist_find_custom(ussd->ss_control_list, sc,
+                               ssc_entry_find_by_service);
+
+       if (l == NULL)
+               return;
+
+       ssc_entry_destroy(l->data);
+       ussd->ss_control_list = g_slist_remove(ussd->ss_control_list, l->data);
+}
+
+gboolean __ofono_ussd_passwd_register(struct ofono_ussd *ussd, const char *sc,
+                                       ofono_ussd_passwd_cb_t cb, void *data,
+                                       ofono_destroy_func destroy)
+{
+       struct ssc_entry *entry;
+
+       if (ussd == NULL)
+               return FALSE;
+
+       entry = ssc_entry_create(sc, cb, data, destroy);
+       if (entry == NULL)
+               return FALSE;
+
+       ussd->ss_passwd_list = g_slist_prepend(ussd->ss_passwd_list, entry);
+
+       return TRUE;
+}
+
+void __ofono_ussd_passwd_unregister(struct ofono_ussd *ussd, const char *sc)
+{
+       GSList *l;
+
+       if (ussd == NULL)
+               return;
+
+       l = g_slist_find_custom(ussd->ss_passwd_list, sc,
+                               ssc_entry_find_by_service);
+
+       if (l == NULL)
+               return;
+
+       ssc_entry_destroy(l->data);
+       ussd->ss_passwd_list = g_slist_remove(ussd->ss_passwd_list, l->data);
+}
+
+static gboolean recognized_passwd_change_string(struct ofono_ussd *ussd,
+                                               int type, char *sc,
+                                               char *sia, char *sib,
+                                               char *sic, char *sid,
+                                               char *dn, DBusMessage *msg)
+{
+       GSList *l = ussd->ss_passwd_list;
+
+       switch (type) {
+       case SS_CONTROL_TYPE_ACTIVATION:
+       case SS_CONTROL_TYPE_REGISTRATION:
+               break;
+
+       default:
+               return FALSE;
+       }
+
+       if (strcmp(sc, "03") || strlen(dn))
+               return FALSE;
+
+       /* If SIC & SID don't match, then we just bail out here */
+       if (strcmp(sic, sid)) {
+               DBusConnection *conn = ofono_dbus_get_connection();
+               DBusMessage *reply = __ofono_error_invalid_format(msg);
+               g_dbus_send_message(conn, reply);
+               return TRUE;
+       }
+
+       while ((l = g_slist_find_custom(l, sia,
+                       ssc_entry_find_by_service)) != NULL) {
+               struct ssc_entry *entry = l->data;
+               ofono_ussd_passwd_cb_t cb = entry->cb;
+
+               if (cb(sia, sib, sic, msg, entry->user))
+                       return TRUE;
+
+               l = l->next;
+       }
+
+       return FALSE;
+}
+
+static gboolean recognized_control_string(struct ofono_ussd *ussd,
+                                               const char *ss_str,
+                                               DBusMessage *msg)
+{
+       char *str = g_strdup(ss_str);
+       char *sc, *sia, *sib, *sic, *sid, *dn;
+       int type;
+       gboolean ret = FALSE;
+
+       DBG("parsing control string");
+
+       if (parse_ss_control_string(str, &type, &sc,
+                               &sia, &sib, &sic, &sid, &dn)) {
+               GSList *l = ussd->ss_control_list;
+
+               DBG("Got parse result: %d, %s, %s, %s, %s, %s, %s",
+                               type, sc, sia, sib, sic, sid, dn);
+
+               /*
+                * A password change string needs to be treated separately
+                * because it uses a fourth SI and is thus not a valid
+                * control string.
+                */
+               if (recognized_passwd_change_string(ussd, type, sc,
+                                       sia, sib, sic, sid, dn, msg)) {
+                       ret = TRUE;
+                       goto out;
+               }
+
+               if (*sid != '\0')
+                       goto out;
+
+               while ((l = g_slist_find_custom(l, sc,
+                               ssc_entry_find_by_service)) != NULL) {
+                       struct ssc_entry *entry = l->data;
+                       ofono_ussd_ssc_cb_t cb = entry->cb;
+
+                       if (cb(type, sc, sia, sib, sic, dn, msg, entry->user)) {
+                               ret = TRUE;
+                               goto out;
+                       }
+
+                       l = l->next;
+               }
+
+       }
+
+       /* TODO: Handle all strings that control voice calls */
+
+       /* TODO: Handle Multiple subscriber profile DN*59#SEND and *59#SEND */
+
+       /*
+        * Note: SIM PIN/PIN2 change and unblock and IMEI presentation
+        * procedures are not handled by the daemon since they are not followed
+        * by SEND and are not valid USSD requests.
+        */
+
+out:
+       g_free(str);
+
+       return ret;
+}
+
+static const char *ussd_get_state_string(struct ofono_ussd *ussd)
+{
+       switch (ussd->state) {
+       case USSD_STATE_IDLE:
+               return "idle";
+       case USSD_STATE_ACTIVE:
+       case USSD_STATE_RESPONSE_SENT:
+               return "active";
+       case USSD_STATE_USER_ACTION:
+               return "user-response";
+       }
+
+       return "";
+}
+
+static void ussd_change_state(struct ofono_ussd *ussd, int state)
+{
+       const char *value;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(ussd->atom);
+
+       if (state == ussd->state)
+               return;
+
+       ussd->state = state;
+
+       value = ussd_get_state_string(ussd);
+       ofono_dbus_signal_property_changed(conn, path,
+                       OFONO_SUPPLEMENTARY_SERVICES_INTERFACE,
+                       "State", DBUS_TYPE_STRING, &value);
+}
+
+static void ussd_request_finish(struct ofono_ussd *ussd, int error, int dcs,
+                               const unsigned char *pdu, int len)
+{
+       struct ussd_request *req = ussd->req;
+
+       if (req && req->cb)
+               req->cb(error, dcs, pdu, len, req->user_data);
+
+       g_free(req);
+       ussd->req = NULL;
+}
+
+static int ussd_status_to_failure_code(int status)
+{
+       switch (status) {
+       case OFONO_USSD_STATUS_TIMED_OUT:
+               return -ETIMEDOUT;
+       case OFONO_USSD_STATUS_NOT_SUPPORTED:
+               return -ENOSYS;
+       }
+
+       return 0;
+}
+
+static char const *ussd_status_name(int status)
+{
+       switch (status) {
+       case OFONO_USSD_STATUS_NOTIFY:
+               return "NOTIFY";
+       case OFONO_USSD_STATUS_ACTION_REQUIRED:
+               return "ACTION_REQUIRED";
+       case OFONO_USSD_STATUS_TERMINATED:
+               return "TERMINATED";
+       case OFONO_USSD_STATUS_LOCAL_CLIENT_RESPONDED:
+               return "LOCAL_CLIENT_RESPONDED";
+       case OFONO_USSD_STATUS_NOT_SUPPORTED:
+               return "NOT_SUPPORTED";
+       case OFONO_USSD_STATUS_TIMED_OUT:
+               return "TIMED_OUT";
+       }
+
+       return "????";
+}
+
+static const char *ussd_state_name(enum ussd_state state)
+{
+       switch (state) {
+       case USSD_STATE_IDLE:
+               return "IDLE";
+       case USSD_STATE_ACTIVE:
+               return "ACTIVE";
+       case USSD_STATE_RESPONSE_SENT:
+               return "RESPONSE_SENT";
+       case USSD_STATE_USER_ACTION:
+               return "USER_ACTION";
+       }
+
+       return "????";
+}
+
+
+void ofono_ussd_notify(struct ofono_ussd *ussd, int status, int dcs,
+                       const unsigned char *data, int data_len)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *ussdstr = "USSD";
+       char *utf8_str = NULL;
+       const char *str;
+       const char sig[] = { DBUS_TYPE_STRING, 0 };
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter variant;
+
+       DBG("status: %d %s, state: %d %s",
+               status, ussd_status_name(status),
+               ussd->state, ussd_state_name(ussd->state));
+
+       if (ussd->req &&
+                       (status == OFONO_USSD_STATUS_NOTIFY ||
+                       status == OFONO_USSD_STATUS_TERMINATED ||
+                       status == OFONO_USSD_STATUS_TIMED_OUT ||
+                       status == OFONO_USSD_STATUS_NOT_SUPPORTED)) {
+               ussd_request_finish(ussd, ussd_status_to_failure_code(status),
+                                       dcs, data, data_len);
+
+               ussd_change_state(ussd, USSD_STATE_IDLE);
+               return;
+       }
+
+       if (status == OFONO_USSD_STATUS_NOT_SUPPORTED) {
+               ussd_change_state(ussd, USSD_STATE_IDLE);
+
+               if (ussd->pending == NULL)
+                       return;
+
+               reply = __ofono_error_not_supported(ussd->pending);
+               goto out;
+       }
+
+       if (status == OFONO_USSD_STATUS_TIMED_OUT) {
+               ussd_change_state(ussd, USSD_STATE_IDLE);
+
+               if (ussd->pending == NULL)
+                       return;
+
+               reply = __ofono_error_timed_out(ussd->pending);
+               goto out;
+       }
+
+       if (data && data_len > 0)
+               utf8_str = ussd_decode(dcs, data_len, data);
+
+       str = utf8_str;
+
+       /* TODO: Rework this in the Agent framework */
+       if (ussd->state == USSD_STATE_ACTIVE) {
+
+               reply = dbus_message_new_method_return(ussd->pending);
+
+               if (str == NULL)
+                       str = "";
+
+               dbus_message_iter_init_append(reply, &iter);
+
+               dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+                                               &ussdstr);
+
+               dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
+                                                       &variant);
+
+               dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING,
+                                               &str);
+
+               dbus_message_iter_close_container(&iter, &variant);
+
+               if (status == OFONO_USSD_STATUS_ACTION_REQUIRED)
+                       ussd_change_state(ussd, USSD_STATE_USER_ACTION);
+               else
+                       ussd_change_state(ussd, USSD_STATE_IDLE);
+
+       } else if (ussd->state == USSD_STATE_RESPONSE_SENT) {
+               reply = dbus_message_new_method_return(ussd->pending);
+
+               if (str == NULL)
+                       str = "";
+
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
+                                               DBUS_TYPE_INVALID);
+
+               if (status == OFONO_USSD_STATUS_ACTION_REQUIRED)
+                       ussd_change_state(ussd, USSD_STATE_USER_ACTION);
+               else
+                       ussd_change_state(ussd, USSD_STATE_IDLE);
+       } else if (ussd->state == USSD_STATE_IDLE) {
+               const char *signal_name;
+               const char *path = __ofono_atom_get_path(ussd->atom);
+               int new_state;
+
+               if (status == OFONO_USSD_STATUS_ACTION_REQUIRED) {
+                       new_state = USSD_STATE_USER_ACTION;
+                       signal_name = "RequestReceived";
+               } else {
+                       new_state = USSD_STATE_IDLE;
+                       signal_name = "NotificationReceived";
+               }
+
+               if (str == NULL)
+                       str = "";
+
+               g_dbus_emit_signal(conn, path,
+                       OFONO_SUPPLEMENTARY_SERVICES_INTERFACE, signal_name,
+                               DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID);
+
+               ussd_change_state(ussd, new_state);
+               goto free;
+       } else {
+               ofono_error("Received an unsolicited USSD but can't handle.");
+               DBG("USSD is: status: %d, %s", status, str);
+
+               goto free;
+       }
+
+out:
+       g_dbus_send_message(conn, reply);
+
+       dbus_message_unref(ussd->pending);
+       ussd->pending = NULL;
+
+free:
+       g_free(utf8_str);
+}
+
+static void ussd_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_ussd *ussd = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               DBG("ussd request failed with error: %s",
+                               telephony_error_to_str(error));
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+               ussd_change_state(ussd, USSD_STATE_ACTIVE);
+               return;
+       }
+
+       if (ussd->pending == NULL)
+               return;
+
+       reply = __ofono_error_failed(ussd->pending);
+       __ofono_dbus_pending_reply(&ussd->pending, reply);
+}
+
+static DBusMessage *ussd_initiate(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_ussd *ussd = data;
+       struct ofono_modem *modem = __ofono_atom_get_modem(ussd->atom);
+       struct ofono_voicecall *vc;
+       gboolean call_in_progress;
+       const char *str;
+       int dcs = 0x0f;
+       unsigned char buf[160];
+       long num_packed;
+
+       if (__ofono_ussd_is_busy(ussd))
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (strlen(str) == 0)
+               return __ofono_error_invalid_format(msg);
+
+       DBG("checking if this is a recognized control string");
+       if (recognized_control_string(ussd, str, msg))
+               return NULL;
+
+       vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem);
+       if (vc)
+               call_in_progress = __ofono_voicecall_is_busy(vc,
+                                       OFONO_VOICECALL_INTERACTION_NONE);
+       else
+               call_in_progress = FALSE;
+
+       DBG("No.., checking if this is a USSD string");
+       if (!valid_ussd_string(str, call_in_progress))
+               return __ofono_error_invalid_format(msg);
+
+       if (!ussd_encode(str, &num_packed, buf))
+               return __ofono_error_invalid_format(msg);
+
+       if (ussd->driver->request == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       DBG("OK, running USSD request");
+
+       ussd->pending = dbus_message_ref(msg);
+
+       ussd->driver->request(ussd, dcs, buf, num_packed, ussd_callback, ussd);
+
+       return NULL;
+}
+
+static void ussd_response_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_ussd *ussd = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               DBG("ussd response failed with error: %s",
+                               telephony_error_to_str(error));
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+               ussd_change_state(ussd, USSD_STATE_RESPONSE_SENT);
+               return;
+       }
+
+       if (ussd->pending == NULL)
+               return;
+
+       reply = __ofono_error_failed(ussd->pending);
+       __ofono_dbus_pending_reply(&ussd->pending, reply);
+}
+
+static DBusMessage *ussd_respond(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_ussd *ussd = data;
+       const char *str;
+       int dcs = 0x0f;
+       unsigned char buf[160];
+       long num_packed;
+
+       if (ussd->pending)
+               return __ofono_error_busy(msg);
+
+       if (ussd->state != USSD_STATE_USER_ACTION)
+               return __ofono_error_not_active(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (strlen(str) == 0)
+               return __ofono_error_invalid_format(msg);
+
+       if (!ussd_encode(str, &num_packed, buf))
+               return __ofono_error_invalid_format(msg);
+
+       if (ussd->driver->request == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       ussd->pending = dbus_message_ref(msg);
+
+       ussd->driver->request(ussd, dcs, buf, num_packed,
+                               ussd_response_callback, ussd);
+
+       return NULL;
+}
+
+static void ussd_cancel_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_ussd *ussd = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("ussd cancel failed with error: %s",
+                               telephony_error_to_str(error));
+
+               reply = __ofono_error_failed(ussd->cancel);
+               __ofono_dbus_pending_reply(&ussd->cancel, reply);
+
+               return;
+       }
+
+       if (ussd->pending) {
+               reply = __ofono_error_canceled(ussd->pending);
+               __ofono_dbus_pending_reply(&ussd->pending, reply);
+       }
+
+       reply = dbus_message_new_method_return(ussd->cancel);
+       __ofono_dbus_pending_reply(&ussd->cancel, reply);
+
+       if (ussd->req)
+               ussd_request_finish(ussd, -ECANCELED, 0, NULL, 0);
+
+       ussd_change_state(ussd, USSD_STATE_IDLE);
+}
+
+static DBusMessage *ussd_cancel(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct ofono_ussd *ussd = data;
+
+       if (ussd->state == USSD_STATE_IDLE)
+               return __ofono_error_not_active(msg);
+
+       /* We have called Respond() but not returned from its callback yet */
+       if (ussd->state == USSD_STATE_USER_ACTION && ussd->pending)
+               return __ofono_error_busy(msg);
+
+       if (ussd->cancel)
+               return __ofono_error_busy(msg);
+
+       if (ussd->driver->cancel == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       ussd->cancel = dbus_message_ref(msg);
+
+       ussd->driver->cancel(ussd, ussd_cancel_callback, ussd);
+
+       return NULL;
+}
+
+static DBusMessage *ussd_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_ussd *ussd = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *value;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       value = ussd_get_state_string(ussd);
+       ofono_dbus_dict_append(&dict, "State", DBUS_TYPE_STRING, &value);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static GDBusMethodTable ussd_methods[] = {
+       { "Initiate",           "s",    "sv",           ussd_initiate,
+                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "Respond",            "s",    "s",            ussd_respond,
+                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "Cancel",             "",     "",             ussd_cancel,
+                                       G_DBUS_METHOD_FLAG_ASYNC },
+       { "GetProperties",      "",     "a{sv}",        ussd_get_properties,
+                                       0 },
+       { }
+};
+
+static GDBusSignalTable ussd_signals[] = {
+       { "NotificationReceived",       "s" },
+       { "RequestReceived",            "s" },
+       { "PropertyChanged",            "sv" },
+       { }
+};
+
+int ofono_ussd_driver_register(const struct ofono_ussd_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_ussd_driver_unregister(const struct ofono_ussd_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void ussd_unregister(struct ofono_atom *atom)
+{
+       struct ofono_ussd *ussd = __ofono_atom_get_data(atom);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+
+       g_slist_foreach(ussd->ss_control_list, (GFunc) ssc_entry_destroy, NULL);
+       g_slist_free(ussd->ss_control_list);
+       ussd->ss_control_list = NULL;
+
+       g_slist_foreach(ussd->ss_passwd_list, (GFunc) ssc_entry_destroy, NULL);
+       g_slist_free(ussd->ss_passwd_list);
+       ussd->ss_passwd_list = NULL;
+
+       ofono_modem_remove_interface(modem,
+                                       OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
+}
+
+static void ussd_remove(struct ofono_atom *atom)
+{
+       struct ofono_ussd *ussd = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (ussd == NULL)
+               return;
+
+       if (ussd->driver && ussd->driver->remove)
+               ussd->driver->remove(ussd);
+
+       g_free(ussd);
+}
+
+struct ofono_ussd *ofono_ussd_create(struct ofono_modem *modem,
+                                       unsigned int vendor,
+                                       const char *driver,
+                                       void *data)
+{
+       struct ofono_ussd *ussd;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       ussd = g_try_new0(struct ofono_ussd, 1);
+
+       if (ussd == NULL)
+               return NULL;
+
+       ussd->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_USSD,
+                                               ussd_remove, ussd);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_ussd_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(ussd, vendor, data) < 0)
+                       continue;
+
+               ussd->driver = drv;
+               break;
+       }
+
+       return ussd;
+}
+
+void ofono_ussd_register(struct ofono_ussd *ussd)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(ussd->atom);
+       const char *path = __ofono_atom_get_path(ussd->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_SUPPLEMENTARY_SERVICES_INTERFACE,
+                                       ussd_methods, ussd_signals, NULL,
+                                       ussd, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem,
+                               OFONO_SUPPLEMENTARY_SERVICES_INTERFACE);
+
+       __ofono_atom_register(ussd->atom, ussd_unregister);
+}
+
+void ofono_ussd_remove(struct ofono_ussd *ussd)
+{
+       __ofono_atom_free(ussd->atom);
+}
+
+void ofono_ussd_set_data(struct ofono_ussd *ussd, void *data)
+{
+       ussd->driver_data = data;
+}
+
+void *ofono_ussd_get_data(struct ofono_ussd *ussd)
+{
+       return ussd->driver_data;
+}
+
+static void ussd_request_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_ussd *ussd = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               ussd_request_finish(ussd, -EINVAL, 0, NULL, 0);
+       else
+               ussd_change_state(ussd, USSD_STATE_ACTIVE);
+}
+
+int __ofono_ussd_initiate(struct ofono_ussd *ussd, int dcs,
+                               const unsigned char *pdu, int len,
+                               ofono_ussd_request_cb_t cb, void *user_data)
+{
+       struct ussd_request *req;
+
+       if (ussd->driver->request == NULL)
+               return -ENOSYS;
+
+       if (__ofono_ussd_is_busy(ussd))
+               return -EBUSY;
+
+       req = g_try_new0(struct ussd_request, 1);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->cb = cb;
+       req->user_data = user_data;
+
+       ussd->req = req;
+
+       ussd->driver->request(ussd, dcs, pdu, len, ussd_request_callback, ussd);
+
+       return 0;
+}
+
+void __ofono_ussd_initiate_cancel(struct ofono_ussd *ussd)
+{
+       if (ussd->req == NULL || ussd->req->cb == NULL)
+               return;
+
+       ussd->req->cb = NULL;
+}
diff --git a/src/util.c b/src/util.c
new file mode 100644 (file)
index 0000000..aecc790
--- /dev/null
@@ -0,0 +1,1489 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "util.h"
+
+/*
+       Name:                   GSM 03.38 to Unicode
+       Unicode version:        3.0
+       Table version:          1.1
+       Table format:           Format A
+       Date:                   2000 May 30
+       Authors:                Ken Whistler
+                               Kent Karlsson
+                               Markus Kuhn
+
+       Copyright (c) 2000 Unicode, Inc.  All Rights reserved.
+
+       This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+       No claims are made as to fitness for any particular purpose.  No
+       warranties of any kind are expressed or implied.  The recipient
+       agrees to determine applicability of information provided.  If this
+       file has been provided on optical media by Unicode, Inc., the sole
+       remedy for any claim will be exchange of defective media within 90
+       days of receipt.
+
+       Unicode, Inc. hereby grants the right to freely use the information
+       supplied in this file in the creation of products supporting the
+       Unicode Standard, and to make copies of this file in any form for
+       internal or external distribution as long as this notice remains
+       attached.
+*/
+
+#define GUND                   0xFFFF
+
+#define UTF8_LENGTH(c) \
+       ((c) < 0x80 ? 1 : ((c) < 0x800 ? 2 : 3))
+
+#define TABLE_SIZE(t) \
+       (sizeof((t)) / sizeof(struct codepoint))
+
+struct codepoint {
+       unsigned short from;
+       unsigned short to;
+};
+
+struct conversion_table {
+       /* To unicode locking shift table */
+       const struct codepoint *locking_u;
+       unsigned int locking_len_u;
+
+       /* To unicode single shift table */
+       const struct codepoint *single_u;
+       unsigned int single_len_u;
+
+       /* To GSM locking shift table, fixed size */
+       const unsigned short *locking_g;
+
+       /* To GSM single shift table */
+       const struct codepoint *single_g;
+       unsigned int single_len_g;
+};
+
+/* GSM to Unicode extension table, for GSM sequences starting with 0x1B */
+static const struct codepoint def_ext_gsm[] = {
+       { 0x0A, 0x000C },               /* See NOTE 3 in 23.038 */
+       { 0x14, 0x005E },
+       { 0x1B, 0x0020 },               /* See NOTE 1 in 23.038 */
+       { 0x28, 0x007B },
+       { 0x29, 0x007D },
+       { 0x2F, 0x005C },
+       { 0x3C, 0x005B },
+       { 0x3D, 0x007E },
+       { 0x3E, 0x005D },
+       { 0x40, 0x007C },
+       { 0x65, 0x20AC }
+};
+
+static const struct codepoint def_ext_unicode[] = {
+       { 0x000C, 0x1B0A },
+       { 0x005B, 0x1B3C },
+       { 0x005C, 0x1B2F },
+       { 0x005D, 0x1B3E },
+       { 0x005E, 0x1B14 },
+       { 0x007B, 0x1B28 },
+       { 0x007C, 0x1B40 },
+       { 0x007D, 0x1B29 },
+       { 0x007E, 0x1B3D },
+       { 0x20AC, 0x1B65 }
+};
+
+/* Appendix A.2.1. in 3GPP TS23.038, V.8.2.0 */
+static const struct codepoint tur_ext_gsm[] = {
+       { 0x0A, 0x000C },               /* See NOTE 3 */
+       { 0x14, 0x005E },
+       { 0x1B, 0x0020 },               /* See NOTE 1 */
+       { 0x28, 0x007B },
+       { 0x29, 0x007D },
+       { 0x2F, 0x005C },
+       { 0x3C, 0x005B },
+       { 0x3D, 0x007E },
+       { 0x3E, 0x005D },
+       { 0x40, 0x007C },
+       { 0x47, 0x011E },
+       { 0x49, 0x0130 },
+       { 0x53, 0x015E },
+       { 0x63, 0x00E7 },
+       { 0x65, 0x20AC },
+       { 0x67, 0x011F },
+       { 0x69, 0x0131 },
+       { 0x73, 0x015F }
+};
+
+static const struct codepoint tur_ext_unicode[] = {
+       { 0x000C, 0x1B0A },
+       { 0x005B, 0x1B3C },
+       { 0x005C, 0x1B2F },
+       { 0x005D, 0x1B3E },
+       { 0x005E, 0x1B14 },
+       { 0x007B, 0x1B28 },
+       { 0x007C, 0x1B40 },
+       { 0x007D, 0x1B29 },
+       { 0x007E, 0x1B3D },
+       { 0x00E7, 0x1B63 },
+       { 0x011E, 0x1B47 },
+       { 0x011F, 0x1B67 },
+       { 0x0130, 0x1B49 },
+       { 0x0131, 0x1B69 },
+       { 0x015E, 0x1B53 },
+       { 0x015F, 0x1B73 },
+       { 0x20AC, 0x1B65 }
+};
+
+/* Appendix A.2.2. in 3GPP TS23.038 V.8.2.0*/
+static const struct codepoint spa_ext_gsm[] = {
+       { 0x09, 0x00E7 },
+       { 0x0A, 0x000C },               /* See NOTE 3 */
+       { 0x14, 0x005E },
+       { 0x1B, 0x0020 },               /* See NOTE 1 */
+       { 0x28, 0x007B },
+       { 0x29, 0x007D },
+       { 0x2F, 0x005C },
+       { 0x3C, 0x005B },
+       { 0x3D, 0x007E },
+       { 0x3E, 0x005D },
+       { 0x40, 0x007C },
+       { 0x41, 0x00C1 },
+       { 0x49, 0x00CD },
+       { 0x4F, 0x00D3 },
+       { 0x55, 0x00DA },
+       { 0x61, 0x00E1 },
+       { 0x65, 0x20AC },
+       { 0x69, 0x00ED },
+       { 0x6F, 0x00F3 },
+       { 0x75, 0x00FA }
+};
+
+static const struct codepoint spa_ext_unicode[] = {
+       { 0x000C, 0x1B0A },
+       { 0x005B, 0x1B3C },
+       { 0x005C, 0x1B2F },
+       { 0x005D, 0x1B3E },
+       { 0x005E, 0x1B14 },
+       { 0x007B, 0x1B28 },
+       { 0x007C, 0x1B40 },
+       { 0x007D, 0x1B29 },
+       { 0x007E, 0x1B3D },
+       { 0x00C1, 0x1B41 },
+       { 0x00CD, 0x1B49 },
+       { 0x00D3, 0x1B4F },
+       { 0x00DA, 0x1B55 },
+       { 0x00E1, 0x1B61 },
+       { 0x00E7, 0x1B09 },
+       { 0x00ED, 0x1B69 },
+       { 0x00F3, 0x1B6F },
+       { 0x00FA, 0x1B75 },
+       { 0x20AC, 0x1B65 }
+};
+
+/* Appendix A.2.3. in 3GPP TS23.038 V.8.2.0 */
+static const struct codepoint por_ext_gsm[] = {
+       { 0x05, 0x00EA },
+       { 0x09, 0x00E7 },
+       { 0x0A, 0x000C },               /* See NOTE 3 */
+       { 0x0B, 0x00D4 },
+       { 0x0C, 0x00F4 },
+       { 0x0E, 0x00C1 },
+       { 0x0F, 0x00E1 },
+       { 0x12, 0x03A6 },
+       { 0x13, 0x0393 },
+       { 0x14, 0x005E },
+       { 0x15, 0x03A9 },
+       { 0x16, 0x03A0 },
+       { 0x17, 0x03A8 },
+       { 0x18, 0x03A3 },
+       { 0x19, 0x0398 },
+       { 0x1B, 0x0020 },               /* See NOTE 1 */
+       { 0x1F, 0x00CA },
+       { 0x28, 0x007B },
+       { 0x29, 0x007D },
+       { 0x2F, 0x005C },
+       { 0x3C, 0x005B },
+       { 0x3D, 0x007E },
+       { 0x3E, 0x005D },
+       { 0x40, 0x007C },
+       { 0x41, 0x00C0 },
+       { 0x49, 0x00CD },
+       { 0x4F, 0x00D3 },
+       { 0x55, 0x00DA },
+       { 0x5B, 0x00C3 },
+       { 0x5C, 0x00D5 },
+       { 0x61, 0x00C2 },
+       { 0x65, 0x20AC },
+       { 0x69, 0x00ED },
+       { 0x6F, 0x00F3 },
+       { 0x75, 0x00FA },
+       { 0x7B, 0x00E3 },
+       { 0x7C, 0x00F5 },
+       { 0x7F, 0x00E2 }
+};
+
+static const struct codepoint por_ext_unicode[] = {
+       { 0x000C, 0x1B0A },
+       { 0x005B, 0x1B3C },
+       { 0x005C, 0x1B2F },
+       { 0x005D, 0x1B3E },
+       { 0x005E, 0x1B14 },
+       { 0x007B, 0x1B28 },
+       { 0x007C, 0x1B40 },
+       { 0x007D, 0x1B29 },
+       { 0x007E, 0x1B3D },
+       { 0x00C0, 0x1B41 },
+       { 0x00C1, 0x1B0E },
+       { 0x00C2, 0x1B61 },
+       { 0x00C3, 0x1B5B },
+       { 0x00CA, 0x1B1F },
+       { 0x00CD, 0x1B49 },
+       { 0x00D3, 0x1B4F },
+       { 0x00D4, 0x1B0B },
+       { 0x00D5, 0x1B5C },
+       { 0x00DA, 0x1B55 },
+       { 0x00E1, 0x1B0F },
+       { 0x00E2, 0x1B7F },
+       { 0x00E3, 0x1B7B },
+       { 0x00E7, 0x1B09 },
+       { 0x00EA, 0x1B05 },
+       { 0x00ED, 0x1B69 },
+       { 0x00F3, 0x1B6F },
+       { 0x00F4, 0x1B0C },
+       { 0x00F5, 0x1B7C },
+       { 0x00FA, 0x1B75 },
+       { 0x0393, 0x1B13 },
+       { 0x0398, 0x1B19 },
+       { 0x03A0, 0x1B16 },
+       { 0x03A3, 0x1B18 },
+       { 0x03A6, 0x1B12 },
+       { 0x03A8, 0x1B17 },
+       { 0x03A9, 0x1B15 },
+       { 0x20AC, 0x1B65 }
+};
+
+/* Used for conversion of GSM to Unicode */
+static const unsigned short def_gsm[] = {
+       0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC,
+       0x00F2, 0x00C7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5,
+       0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
+       0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9,
+       0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+       0x00A1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005A, 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7,
+       0x00BF, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007A, 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0
+};
+
+/* Used for conversion of Unicode to GSM */
+static const struct codepoint def_unicode[] = {
+       { 0x000A, 0x0A }, { 0x000D, 0x0D }, { 0x0020, 0x20 }, { 0x0021, 0x21 },
+       { 0x0022, 0x22 }, { 0x0023, 0x23 }, { 0x0024, 0x02 }, { 0x0025, 0x25 },
+       { 0x0026, 0x26 }, { 0x0027, 0x27 }, { 0x0028, 0x28 }, { 0x0029, 0x29 },
+       { 0x002A, 0x2A }, { 0x002B, 0x2B }, { 0x002C, 0x2C }, { 0x002D, 0x2D },
+       { 0x002E, 0x2E }, { 0x002F, 0x2F }, { 0x0030, 0x30 }, { 0x0031, 0x31 },
+       { 0x0032, 0x32 }, { 0x0033, 0x33 }, { 0x0034, 0x34 }, { 0x0035, 0x35 },
+       { 0x0036, 0x36 }, { 0x0037, 0x37 }, { 0x0038, 0x38 }, { 0x0039, 0x39 },
+       { 0x003A, 0x3A }, { 0x003B, 0x3B }, { 0x003C, 0x3C }, { 0x003D, 0x3D },
+       { 0x003E, 0x3E }, { 0x003F, 0x3F }, { 0x0040, 0x00 }, { 0x0041, 0x41 },
+       { 0x0042, 0x42 }, { 0x0043, 0x43 }, { 0x0044, 0x44 }, { 0x0045, 0x45 },
+       { 0x0046, 0x46 }, { 0x0047, 0x47 }, { 0x0048, 0x48 }, { 0x0049, 0x49 },
+       { 0x004A, 0x4A }, { 0x004B, 0x4B }, { 0x004C, 0x4C }, { 0x004D, 0x4D },
+       { 0x004E, 0x4E }, { 0x004F, 0x4F }, { 0x0050, 0x50 }, { 0x0051, 0x51 },
+       { 0x0052, 0x52 }, { 0x0053, 0x53 }, { 0x0054, 0x54 }, { 0x0055, 0x55 },
+       { 0x0056, 0x56 }, { 0x0057, 0x57 }, { 0x0058, 0x58 }, { 0x0059, 0x59 },
+       { 0x005A, 0x5A }, { 0x005F, 0x11 }, { 0x0061, 0x61 }, { 0x0062, 0x62 },
+       { 0x0063, 0x63 }, { 0x0064, 0x64 }, { 0x0065, 0x65 }, { 0x0066, 0x66 },
+       { 0x0067, 0x67 }, { 0x0068, 0x68 }, { 0x0069, 0x69 }, { 0x006A, 0x6A },
+       { 0x006B, 0x6B }, { 0x006C, 0x6C }, { 0x006D, 0x6D }, { 0x006E, 0x6E },
+       { 0x006F, 0x6F }, { 0x0070, 0x70 }, { 0x0071, 0x71 }, { 0x0072, 0x72 },
+       { 0x0073, 0x73 }, { 0x0074, 0x74 }, { 0x0075, 0x75 }, { 0x0076, 0x76 },
+       { 0x0077, 0x77 }, { 0x0078, 0x78 }, { 0x0079, 0x79 }, { 0x007A, 0x7A },
+       { 0x00A0, 0x20 }, { 0x00A1, 0x40 }, { 0x00A3, 0x01 }, { 0x00A4, 0x24 },
+       { 0x00A5, 0x03 }, { 0x00A7, 0x5F }, { 0x00BF, 0x60 }, { 0x00C4, 0x5B },
+       { 0x00C5, 0x0E }, { 0x00C6, 0x1C }, { 0x00C7, 0x09 }, { 0x00C9, 0x1F },
+       { 0x00D1, 0x5D }, { 0x00D6, 0x5C }, { 0x00D8, 0x0B }, { 0x00DC, 0x5E },
+       { 0x00DF, 0x1E }, { 0x00E0, 0x7F }, { 0x00E4, 0x7B }, { 0x00E5, 0x0F },
+       { 0x00E6, 0x1D }, { 0x00E8, 0x04 }, { 0x00E9, 0x05 }, { 0x00EC, 0x07 },
+       { 0x00F1, 0x7D }, { 0x00F2, 0x08 }, { 0x00F6, 0x7C }, { 0x00F8, 0x0C },
+       { 0x00F9, 0x06 }, { 0x00FC, 0x7E }, { 0x0393, 0x13 }, { 0x0394, 0x10 },
+       { 0x0398, 0x19 }, { 0x039B, 0x14 }, { 0x039E, 0x1A }, { 0x03A0, 0x16 },
+       { 0x03A3, 0x18 }, { 0x03A6, 0x12 }, { 0x03A8, 0x17 }, { 0x03A9, 0x15 }
+};
+
+/* Appendix A.3.1 in 3GPP TS23.038 */
+static const unsigned short tur_gsm[] = {
+       0x0040, 0x00A3, 0x0024, 0x00A5, 0x20AC, 0x00E9, 0x00F9, 0x0131,
+       0x00F2, 0x00C7, 0x000A, 0x011E, 0x011F, 0x000D, 0x00C5, 0x00E5,
+       0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
+       0x03A3, 0x0398, 0x039E, 0x00A0, 0x015E, 0x015F, 0x00DF, 0x00C9,
+       0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+       0x0130, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005A, 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7,
+       0x00E7, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007A, 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0
+};
+
+static const struct codepoint tur_unicode[] = {
+       { 0x000A, 0x0A }, { 0x000D, 0x0D }, { 0x0020, 0x20 }, { 0x0021, 0x21 },
+       { 0x0022, 0x22 }, { 0x0023, 0x23 }, { 0x0024, 0x02 }, { 0x0025, 0x25 },
+       { 0x0026, 0x26 }, { 0x0027, 0x27 }, { 0x0028, 0x28 }, { 0x0029, 0x29 },
+       { 0x002A, 0x2A }, { 0x002B, 0x2B }, { 0x002C, 0x2C }, { 0x002D, 0x2D },
+       { 0x002E, 0x2E }, { 0x002F, 0x2F }, { 0x0030, 0x30 }, { 0x0031, 0x31 },
+       { 0x0032, 0x32 }, { 0x0033, 0x33 }, { 0x0034, 0x34 }, { 0x0035, 0x35 },
+       { 0x0036, 0x36 }, { 0x0037, 0x37 }, { 0x0038, 0x38 }, { 0x0039, 0x39 },
+       { 0x003A, 0x3A }, { 0x003B, 0x3B }, { 0x003C, 0x3C }, { 0x003D, 0x3D },
+       { 0x003E, 0x3E }, { 0x003F, 0x3F }, { 0x0040, 0x00 }, { 0x0041, 0x41 },
+       { 0x0042, 0x42 }, { 0x0043, 0x43 }, { 0x0044, 0x44 }, { 0x0045, 0x45 },
+       { 0x0046, 0x46 }, { 0x0047, 0x47 }, { 0x0048, 0x48 }, { 0x0049, 0x49 },
+       { 0x004A, 0x4A }, { 0x004B, 0x4B }, { 0x004C, 0x4C }, { 0x004D, 0x4D },
+       { 0x004E, 0x4E }, { 0x004F, 0x4F }, { 0x0050, 0x50 }, { 0x0051, 0x51 },
+       { 0x0052, 0x52 }, { 0x0053, 0x53 }, { 0x0054, 0x54 }, { 0x0055, 0x55 },
+       { 0x0056, 0x56 }, { 0x0057, 0x57 }, { 0x0058, 0x58 }, { 0x0059, 0x59 },
+       { 0x005A, 0x5A }, { 0x005F, 0x11 }, { 0x0061, 0x61 }, { 0x0062, 0x62 },
+       { 0x0063, 0x63 }, { 0x0064, 0x64 }, { 0x0065, 0x65 }, { 0x0066, 0x66 },
+       { 0x0067, 0x67 }, { 0x0068, 0x68 }, { 0x0069, 0x69 }, { 0x006A, 0x6A },
+       { 0x006B, 0x6B }, { 0x006C, 0x6C }, { 0x006D, 0x6D }, { 0x006E, 0x6E },
+       { 0x006F, 0x6F }, { 0x0070, 0x70 }, { 0x0071, 0x71 }, { 0x0072, 0x72 },
+       { 0x0073, 0x73 }, { 0x0074, 0x74 }, { 0x0075, 0x75 }, { 0x0076, 0x76 },
+       { 0x0077, 0x77 }, { 0x0078, 0x78 }, { 0x0079, 0x79 }, { 0x007A, 0x7A },
+       { 0x00A0, 0x20 }, { 0x00A3, 0x01 }, { 0x00A4, 0x24 }, { 0x00A5, 0x03 },
+       { 0x00A7, 0x5F }, { 0x00C4, 0x5B }, { 0x00C5, 0x0E }, { 0x00C7, 0x09 },
+       { 0x00C9, 0x1F }, { 0x00D1, 0x5D }, { 0x00D6, 0x5C }, { 0x00DC, 0x5E },
+       { 0x00DF, 0x1E }, { 0x00E0, 0x7F }, { 0x00E4, 0x7B }, { 0x00E5, 0x0F },
+       { 0x00E7, 0x60 }, { 0x00E9, 0x05 }, { 0x00F1, 0x7D }, { 0x00F2, 0x08 },
+       { 0x00F6, 0x7C }, { 0x00F9, 0x06 }, { 0x00FC, 0x7E }, { 0x011E, 0x0B },
+       { 0x011F, 0x0C }, { 0x0130, 0x40 }, { 0x0131, 0x07 }, { 0x015E, 0x1C },
+       { 0x015F, 0x1D }, { 0x0393, 0x13 }, { 0x0394, 0x10 }, { 0x0398, 0x19 },
+       { 0x039B, 0x14 }, { 0x039E, 0x1A }, { 0x03A0, 0x16 }, { 0x03A3, 0x18 },
+       { 0x03A6, 0x12 }, { 0x03A8, 0x17 }, { 0x03A9, 0x15 }, { 0x20AC, 0x04 }
+};
+
+/* Appendix A.3.2 in 3GPP TS23.038 */
+static const unsigned short por_gsm[] = {
+       0x0040, 0x00A3, 0x0024, 0x00A5, 0x00EA, 0x00E9, 0x00FA, 0x00ED,
+       0x00F3, 0x00E7, 0x000A, 0x00D4, 0x00F4, 0x000D, 0x00C1, 0x00E1,
+       0x0394, 0x005F, 0x00AA, 0x00C7, 0x00C0, 0x221E, 0x005E, 0x005C,
+       0x20ac, 0x00D3, 0x007C, 0x00A0, 0x00C2, 0x00E2, 0x00CA, 0x00C9,
+       0x0020, 0x0021, 0x0022, 0x0023, 0x00BA, 0x0025, 0x0026, 0x0027,
+       0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+       0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+       0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+       0x00CD, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+       0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+       0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+       0x0058, 0x0059, 0x005A, 0x00C3, 0x00D5, 0x00DA, 0x00DC, 0x00A7,
+       0x007E, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+       0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+       0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+       0x0078, 0x0079, 0x007A, 0x00E3, 0x00F5, 0x0060, 0x00FC, 0x00E0
+};
+
+static const struct codepoint por_unicode[] = {
+       { 0x000A, 0x0A }, { 0x000D, 0x0D }, { 0x0020, 0x20 }, { 0x0021, 0x21 },
+       { 0x0022, 0x22 }, { 0x0023, 0x23 }, { 0x0024, 0x02 }, { 0x0025, 0x25 },
+       { 0x0026, 0x26 }, { 0x0027, 0x27 }, { 0x0028, 0x28 }, { 0x0029, 0x29 },
+       { 0x002A, 0x2A }, { 0x002B, 0x2B }, { 0x002C, 0x2C }, { 0x002D, 0x2D },
+       { 0x002E, 0x2E }, { 0x002F, 0x2F }, { 0x0030, 0x30 }, { 0x0031, 0x31 },
+       { 0x0032, 0x32 }, { 0x0033, 0x33 }, { 0x0034, 0x34 }, { 0x0035, 0x35 },
+       { 0x0036, 0x36 }, { 0x0037, 0x37 }, { 0x0038, 0x38 }, { 0x0039, 0x39 },
+       { 0x003A, 0x3A }, { 0x003B, 0x3B }, { 0x003C, 0x3C }, { 0x003D, 0x3D },
+       { 0x003E, 0x3E }, { 0x003F, 0x3F }, { 0x0040, 0x00 }, { 0x0041, 0x41 },
+       { 0x0042, 0x42 }, { 0x0043, 0x43 }, { 0x0044, 0x44 }, { 0x0045, 0x45 },
+       { 0x0046, 0x46 }, { 0x0047, 0x47 }, { 0x0048, 0x48 }, { 0x0049, 0x49 },
+       { 0x004A, 0x4A }, { 0x004B, 0x4B }, { 0x004C, 0x4C }, { 0x004D, 0x4D },
+       { 0x004E, 0x4E }, { 0x004F, 0x4F }, { 0x0050, 0x50 }, { 0x0051, 0x51 },
+       { 0x0052, 0x52 }, { 0x0053, 0x53 }, { 0x0054, 0x54 }, { 0x0055, 0x55 },
+       { 0x0056, 0x56 }, { 0x0057, 0x57 }, { 0x0058, 0x58 }, { 0x0059, 0x59 },
+       { 0x005A, 0x5A }, { 0x005C, 0x17 }, { 0x005E, 0x16 }, { 0x005F, 0x11 },
+       { 0x0060, 0x7D }, { 0x0061, 0x61 }, { 0x0062, 0x62 }, { 0x0063, 0x63 },
+       { 0x0064, 0x64 }, { 0x0065, 0x65 }, { 0x0066, 0x66 }, { 0x0067, 0x67 },
+       { 0x0068, 0x68 }, { 0x0069, 0x69 }, { 0x006A, 0x6A }, { 0x006B, 0x6B },
+       { 0x006C, 0x6C }, { 0x006D, 0x6D }, { 0x006E, 0x6E }, { 0x006F, 0x6F },
+       { 0x0070, 0x70 }, { 0x0071, 0x71 }, { 0x0072, 0x72 }, { 0x0073, 0x73 },
+       { 0x0074, 0x74 }, { 0x0075, 0x75 }, { 0x0076, 0x76 }, { 0x0077, 0x77 },
+       { 0x0078, 0x78 }, { 0x0079, 0x79 }, { 0x007A, 0x7A }, { 0x007C, 0x1A },
+       { 0x007E, 0x60 }, { 0x00A0, 0x20 }, { 0x00A3, 0x01 }, { 0x00A5, 0x03 },
+       { 0x00A7, 0x5F }, { 0x00AA, 0x12 }, { 0x00BA, 0x24 }, { 0x00C0, 0x14 },
+       { 0x00C1, 0x0E }, { 0x00C2, 0x1C }, { 0x00C3, 0x5B }, { 0x00C7, 0x13 },
+       { 0x00C9, 0x1F }, { 0x00CA, 0x1E }, { 0x00CD, 0x40 }, { 0x00D3, 0x19 },
+       { 0x00D4, 0x0B }, { 0x00D5, 0x5C }, { 0x00DA, 0x5D }, { 0x00DC, 0x5E },
+       { 0x00E0, 0x7F }, { 0x00E1, 0x0F }, { 0x00E2, 0x1D }, { 0x00E3, 0x7B },
+       { 0x00E7, 0x09 }, { 0x00E9, 0x05 }, { 0x00EA, 0x04 }, { 0x00ED, 0x07 },
+       { 0x00F3, 0x08 }, { 0x00F4, 0x0C }, { 0x00F5, 0x7C }, { 0x00FA, 0x06 },
+       { 0x00FC, 0x7E }, { 0x0394, 0x10 }, { 0x20AC, 0x18 }, { 0x221E, 0x15 }
+};
+
+static int compare_codepoints(const void *a, const void *b)
+{
+       const struct codepoint *ca = (const struct codepoint *) a;
+       const struct codepoint *cb = (const struct codepoint *) b;
+
+       return (ca->from > cb->from) - (ca->from < cb->from);
+}
+
+static unsigned short codepoint_lookup(struct codepoint *key,
+                                       const struct codepoint *table,
+                                       unsigned int len)
+{
+       struct codepoint *result = NULL;
+
+       result = bsearch(key, table, len, sizeof(struct codepoint),
+                               compare_codepoints);
+
+       return result ? result->to : GUND;
+}
+
+static unsigned short gsm_locking_shift_lookup(struct conversion_table *t,
+                                               unsigned char k)
+{
+       return t->locking_g[k];
+}
+
+static unsigned short gsm_single_shift_lookup(struct conversion_table *t,
+                                               unsigned char k)
+{
+       struct codepoint key = { k, 0 };
+       return codepoint_lookup(&key, t->single_g, t->single_len_g);
+}
+
+static unsigned short unicode_locking_shift_lookup(struct conversion_table *t,
+                                                       unsigned short k)
+{
+       struct codepoint key = { k, 0 };
+       return codepoint_lookup(&key, t->locking_u, t->locking_len_u);
+}
+
+static unsigned short unicode_single_shift_lookup(struct conversion_table *t,
+                                                       unsigned short k)
+{
+       struct codepoint key = { k, 0 };
+       return codepoint_lookup(&key, t->single_u, t->single_len_u);
+}
+
+static gboolean populate_locking_shift(struct conversion_table *t,
+                                       enum gsm_dialect lang)
+{
+       switch (lang) {
+       case GSM_DIALECT_DEFAULT:
+       case GSM_DIALECT_SPANISH:
+               t->locking_g = def_gsm;
+               t->locking_u = def_unicode;
+               t->locking_len_u = TABLE_SIZE(def_unicode);
+               return TRUE;
+
+       case GSM_DIALECT_TURKISH:
+               t->locking_g = tur_gsm;
+               t->locking_u = tur_unicode;
+               t->locking_len_u = TABLE_SIZE(tur_unicode);
+               return TRUE;
+
+       case GSM_DIALECT_PORTUGUESE:
+               t->locking_g = por_gsm;
+               t->locking_u = por_unicode;
+               t->locking_len_u = TABLE_SIZE(por_unicode);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean populate_single_shift(struct conversion_table *t,
+                                       enum gsm_dialect lang)
+{
+       switch (lang) {
+       case GSM_DIALECT_DEFAULT:
+               t->single_g = def_ext_gsm;
+               t->single_len_g = TABLE_SIZE(def_ext_gsm);
+               t->single_u = def_ext_unicode;
+               t->single_len_u = TABLE_SIZE(def_ext_unicode);
+               return TRUE;
+
+       case GSM_DIALECT_TURKISH:
+               t->single_g = tur_ext_gsm;
+               t->single_len_g = TABLE_SIZE(tur_ext_gsm);
+               t->single_u = tur_ext_unicode;
+               t->single_len_u = TABLE_SIZE(tur_ext_unicode);
+               return TRUE;
+
+       case GSM_DIALECT_SPANISH:
+               t->single_g = spa_ext_gsm;
+               t->single_len_g = TABLE_SIZE(spa_ext_gsm);
+               t->single_u = spa_ext_unicode;
+               t->single_len_u = TABLE_SIZE(spa_ext_unicode);
+               return TRUE;
+
+       case GSM_DIALECT_PORTUGUESE:
+               t->single_g = por_ext_gsm;
+               t->single_len_g = TABLE_SIZE(por_ext_gsm);
+               t->single_u = por_ext_unicode;
+               t->single_len_u = TABLE_SIZE(por_ext_unicode);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean conversion_table_init(struct conversion_table *t,
+                                       enum gsm_dialect locking,
+                                       enum gsm_dialect single)
+{
+       memset(t, 0, sizeof(struct conversion_table));
+
+       return populate_locking_shift(t, locking) &&
+                       populate_single_shift(t, single);
+}
+
+/*!
+ * Converts text coded using GSM codec into UTF8 encoded text, using
+ * the given language identifiers for single shift and locking shift
+ * tables.  If len is less than 0, and terminator character is given,
+ * the length is computed automatically.
+ *
+ * Returns newly-allocated UTF8 encoded string or NULL if the conversion
+ * could not be performed.  Returns the number of bytes read from the
+ * GSM encoded string in items_read (if not NULL), not including the
+ * terminator character. Returns the number of bytes written into the UTF8
+ * encoded string in items_written (if not NULL) not including the terminal
+ * '\0' character.  The caller is responsible for freeing the returned value.
+ */
+char *convert_gsm_to_utf8_with_lang(const unsigned char *text, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator,
+                                       enum gsm_dialect locking_lang,
+                                       enum gsm_dialect single_lang)
+{
+       char *res = NULL;
+       char *out;
+       long i = 0;
+       long res_length;
+
+       struct conversion_table t;
+
+       if (conversion_table_init(&t, locking_lang, single_lang) == FALSE)
+               return NULL;
+
+       if (len < 0 && !terminator)
+               goto error;
+
+       if (len < 0) {
+               i = 0;
+
+               while (text[i] != terminator)
+                       i++;
+
+               len = i;
+       }
+
+       for (i = 0, res_length = 0; i < len; i++) {
+               unsigned short c;
+
+               if (text[i] > 0x7f)
+                       goto error;
+
+               if (text[i] == 0x1b) {
+                       ++i;
+                       if (i >= len)
+                               goto error;
+
+                       c = gsm_single_shift_lookup(&t, text[i]);
+
+                       if (c == GUND)
+                               goto error;
+               } else {
+                       c = gsm_locking_shift_lookup(&t, text[i]);
+               }
+
+               res_length += UTF8_LENGTH(c);
+       }
+
+       res = g_try_malloc(res_length + 1);
+       if (res == NULL)
+               goto error;
+
+       out = res;
+
+       i = 0;
+       while (out < res + res_length) {
+               unsigned short c;
+
+               if (text[i] == 0x1b)
+                       c = gsm_single_shift_lookup(&t, text[++i]);
+               else
+                       c = gsm_locking_shift_lookup(&t, text[i]);
+
+               out += g_unichar_to_utf8(c, out);
+
+               ++i;
+       }
+
+       *out = '\0';
+
+       if (items_written)
+               *items_written = out - res;
+
+error:
+       if (items_read)
+               *items_read = i;
+
+       return res;
+}
+
+char *convert_gsm_to_utf8(const unsigned char *text, long len,
+                               long *items_read, long *items_written,
+                               unsigned char terminator)
+{
+       return convert_gsm_to_utf8_with_lang(text, len, items_read,
+                                               items_written,
+                                               terminator,
+                                               GSM_DIALECT_DEFAULT,
+                                               GSM_DIALECT_DEFAULT);
+}
+
+/*!
+ * Converts UTF-8 encoded text to GSM alphabet.  The result is unpacked,
+ * with the 7th bit always 0.  If terminator is not 0, a terminator character
+ * is appended to the result.  This should be in the range 0x80-0xf0
+ *
+ * Returns the encoded data or NULL if the data could not be encoded.  The
+ * data must be freed by the caller.  If items_read is not NULL, it contains
+ * the actual number of bytes read.  If items_written is not NULL, contains
+ * the number of bytes written.
+ */
+unsigned char *convert_utf8_to_gsm_with_lang(const char *text, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator,
+                                       enum gsm_dialect locking_lang,
+                                       enum gsm_dialect single_lang)
+{
+       struct conversion_table t;
+       long nchars = 0;
+       const char *in;
+       unsigned char *out;
+       unsigned char *res = NULL;
+       long res_len;
+       long i;
+
+       if (conversion_table_init(&t, locking_lang, single_lang) == FALSE)
+               return NULL;
+
+       in = text;
+       res_len = 0;
+
+       while ((len < 0 || text + len - in > 0) && *in) {
+               long max = len < 0 ? 6 : text + len - in;
+               gunichar c = g_utf8_get_char_validated(in, max);
+               unsigned short converted = GUND;
+
+               if (c & 0x80000000)
+                       goto err_out;
+
+               if (c > 0xffff)
+                       goto err_out;
+
+               converted = unicode_locking_shift_lookup(&t, c);
+
+               if (converted == GUND)
+                       converted = unicode_single_shift_lookup(&t, c);
+
+               if (converted == GUND)
+                       goto err_out;
+
+               if (converted & 0x1b00)
+                       res_len += 2;
+               else
+                       res_len += 1;
+
+               in = g_utf8_next_char(in);
+               nchars += 1;
+       }
+
+       res = g_try_malloc(res_len + (terminator ? 1 : 0));
+       if (res == NULL)
+               goto err_out;
+
+       in = text;
+       out = res;
+       for (i = 0; i < nchars; i++) {
+               unsigned short converted;
+
+               gunichar c = g_utf8_get_char(in);
+
+               converted = unicode_locking_shift_lookup(&t, c);
+
+               if (converted == GUND)
+                       converted = unicode_single_shift_lookup(&t, c);
+
+               if (converted & 0x1b00) {
+                       *out = 0x1b;
+                       ++out;
+               }
+
+               *out = converted;
+               ++out;
+
+               in = g_utf8_next_char(in);
+       }
+
+       if (terminator)
+               *out = terminator;
+
+       if (items_written)
+               *items_written = out - res;
+
+err_out:
+       if (items_read)
+               *items_read = in - text;
+
+       return res;
+}
+
+unsigned char *convert_utf8_to_gsm(const char *text, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator)
+{
+       return convert_utf8_to_gsm_with_lang(text, len, items_read,
+                                               items_written,
+                                               terminator,
+                                               GSM_DIALECT_DEFAULT,
+                                               GSM_DIALECT_DEFAULT);
+}
+
+/*!
+ * Converts UTF-8 encoded text to GSM alphabet. It finds an encoding
+ * that uses the minimum set of GSM dialects based on the hint given.
+ *
+ * It first attempts to use the default dialect's single shift and
+ * locking shift tables. It then tries with only the single shift
+ * table of the hinted dialect, and finally with both the single shift
+ * and locking shift tables of the hinted dialect.
+ *
+ * Returns the encoded data or NULL if no suitable encoding could be
+ * found. The data must be freed by the caller. If items_read is not
+ * NULL, it contains the actual number of bytes read. If items_written
+ * is not NULL, it contains the number of bytes written. If
+ * used_locking and used_single are not NULL, they will contain the
+ * dialects used for the locking shift and single shift tables.
+ */
+unsigned char *convert_utf8_to_gsm_best_lang(const char *utf8, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator,
+                                       enum gsm_dialect hint,
+                                       enum gsm_dialect *used_locking,
+                                       enum gsm_dialect *used_single)
+{
+       enum gsm_dialect locking = GSM_DIALECT_DEFAULT;
+       enum gsm_dialect single = GSM_DIALECT_DEFAULT;
+       unsigned char *encoded;
+
+       encoded = convert_utf8_to_gsm_with_lang(utf8, len, items_read,
+                                               items_written, terminator,
+                                               locking, single);
+       if (encoded != NULL)
+               goto out;
+
+       if (hint == GSM_DIALECT_DEFAULT)
+               return NULL;
+
+       single = hint;
+       encoded = convert_utf8_to_gsm_with_lang(utf8, len, items_read,
+                                               items_written, terminator,
+                                               locking, single);
+       if (encoded != NULL)
+               goto out;
+
+       /* Spanish dialect uses the default locking shift table */
+       if (hint == GSM_DIALECT_SPANISH)
+               return NULL;
+
+       locking = hint;
+       encoded = convert_utf8_to_gsm_with_lang(utf8, len, items_read,
+                                               items_written, terminator,
+                                               locking, single);
+
+       if (encoded == NULL)
+               return NULL;
+
+out:
+       if (used_locking != NULL)
+               *used_locking = locking;
+
+       if (used_single != NULL)
+               *used_single = single;
+
+       return encoded;
+}
+
+/*!
+ * Decodes the hex encoded data and converts to a byte array.  If terminator
+ * is not 0, the terminator character is appended to the end of the result.
+ * This might be useful for converting GSM encoded data if the CSCS is set
+ * to HEX.
+ *
+ * Please note that this since GSM does allow embedded null characeters, use
+ * of the terminator or the items_writen is encouraged to find the real size
+ * of the result.
+ */
+unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written,
+                                       unsigned char terminator,
+                                       unsigned char *buf)
+{
+       long i, j;
+       char c;
+       unsigned char b;
+
+       if (len < 0)
+               len = strlen(in);
+
+       len &= ~0x1;
+
+       for (i = 0, j = 0; i < len; i++, j++) {
+               c = toupper(in[i]);
+
+               if (c >= '0' && c <= '9')
+                       b = c - '0';
+               else if (c >= 'A' && c <= 'F')
+                       b = 10 + c - 'A';
+               else
+                       return NULL;
+
+               i += 1;
+
+               c = toupper(in[i]);
+
+               if (c >= '0' && c <= '9')
+                       b = b * 16 + c - '0';
+               else if (c >= 'A' && c <= 'F')
+                       b = b * 16 + 10 + c - 'A';
+               else
+                       return NULL;
+
+               buf[j] = b;
+       }
+
+       if (terminator)
+               buf[j] = terminator;
+
+       if (items_written)
+               *items_written = j;
+
+       return buf;
+}
+
+unsigned char *decode_hex(const char *in, long len, long *items_written,
+                               unsigned char terminator)
+{
+       long i;
+       char c;
+       unsigned char *buf;
+
+       if (len < 0)
+               len = strlen(in);
+
+       len &= ~0x1;
+
+       for (i = 0; i < len; i++) {
+               c = toupper(in[i]);
+
+               if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))
+                       continue;
+
+               return NULL;
+       }
+
+       buf = g_new(unsigned char, (len >> 1) + (terminator ? 1 : 0));
+
+       return decode_hex_own_buf(in, len, items_written, terminator, buf);
+}
+
+/*!
+ * Encodes the data using hexadecimal characters.  len can be negative,
+ * in that case the terminator is used to find the last character.  This is
+ * useful for handling GSM-encoded strings which allow ASCII NULL character
+ * in the stream.
+ */
+char *encode_hex_own_buf(const unsigned char *in, long len,
+                               unsigned char terminator, char *buf)
+{
+       long i, j;
+       char c;
+
+       if (len < 0) {
+               i = 0;
+
+               while (in[i] != terminator)
+                       i++;
+
+               len = i;
+       }
+
+       for (i = 0, j = 0; i < len; i++, j++) {
+               c = (in[i] >> 4) & 0xf;
+
+               if (c <= 9)
+                       buf[j] = '0' + c;
+               else
+                       buf[j] = 'A' + c - 10;
+
+               j += 1;
+
+               c = (in[i]) & 0xf;
+
+               if (c <= 9)
+                       buf[j] = '0' + c;
+               else
+                       buf[j] = 'A' + c - 10;
+       }
+
+       buf[j] = '\0';
+
+       return buf;
+}
+
+char *encode_hex(const unsigned char *in, long len, unsigned char terminator)
+{
+       char *buf;
+       int i;
+
+       if (len < 0) {
+               i = 0;
+
+               while (in[i] != terminator)
+                       i++;
+
+               len = i;
+       }
+
+       buf = g_new(char, len * 2 + 1);
+
+       return encode_hex_own_buf(in, len, terminator, buf);
+}
+
+unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len,
+                                       int byte_offset, gboolean ussd,
+                                       long max_to_unpack, long *items_written,
+                                       unsigned char terminator,
+                                       unsigned char *buf)
+{
+       unsigned char rest = 0;
+       unsigned char *out = buf;
+       int bits = 7 - (byte_offset % 7);
+       long i;
+
+       if (len <= 0)
+               return NULL;
+
+       /* In the case of CB, unpack as much as possible */
+       if (ussd == TRUE)
+               max_to_unpack = len * 8 / 7;
+
+       for (i = 0; (i < len) && ((out-buf) < max_to_unpack); i++) {
+               /* Grab what we have in the current octet */
+               *out = (in[i] & ((1 << bits) - 1)) << (7 - bits);
+
+               /* Append what we have from the previous octet, if any */
+               *out |= rest;
+
+               /* Figure out the remainder */
+               rest = (in[i] >> bits) & ((1 << (8-bits)) - 1);
+
+               /*
+                * We have the entire character, here we don't increate
+                * out if this is we started at an offset.  Instead
+                * we effectively populate variable rest
+                */
+               if (i != 0 || bits == 7)
+                       out++;
+
+               if ((out-buf) == max_to_unpack)
+                       break;
+
+               /*
+                * We expected only 1 bit from this octet, means there's 7
+                * left, take care of them here
+                */
+               if (bits == 1) {
+                       *out = rest;
+                       out++;
+                       bits = 7;
+                       rest = 0;
+               } else {
+                       bits = bits - 1;
+               }
+       }
+
+       /*
+        * According to 23.038 6.1.2.3.1, last paragraph:
+        * "If the total number of characters to be sent equals (8n-1)
+        * where n=1,2,3 etc. then there are 7 spare bits at the end
+        * of the message. To avoid the situation where the receiving
+        * entity confuses 7 binary zero pad bits as the @ character,
+        * the carriage return or <CR> character shall be used for
+        * padding in this situation, just as for Cell Broadcast."
+        *
+        * "The receiving entity shall remove the final <CR> character where
+        * the message ends on an octet boundary with <CR> as the last
+        * character.
+        */
+       if (ussd && (((out - buf) % 8) == 0) && (*(out - 1) == '\r'))
+               out = out - 1;
+
+       if (terminator)
+               *out = terminator;
+
+       if (items_written)
+               *items_written = out - buf;
+
+       return buf;
+}
+
+unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset,
+                               gboolean ussd, long max_to_unpack,
+                               long *items_written, unsigned char terminator)
+{
+       unsigned char *buf = g_new(unsigned char,
+                                       len * 8 / 7 + (terminator ? 1 : 0));
+
+       return unpack_7bit_own_buf(in, len, byte_offset, ussd, max_to_unpack,
+                               items_written, terminator, buf);
+}
+
+unsigned char *pack_7bit_own_buf(const unsigned char *in, long len,
+                                       int byte_offset, gboolean ussd,
+                                       long *items_written,
+                                       unsigned char terminator,
+                                       unsigned char *buf)
+{
+       int bits = 7 - (byte_offset % 7);
+       unsigned char *out = buf;
+       long i;
+       long total_bits;
+
+       if (len == 0)
+               return NULL;
+
+       if (len < 0) {
+               i = 0;
+
+               while (in[i] != terminator)
+                       i++;
+
+               len = i;
+       }
+
+       total_bits = len * 7;
+
+       if (bits != 7) {
+               total_bits += bits;
+               bits = bits - 1;
+               *out = 0;
+       }
+
+       for (i = 0; i < len; i++) {
+               if (bits != 7) {
+                       *out |= (in[i] & ((1 << (7 - bits)) - 1)) <<
+                                       (bits + 1);
+                       out++;
+               }
+
+               /* This is a no op when bits == 0, lets keep valgrind happy */
+               if (bits != 0)
+                       *out = in[i] >> (7 - bits);
+
+               if (bits == 0)
+                       bits = 7;
+               else
+                       bits = bits - 1;
+       }
+
+       /*
+        * If <CR> is intended to be the last character and the message
+        * (including the wanted <CR>) ends on an octet boundary, then
+        * another <CR> must be added together with a padding bit 0. The
+        * receiving entity will perform the carriage return function twice,
+        * but this will not result in misoperation as the definition of
+        * <CR> in clause 6.1.1 is identical to the definition of <CR><CR>.
+        */
+       if (ussd && ((total_bits % 8) == 1))
+               *out |= '\r' << 1;
+
+       if (bits != 7)
+               out++;
+
+       if (ussd && ((total_bits % 8) == 0) && (in[len - 1] == '\r')) {
+               *out = '\r';
+               out++;
+       }
+
+       if (items_written)
+               *items_written = out - buf;
+
+       return buf;
+}
+
+unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset,
+                               gboolean ussd, long *items_written,
+                               unsigned char terminator)
+{
+       int bits = 7 - (byte_offset % 7);
+       long i;
+       long total_bits;
+       unsigned char *buf;
+
+       if (len == 0 || items_written == NULL)
+               return NULL;
+
+       if (len < 0) {
+               i = 0;
+
+               while (in[i] != terminator)
+                       i++;
+
+               len = i;
+       }
+
+       total_bits = len * 7;
+
+       if (bits != 7)
+               total_bits += bits;
+
+       /* Round up number of bytes, must append <cr> if true */
+       if (ussd && ((total_bits % 8) == 0) && (in[len - 1] == '\r'))
+               buf = g_new(unsigned char, (total_bits + 14) / 8);
+       else
+               buf = g_new(unsigned char, (total_bits + 7) / 8);
+
+       return pack_7bit_own_buf(in, len, byte_offset, ussd, items_written,
+                                       terminator, buf);
+}
+
+char *sim_string_to_utf8(const unsigned char *buffer, int length)
+{
+       struct conversion_table t;
+       int i;
+       int j;
+       int num_chars;
+       unsigned short ucs2_offset;
+       int res_len;
+       int offset;
+       char *utf8 = NULL;
+       char *out;
+
+       if (conversion_table_init(&t, GSM_DIALECT_DEFAULT,
+                                       GSM_DIALECT_DEFAULT) == FALSE)
+               return NULL;
+
+       if (length < 1)
+               return NULL;
+
+       if (buffer[0] < 0x80) {
+               /*
+                * We have to find the real length, since on SIM file system
+                * alpha fields are 0xff padded
+                */
+               for (i = 0; i < length; i++)
+                       if (buffer[i] == 0xff)
+                               break;
+
+               return convert_gsm_to_utf8(buffer, i, NULL, NULL, 0);
+       }
+
+       switch (buffer[0]) {
+       case 0x80:
+               if (((length - 1) % 2) == 1) {
+                       if (buffer[length - 1] != 0xff)
+                               return NULL;
+
+                       length = length - 1;
+               }
+
+               for (i = 1; i < length; i += 2)
+                       if (buffer[i] == 0xff && buffer[i + 1] == 0xff)
+                               break;
+
+               return g_convert((char *) buffer + 1, i - 1,
+                                       "UTF-8//TRANSLIT", "UCS-2BE",
+                                       NULL, NULL, NULL);
+       case 0x81:
+               if (length < 3 || (buffer[1] > (length - 3)))
+                       return NULL;
+
+               num_chars = buffer[1];
+               ucs2_offset = buffer[2] << 7;
+               offset = 3;
+               break;
+
+       case 0x82:
+               if (length < 4 || buffer[1] > length - 4)
+                       return NULL;
+
+               num_chars = buffer[1];
+               ucs2_offset = (buffer[2] << 8) | buffer[3];
+               offset = 4;
+               break;
+
+       default:
+               return NULL;
+       }
+
+       res_len = 0;
+       i = offset;
+       j = 0;
+
+       while ((i < length) && (j < num_chars)) {
+               unsigned short c;
+
+               if (buffer[i] & 0x80) {
+                       c = (buffer[i++] & 0x7f) + ucs2_offset;
+
+                       if (c >= 0xd800 && c < 0xe000)
+                               return NULL;
+
+                       res_len += UTF8_LENGTH(c);
+                       j += 1;
+                       continue;
+               }
+
+               if (buffer[i] == 0x1b) {
+                       ++i;
+                       if (i >= length)
+                               return NULL;
+
+                       c = gsm_single_shift_lookup(&t, buffer[i++]);
+
+                       if (c == 0)
+                               return NULL;
+
+                       j += 2;
+               } else {
+                       c = gsm_locking_shift_lookup(&t, buffer[i++]);
+                       j += 1;
+               }
+
+               res_len += UTF8_LENGTH(c);
+       }
+
+       if (j != num_chars)
+               return NULL;
+
+       /* Check that the string is padded out to the length by 0xff */
+       for (; i < length; i++)
+               if (buffer[i] != 0xff)
+                       return NULL;
+
+       utf8 = g_try_malloc(res_len + 1);
+       if (utf8 == NULL)
+               return NULL;
+
+       i = offset;
+       out = utf8;
+
+       while (out < utf8 + res_len) {
+               unsigned short c;
+
+               if (buffer[i] & 0x80)
+                       c = (buffer[i++] & 0x7f) + ucs2_offset;
+               else if (buffer[i] == 0x1b) {
+                       ++i;
+                       c = gsm_single_shift_lookup(&t, buffer[i++]);
+               } else
+                       c = gsm_locking_shift_lookup(&t, buffer[i++]);
+
+               out += g_unichar_to_utf8(c, out);
+       }
+
+       *out = '\0';
+
+       return utf8;
+}
+
+unsigned char *utf8_to_sim_string(const char *utf, int max_length,
+                                       int *out_length)
+{
+       unsigned char *result;
+       unsigned char *ucs2;
+       long gsm_bytes;
+       gsize converted;
+
+       result = convert_utf8_to_gsm(utf, -1, NULL, &gsm_bytes, 0);
+       if (result) {
+               if (gsm_bytes > max_length) {
+                       gsm_bytes = max_length;
+                       while (gsm_bytes && result[gsm_bytes - 1] == 0x1b)
+                               gsm_bytes -= 1;
+               }
+
+               *out_length = gsm_bytes;
+               return result;
+       }
+
+       /* NOTE: UCS2 formats with an offset are never used */
+
+       ucs2 = (guint8 *) g_convert(utf, -1, "UCS-2BE//TRANSLIT", "UTF-8",
+                                       NULL, &converted, NULL);
+       if (ucs2 == NULL)
+               return NULL;
+
+       if (max_length != -1 && (int) converted + 1 > max_length)
+               converted = (max_length - 1) & ~1;
+
+       result = g_try_malloc(converted + 1);
+       if (result == NULL) {
+               g_free(ucs2);
+               return NULL;
+       }
+
+       *out_length = converted + 1;
+
+       result[0] = 0x80;
+       memcpy(&result[1], ucs2, converted);
+       g_free(ucs2);
+
+       return result;
+}
+
+/*!
+ * Converts UCS2 encoded text to GSM alphabet. The result is unpacked,
+ * with the 7th bit always 0. If terminator is not 0, a terminator character
+ * is appended to the result.
+ *
+ * Returns the encoded data or NULL if the data could not be encoded. The
+ * data must be freed by the caller. If items_read is not NULL, it contains
+ * the actual number of bytes read. If items_written is not NULL, contains
+ * the number of bytes written.
+ */
+unsigned char *convert_ucs2_to_gsm_with_lang(const unsigned char *text,
+                                       long len, long *items_read,
+                                       long *items_written,
+                                       unsigned char terminator,
+                                       enum gsm_dialect locking_lang,
+                                       enum gsm_dialect single_lang)
+{
+       struct conversion_table t;
+       long nchars = 0;
+       const unsigned char *in;
+       unsigned char *out;
+       unsigned char *res = NULL;
+       long res_len;
+       long i;
+
+       if (conversion_table_init(&t, locking_lang, single_lang) == FALSE)
+               return NULL;
+
+       if (len < 1 || len % 2)
+               return NULL;
+
+       in = text;
+       res_len = 0;
+
+       for (i = 0; i < len; i += 2) {
+               gunichar c = (in[i] << 8) | in[i + 1];
+               unsigned short converted = GUND;
+
+               if (c > 0xffff)
+                       goto err_out;
+
+               converted = unicode_locking_shift_lookup(&t, c);
+
+               if (converted == GUND)
+                       converted = unicode_single_shift_lookup(&t, c);
+
+               if (converted == GUND)
+                       goto err_out;
+
+               if (converted & 0x1b00)
+                       res_len += 2;
+               else
+                       res_len += 1;
+
+               nchars += 1;
+       }
+
+       res = g_try_malloc(res_len + (terminator ? 1 : 0));
+       if (res == NULL)
+               goto err_out;
+
+       in = text;
+       out = res;
+
+       for (i = 0; i < len; i += 2) {
+               gunichar c = (in[i] << 8) | in[i + 1];
+               unsigned short converted = GUND;
+
+               converted = unicode_locking_shift_lookup(&t, c);
+
+               if (converted == GUND)
+                       converted = unicode_single_shift_lookup(&t, c);
+
+               if (converted & 0x1b00) {
+                       *out = 0x1b;
+                       ++out;
+               }
+
+               *out = converted;
+               ++out;
+       }
+
+       if (terminator)
+               *out = terminator;
+
+       if (items_written)
+               *items_written = out - res;
+
+err_out:
+       if (items_read)
+               *items_read = i;
+
+       return res;
+}
+
+unsigned char *convert_ucs2_to_gsm(const unsigned char *text, long len,
+                               long *items_read, long *items_written,
+                               unsigned char terminator)
+{
+       return convert_ucs2_to_gsm_with_lang(text, len, items_read,
+                                               items_written,
+                                               terminator,
+                                               GSM_DIALECT_DEFAULT,
+                                               GSM_DIALECT_DEFAULT);
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..092b4b5
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum gsm_dialect {
+       GSM_DIALECT_DEFAULT = 0,
+       GSM_DIALECT_TURKISH,
+       GSM_DIALECT_SPANISH,
+       GSM_DIALECT_PORTUGUESE,
+};
+
+char *convert_gsm_to_utf8(const unsigned char *text, long len, long *items_read,
+                               long *items_written, unsigned char terminator);
+
+char *convert_gsm_to_utf8_with_lang(const unsigned char *text, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator,
+                                       enum gsm_dialect locking_shift_lang,
+                                       enum gsm_dialect single_shift_lang);
+
+unsigned char *convert_utf8_to_gsm(const char *text, long len, long *items_read,
+                               long *items_written, unsigned char terminator);
+
+unsigned char *convert_utf8_to_gsm_with_lang(const char *text, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator,
+                                       enum gsm_dialect locking_shift_lang,
+                                       enum gsm_dialect single_shift_lang);
+
+unsigned char *convert_utf8_to_gsm_best_lang(const char *utf8, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator,
+                                       enum gsm_dialect hint,
+                                       enum gsm_dialect *used_locking,
+                                       enum gsm_dialect *used_single);
+
+unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written,
+                                       unsigned char terminator,
+                                       unsigned char *buf);
+
+unsigned char *decode_hex(const char *in, long len, long *items_written,
+                               unsigned char terminator);
+
+char *encode_hex_own_buf(const unsigned char *in, long len,
+                               unsigned char terminator, char *buf);
+
+char *encode_hex(const unsigned char *in, long len,
+                       unsigned char terminator);
+
+unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len,
+                                       int byte_offset, gboolean ussd,
+                                       long max_to_unpack, long *items_written,
+                                       unsigned char terminator,
+                                       unsigned char *buf);
+
+unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset,
+                               gboolean ussd, long max_to_unpack,
+                               long *items_written, unsigned char terminator);
+
+unsigned char *pack_7bit_own_buf(const unsigned char *in, long len,
+                                       int byte_offset, gboolean ussd,
+                                       long *items_written,
+                                       unsigned char terminator,
+                                       unsigned char *buf);
+
+unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset,
+                               gboolean ussd,
+                               long *items_written, unsigned char terminator);
+
+char *sim_string_to_utf8(const unsigned char *buffer, int length);
+
+unsigned char *utf8_to_sim_string(const char *utf,
+                                       int max_length, int *out_length);
+
+unsigned char *convert_ucs2_to_gsm_with_lang(const unsigned char *text,
+                                               long len, long *items_read,
+                                               long *items_written,
+                                               unsigned char terminator,
+                                               enum gsm_dialect locking_lang,
+                                               enum gsm_dialect single_lang);
+
+unsigned char *convert_ucs2_to_gsm(const unsigned char *text, long len,
+                                       long *items_read, long *items_written,
+                                       unsigned char terminator);
diff --git a/src/voicecall.c b/src/voicecall.c
new file mode 100644 (file)
index 0000000..e224d3a
--- /dev/null
@@ -0,0 +1,3912 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+#include "simutil.h"
+#include "smsutil.h"
+#include "storage.h"
+
+#define MAX_VOICE_CALLS 16
+
+#define VOICECALL_FLAG_SIM_ECC_READY 0x1
+#define VOICECALL_FLAG_STK_MODEM_CALLSETUP 0x2
+
+#define SETTINGS_STORE "voicecall"
+#define SETTINGS_GROUP "Settings"
+
+GSList *g_drivers = NULL;
+
+struct ofono_voicecall {
+       GSList *call_list;
+       GSList *release_list;
+       GSList *multiparty_list;
+       GHashTable *en_list; /* emergency number list */
+       GSList *sim_en_list; /* Emergency numbers already read from SIM */
+       GSList *new_sim_en_list; /* Emergency numbers being read from SIM */
+       char **nw_en_list; /* Emergency numbers from modem/network */
+       DBusMessage *pending;
+       uint32_t flags;
+       struct ofono_sim *sim;
+       struct ofono_sim_context *sim_context;
+       unsigned int sim_watch;
+       unsigned int sim_state_watch;
+       const struct ofono_voicecall_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       struct dial_request *dial_req;
+       GQueue *toneq;
+       guint tone_source;
+       unsigned int hfp_watch;
+       GKeyFile *settings;
+       char *imsi;
+       ofono_voicecall_cb_t release_queue_done_cb;
+       struct ofono_emulator *pending_em;
+       unsigned int pending_id;
+};
+
+struct voicecall {
+       struct ofono_call *call;
+       struct ofono_voicecall *vc;
+       time_t start_time;
+       time_t detect_time;
+       char *message;
+       uint8_t icon_id;
+       gboolean untracked;
+       gboolean dial_result_handled;
+       ofono_bool_t remote_held;
+       ofono_bool_t remote_multiparty;
+};
+
+struct dial_request {
+       struct ofono_voicecall *vc;
+       char *message;
+       uint8_t icon_id;
+       enum ofono_voicecall_interaction interaction;
+       ofono_voicecall_dial_cb_t cb;
+       void *user_data;
+       struct voicecall *call;
+       struct ofono_phone_number ph;
+};
+
+struct tone_queue_entry {
+       char *tone_str;
+       char *left;
+       ofono_voicecall_tone_cb_t cb;
+       void *user_data;
+       ofono_destroy_func destroy;
+       int id;
+};
+
+struct emulator_status {
+       struct ofono_voicecall *vc;
+       int status;
+};
+
+static const char *default_en_list[] = { "911", "112", NULL };
+static const char *default_en_list_no_sim[] = { "119", "118", "999", "110",
+                                               "08", "000", NULL };
+
+static void generic_callback(const struct ofono_error *error, void *data);
+static void hangup_all_active(const struct ofono_error *error, void *data);
+static void multirelease_callback(const struct ofono_error *err, void *data);
+static gboolean tone_request_run(gpointer user_data);
+
+static gint call_compare_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct ofono_call *call = ((struct voicecall *)a)->call;
+       unsigned int id = GPOINTER_TO_UINT(b);
+
+       if (id < call->id)
+               return -1;
+
+       if (id > call->id)
+               return 1;
+
+       return 0;
+}
+
+static gint call_compare(gconstpointer a, gconstpointer b)
+{
+       const struct voicecall *ca = a;
+       const struct voicecall *cb = b;
+
+       if (ca->call->id < cb->call->id)
+               return -1;
+
+       if (ca->call->id > cb->call->id)
+               return 1;
+
+       return 0;
+}
+
+static void add_to_en_list(struct ofono_voicecall *vc, char **list)
+{
+       int i = 0;
+
+       while (list[i])
+               g_hash_table_insert(vc->en_list, g_strdup(list[i++]), NULL);
+}
+
+static const char *disconnect_reason_to_string(enum ofono_disconnect_reason r)
+{
+       switch (r) {
+       case OFONO_DISCONNECT_REASON_LOCAL_HANGUP:
+               return "local";
+       case OFONO_DISCONNECT_REASON_REMOTE_HANGUP:
+               return "remote";
+       default:
+               return "network";
+       }
+}
+
+static const char *call_status_to_string(int status)
+{
+       switch (status) {
+       case CALL_STATUS_ACTIVE:
+               return "active";
+       case CALL_STATUS_HELD:
+               return "held";
+       case CALL_STATUS_DIALING:
+               return "dialing";
+       case CALL_STATUS_ALERTING:
+               return "alerting";
+       case CALL_STATUS_INCOMING:
+               return "incoming";
+       case CALL_STATUS_WAITING:
+               return "waiting";
+       default:
+               return "disconnected";
+       }
+}
+
+static const char *phone_and_clip_to_string(const struct ofono_phone_number *n,
+                                               int clip_validity)
+{
+       if (clip_validity == CLIP_VALIDITY_WITHHELD && !strlen(n->number))
+               return "withheld";
+
+       if (clip_validity == CLIP_VALIDITY_NOT_AVAILABLE)
+               return "";
+
+       return phone_number_to_string(n);
+}
+
+static const char *cnap_to_string(const char *name, int cnap_validity)
+{
+       if (cnap_validity == CNAP_VALIDITY_WITHHELD && !strlen(name))
+               return "withheld";
+
+       if (cnap_validity == CNAP_VALIDITY_NOT_AVAILABLE)
+               return "";
+
+       return name;
+}
+
+static const char *time_to_str(const time_t *t)
+{
+       static char buf[128];
+       struct tm tm;
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime_r(t, &tm));
+       buf[127] = '\0';
+
+       return buf;
+}
+
+static unsigned int voicecalls_num_with_status(struct ofono_voicecall *vc,
+                                               int status)
+{
+       GSList *l;
+       struct voicecall *v;
+       int num = 0;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == status)
+                       num += 1;
+       }
+
+       return num;
+}
+
+static unsigned int voicecalls_num_active(struct ofono_voicecall *vc)
+{
+       return voicecalls_num_with_status(vc, CALL_STATUS_ACTIVE);
+}
+
+static unsigned int voicecalls_num_held(struct ofono_voicecall *vc)
+{
+       return voicecalls_num_with_status(vc, CALL_STATUS_HELD);
+}
+
+static unsigned int voicecalls_num_connecting(struct ofono_voicecall *vc)
+{
+       unsigned int r = 0;
+
+       r += voicecalls_num_with_status(vc, CALL_STATUS_DIALING);
+       r += voicecalls_num_with_status(vc, CALL_STATUS_ALERTING);
+
+       return r;
+}
+
+static gboolean voicecalls_have_active(struct ofono_voicecall *vc)
+{
+       GSList *l;
+       struct voicecall *v;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == CALL_STATUS_ACTIVE ||
+                               v->call->status == CALL_STATUS_DIALING ||
+                               v->call->status == CALL_STATUS_ALERTING)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean voicecalls_have_with_status(struct ofono_voicecall *vc,
+                                               int status)
+{
+       GSList *l;
+       struct voicecall *v;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == status)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean voicecalls_have_held(struct ofono_voicecall *vc)
+{
+       return voicecalls_have_with_status(vc, CALL_STATUS_HELD);
+}
+
+static gboolean voicecalls_have_waiting(struct ofono_voicecall *vc)
+{
+       return voicecalls_have_with_status(vc, CALL_STATUS_WAITING);
+}
+
+static gboolean voicecalls_have_incoming(struct ofono_voicecall *vc)
+{
+       return voicecalls_have_with_status(vc, CALL_STATUS_INCOMING);
+}
+
+static void dial_request_finish(struct ofono_voicecall *vc)
+{
+       struct dial_request *dial_req = vc->dial_req;
+
+       if (dial_req->cb)
+               dial_req->cb(dial_req->call ? dial_req->call->call : NULL,
+                               dial_req->user_data);
+
+       g_free(dial_req->message);
+       g_free(dial_req);
+       vc->dial_req = NULL;
+}
+
+static gboolean voicecalls_can_dtmf(struct ofono_voicecall *vc)
+{
+       GSList *l;
+       struct voicecall *v;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == CALL_STATUS_ACTIVE)
+                       return TRUE;
+
+               /* Connected for 2nd stage dialing */
+               if (v->call->status == CALL_STATUS_ALERTING)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static int tone_queue(struct ofono_voicecall *vc, const char *tone_str,
+                       ofono_voicecall_tone_cb_t cb, void *data,
+                       ofono_destroy_func destroy)
+{
+       struct tone_queue_entry *entry;
+       int id = 1;
+       int n = 0;
+       int i;
+
+       /*
+        * Tones can be 0-9, *, #, A-D according to 27.007 C.2.11,
+        * and p for Pause.
+        */
+       for (i = 0; tone_str[i]; i++)
+               if (!g_ascii_isdigit(tone_str[i]) && tone_str[i] != 'p' &&
+                               tone_str[i] != 'P' && tone_str[i] != '*' &&
+                               tone_str[i] != '#' && (tone_str[i] < 'A' ||
+                               tone_str[i] > 'D'))
+                       return -EINVAL;
+
+       while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL)
+               if (entry->id >= id)
+                       id = entry->id + 1;
+
+       entry = g_try_new0(struct tone_queue_entry, 1);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       entry->tone_str = g_strdup(tone_str);
+       entry->left = entry->tone_str;
+       entry->cb = cb;
+       entry->user_data = data;
+       entry->destroy = destroy;
+       entry->id = id;
+
+       g_queue_push_tail(vc->toneq, entry);
+
+       if (g_queue_get_length(vc->toneq) == 1)
+               g_timeout_add(0, tone_request_run, vc);
+
+       return id;
+}
+
+static void tone_request_finish(struct ofono_voicecall *vc,
+                               struct tone_queue_entry *entry,
+                               int error, gboolean callback)
+{
+       g_queue_remove(vc->toneq, entry);
+
+       if (callback)
+               entry->cb(error, entry->user_data);
+
+       if (entry->destroy)
+               entry->destroy(entry->user_data);
+
+       g_free(entry->tone_str);
+       g_free(entry);
+}
+
+static gboolean is_emergency_number(struct ofono_voicecall *vc,
+                                       const char *number)
+{
+       return g_hash_table_lookup_extended(vc->en_list, number, NULL, NULL);
+}
+
+static void append_voicecall_properties(struct voicecall *v,
+                                       DBusMessageIter *dict)
+{
+       struct ofono_call *call = v->call;
+       const char *status;
+       const char *callerid;
+       const char *timestr;
+       const char *name;
+       ofono_bool_t mpty;
+       dbus_bool_t emergency_call;
+
+       status = call_status_to_string(call->status);
+
+       ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &status);
+
+       if (call->direction == CALL_DIRECTION_MOBILE_TERMINATED)
+               callerid = phone_and_clip_to_string(&call->phone_number,
+                                                       call->clip_validity);
+       else
+               callerid = phone_number_to_string(&call->phone_number);
+
+       ofono_dbus_dict_append(dict, "LineIdentification",
+                                       DBUS_TYPE_STRING, &callerid);
+
+       if (call->called_number.number[0] != '\0') {
+               const char *calledid;
+
+               calledid = phone_number_to_string(&call->called_number);
+
+               ofono_dbus_dict_append(dict, "IncomingLine",
+                                               DBUS_TYPE_STRING, &calledid);
+       }
+
+       name = cnap_to_string(call->name, call->cnap_validity);
+
+       ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &name);
+
+       if (call->status == CALL_STATUS_ACTIVE ||
+                       call->status == CALL_STATUS_HELD ||
+                       (call->status == CALL_STATUS_DISCONNECTED &&
+                               v->start_time != 0)) {
+               timestr = time_to_str(&v->start_time);
+
+               ofono_dbus_dict_append(dict, "StartTime", DBUS_TYPE_STRING,
+                                       &timestr);
+       }
+
+       if (g_slist_find_custom(v->vc->multiparty_list,
+                               GINT_TO_POINTER(call->id), call_compare_by_id))
+               mpty = TRUE;
+       else
+               mpty = FALSE;
+
+       ofono_dbus_dict_append(dict, "Multiparty", DBUS_TYPE_BOOLEAN, &mpty);
+
+       ofono_dbus_dict_append(dict, "RemoteHeld", DBUS_TYPE_BOOLEAN,
+                               &v->remote_held);
+
+       ofono_dbus_dict_append(dict, "RemoteMultiparty", DBUS_TYPE_BOOLEAN,
+                               &v->remote_multiparty);
+
+       if (v->message)
+               ofono_dbus_dict_append(dict, "Information",
+                                               DBUS_TYPE_STRING, &v->message);
+
+       if (v->icon_id)
+               ofono_dbus_dict_append(dict, "Icon",
+                                               DBUS_TYPE_BYTE, &v->icon_id);
+
+       if (is_emergency_number(v->vc, callerid) == TRUE)
+               emergency_call = TRUE;
+       else
+               emergency_call = FALSE;
+
+       ofono_dbus_dict_append(dict, "Emergency",
+                                       DBUS_TYPE_BOOLEAN, &emergency_call);
+
+}
+
+static DBusMessage *voicecall_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct voicecall *v = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       append_voicecall_properties(v, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *voicecall_deflect(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct voicecall *v = data;
+       struct ofono_voicecall *vc = v->vc;
+       struct ofono_call *call = v->call;
+
+       struct ofono_phone_number ph;
+       const char *number;
+
+       if (call->status != CALL_STATUS_INCOMING &&
+                       call->status != CALL_STATUS_WAITING)
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->deflect == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (!valid_phone_number_format(number))
+               return __ofono_error_invalid_format(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       string_to_phone_number(number, &ph);
+
+       vc->driver->deflect(vc, &ph, generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *voicecall_hangup(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct voicecall *v = data;
+       struct ofono_voicecall *vc = v->vc;
+       struct ofono_call *call = v->call;
+       gboolean single_call = vc->call_list->next == 0;
+
+       if (vc->pending || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (vc->dial_req && vc->dial_req->call != v)
+               return __ofono_error_busy(msg);
+
+       switch (call->status) {
+       case CALL_STATUS_DISCONNECTED:
+               return __ofono_error_failed(msg);
+
+       case CALL_STATUS_INCOMING:
+               if (vc->driver->hangup_all == NULL &&
+                               vc->driver->hangup_active == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               vc->pending = dbus_message_ref(msg);
+
+               if (vc->driver->hangup_all)
+                       vc->driver->hangup_all(vc, generic_callback, vc);
+               else
+                       vc->driver->hangup_active(vc, generic_callback, vc);
+
+               return NULL;
+
+       case CALL_STATUS_WAITING:
+               if (vc->driver->set_udub == NULL)
+                       return __ofono_error_not_implemented(msg);
+
+               vc->pending = dbus_message_ref(msg);
+               vc->driver->set_udub(vc, generic_callback, vc);
+
+               return NULL;
+
+       case CALL_STATUS_HELD:
+               if (vc->driver->release_all_held &&
+                               voicecalls_num_held(vc) == 1 &&
+                               voicecalls_have_waiting(vc) == FALSE) {
+                       vc->pending = dbus_message_ref(msg);
+                       vc->driver->release_all_held(vc, generic_callback, vc);
+
+                       return NULL;
+               }
+
+               break;
+
+       case CALL_STATUS_DIALING:
+       case CALL_STATUS_ALERTING:
+               if (vc->driver->hangup_active != NULL) {
+                       vc->pending = dbus_message_ref(msg);
+                       vc->driver->hangup_active(vc, generic_callback, vc);
+
+                       return NULL;
+               }
+
+               /*
+                * Fall through, we check if we have a single alerting,
+                * dialing or active call and try to hang it up with
+                * hangup_all or hangup_active
+                */
+       case CALL_STATUS_ACTIVE:
+               if (single_call == TRUE && vc->driver->hangup_all != NULL) {
+                       vc->pending = dbus_message_ref(msg);
+                       vc->driver->hangup_all(vc, generic_callback, vc);
+
+                       return NULL;
+               }
+
+               if (voicecalls_num_active(vc) == 1 &&
+                               vc->driver->hangup_active != NULL) {
+                       vc->pending = dbus_message_ref(msg);
+                       vc->driver->hangup_active(vc, generic_callback, vc);
+
+                       return NULL;
+               }
+
+               break;
+       }
+
+       if (vc->driver->release_specific == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+       vc->driver->release_specific(vc, call->id,
+                                       generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *voicecall_answer(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct voicecall *v = data;
+       struct ofono_voicecall *vc = v->vc;
+       struct ofono_call *call = v->call;
+
+       if (call->status != CALL_STATUS_INCOMING)
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->answer == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->answer(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static GDBusMethodTable voicecall_methods[] = {
+       { "GetProperties",  "",    "a{sv}",   voicecall_get_properties },
+       { "Deflect",        "s",   "",        voicecall_deflect,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Hangup",         "",    "",        voicecall_hangup,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Answer",         "",    "",        voicecall_answer,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { }
+};
+
+static GDBusSignalTable voicecall_signals[] = {
+       { "PropertyChanged",    "sv" },
+       { "DisconnectReason",   "s" },
+       { }
+};
+
+static struct voicecall *voicecall_create(struct ofono_voicecall *vc,
+                                               struct ofono_call *call)
+{
+       struct voicecall *v;
+
+       v = g_try_new0(struct voicecall, 1);
+       if (v == NULL)
+               return NULL;
+
+       v->call = call;
+       v->vc = vc;
+
+       return v;
+}
+
+static void voicecall_destroy(gpointer userdata)
+{
+       struct voicecall *voicecall = (struct voicecall *)userdata;
+
+       g_free(voicecall->call);
+       g_free(voicecall->message);
+
+       g_free(voicecall);
+}
+
+static const char *voicecall_build_path(struct ofono_voicecall *vc,
+                                       const struct ofono_call *call)
+{
+       static char path[256];
+
+       snprintf(path, sizeof(path), "%s/voicecall%02d",
+                       __ofono_atom_get_path(vc->atom), call->id);
+
+       return path;
+}
+
+static void voicecall_emit_disconnect_reason(struct voicecall *call,
+                                       enum ofono_disconnect_reason reason)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *reason_str;
+
+       reason_str = disconnect_reason_to_string(reason);
+       path = voicecall_build_path(call->vc, call->call);
+
+       g_dbus_emit_signal(conn, path, OFONO_VOICECALL_INTERFACE,
+                               "DisconnectReason",
+                               DBUS_TYPE_STRING, &reason_str,
+                               DBUS_TYPE_INVALID);
+}
+
+static void voicecall_emit_multiparty(struct voicecall *call, gboolean mpty)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = voicecall_build_path(call->vc, call->call);
+       dbus_bool_t val = mpty;
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "Multiparty", DBUS_TYPE_BOOLEAN,
+                                               &val);
+}
+
+static void emulator_call_status_cb(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+       struct emulator_status *s = data;
+
+       ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_CALL, s->status);
+}
+
+static void emulator_callsetup_status_cb(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+       struct emulator_status *s = data;
+
+       ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_CALLSETUP,
+                                       s->status);
+}
+
+static void emulator_callheld_status_cb(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+       struct emulator_status *s = data;
+
+       ofono_emulator_set_indicator(em, OFONO_EMULATOR_IND_CALLHELD,
+                                       s->status);
+}
+
+static void notify_emulator_call_status(struct ofono_voicecall *vc)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       gboolean call = FALSE;
+       unsigned int non_mpty = 0;
+       gboolean multiparty = FALSE;
+       gboolean held = FALSE;
+       gboolean incoming = FALSE;
+       gboolean dialing = FALSE;
+       gboolean alerting = FALSE;
+       gboolean waiting = FALSE;
+       GSList *l;
+       struct voicecall *v;
+       struct emulator_status data;
+
+       data.vc = vc;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               switch (v->call->status) {
+               case CALL_STATUS_ACTIVE:
+                       call = TRUE;
+                       if (g_slist_find_custom(vc->multiparty_list,
+                                               GINT_TO_POINTER(v->call->id),
+                                               call_compare_by_id))
+                               multiparty = TRUE;
+                       else
+                               non_mpty++;
+                       break;
+
+               case CALL_STATUS_HELD:
+                       held = TRUE;
+                       break;
+
+               case CALL_STATUS_DIALING:
+                       dialing = TRUE;
+                       break;
+
+               case CALL_STATUS_ALERTING:
+                       alerting = TRUE;
+                       break;
+
+               case CALL_STATUS_INCOMING:
+                       incoming = TRUE;
+                       break;
+
+               case CALL_STATUS_WAITING:
+                       waiting = TRUE;
+                       break;
+               }
+       }
+
+       data.status = call || held ? OFONO_EMULATOR_CALL_ACTIVE :
+                                       OFONO_EMULATOR_CALL_INACTIVE;
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_call_status_cb, &data);
+
+       if (incoming)
+               data.status = OFONO_EMULATOR_CALLSETUP_INCOMING;
+       else if (dialing)
+               data.status = OFONO_EMULATOR_CALLSETUP_OUTGOING;
+       else if (alerting)
+               data.status = OFONO_EMULATOR_CALLSETUP_ALERTING;
+       else if (waiting)
+               data.status = OFONO_EMULATOR_CALLSETUP_INCOMING;
+       else
+               data.status = OFONO_EMULATOR_CALLSETUP_INACTIVE;
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_callsetup_status_cb,
+                                               &data);
+
+       if (held)
+               data.status = call ? OFONO_EMULATOR_CALLHELD_MULTIPLE :
+                                       OFONO_EMULATOR_CALLHELD_ON_HOLD;
+       else if (non_mpty > 1 || (non_mpty && multiparty))
+               /*
+                * After call swap, it is possible that all calls move
+                * temporarily to active state (depending on call state update
+                * order), generating an update of callheld indicator to 0.
+                * This will fail PTS test TP/TWC/BV-03-I.
+                *
+                * So, in case of multiple active calls, or an active call with
+                * an active mutiparty call, force update of callheld indicator
+                * to 2 (intermediate state allowed).
+                */
+               data.status = OFONO_EMULATOR_CALLHELD_ON_HOLD;
+       else
+               data.status = OFONO_EMULATOR_CALLHELD_NONE;
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_callheld_status_cb,
+                                               &data);
+}
+
+static void voicecall_set_call_status(struct voicecall *call, int status)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *status_str;
+       int old_status;
+
+       if (call->call->status == status)
+               return;
+
+       old_status = call->call->status;
+
+       call->call->status = status;
+
+       status_str = call_status_to_string(status);
+       path = voicecall_build_path(call->vc, call->call);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "State", DBUS_TYPE_STRING,
+                                               &status_str);
+
+       notify_emulator_call_status(call->vc);
+
+       if (status == CALL_STATUS_ACTIVE &&
+               (old_status == CALL_STATUS_INCOMING ||
+                       old_status == CALL_STATUS_DIALING ||
+                       old_status == CALL_STATUS_ALERTING ||
+                       old_status == CALL_STATUS_WAITING)) {
+               const char *timestr;
+
+               call->start_time = time(NULL);
+               timestr = time_to_str(&call->start_time);
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "StartTime", DBUS_TYPE_STRING,
+                                               &timestr);
+
+               if (call->vc->dial_req && call == call->vc->dial_req->call)
+                       dial_request_finish(call->vc);
+       }
+
+       if (status == CALL_STATUS_DISCONNECTED && call->vc->dial_req &&
+                       call == call->vc->dial_req->call)
+               dial_request_finish(call->vc);
+
+       if (!voicecalls_can_dtmf(call->vc)) {
+               struct tone_queue_entry *entry;
+
+               while ((entry = g_queue_peek_head(call->vc->toneq)))
+                       tone_request_finish(call->vc, entry, ENOENT, TRUE);
+       }
+}
+
+static void voicecall_set_call_lineid(struct voicecall *v,
+                                       const struct ofono_phone_number *ph,
+                                       int clip_validity)
+{
+       struct ofono_call *call = v->call;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *lineid_str;
+
+       if (!strcmp(call->phone_number.number, ph->number) &&
+                       call->phone_number.type == ph->type &&
+                       call->clip_validity == clip_validity)
+               return;
+
+       /*
+        * Two cases: We get an incoming call with CLIP factored in, or
+        * CLIP comes in later as a separate event
+        * For COLP only the phone number should be checked, it can come
+        * in with the initial call event or later as a separate event
+        */
+
+       /* For plugins that don't keep state, ignore */
+       if (call->clip_validity == CLIP_VALIDITY_VALID &&
+                       clip_validity == CLIP_VALIDITY_NOT_AVAILABLE)
+               return;
+
+       strcpy(call->phone_number.number, ph->number);
+       call->clip_validity = clip_validity;
+       call->phone_number.type = ph->type;
+
+       path = voicecall_build_path(v->vc, call);
+
+       if (call->direction == CALL_DIRECTION_MOBILE_TERMINATED)
+               lineid_str = phone_and_clip_to_string(ph, clip_validity);
+       else
+               lineid_str = phone_number_to_string(ph);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "LineIdentification",
+                                               DBUS_TYPE_STRING, &lineid_str);
+
+       if (is_emergency_number(v->vc, lineid_str)) {
+               dbus_bool_t emergency_call = TRUE;
+
+               ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "Emergency",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &emergency_call);
+       }
+}
+
+static void voicecall_set_call_calledid(struct voicecall *v,
+                                       const struct ofono_phone_number *ph)
+{
+       struct ofono_call *call = v->call;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *calledid_str;
+
+       if (!strcmp(call->called_number.number, ph->number) &&
+                                       call->called_number.type == ph->type)
+               return;
+
+       strcpy(call->called_number.number, ph->number);
+       call->called_number.type = ph->type;
+
+       path = voicecall_build_path(v->vc, call);
+       calledid_str = phone_number_to_string(ph);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "IncomingLine",
+                                               DBUS_TYPE_STRING,
+                                               &calledid_str);
+}
+
+
+static void voicecall_set_call_name(struct voicecall *v,
+                                       const char *name,
+                                       int cnap_validity)
+{
+       struct ofono_call *call = v->call;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+       const char *name_str;
+
+       if (!strcmp(call->name, name) && call->cnap_validity == cnap_validity)
+               return;
+
+       /* For plugins that don't keep state, ignore */
+       if (call->cnap_validity == CNAP_VALIDITY_VALID &&
+                       cnap_validity == CNAP_VALIDITY_NOT_AVAILABLE)
+               return;
+
+       strncpy(call->name, name, OFONO_MAX_CALLER_NAME_LENGTH);
+       call->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0';
+       call->cnap_validity = cnap_validity;
+
+       path = voicecall_build_path(v->vc, call);
+
+       name_str = cnap_to_string(name, cnap_validity);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "Name",
+                                               DBUS_TYPE_STRING, &name_str);
+}
+
+static gboolean voicecall_dbus_register(struct voicecall *v)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+
+       if (v == NULL)
+               return FALSE;
+
+       path = voicecall_build_path(v->vc, v->call);
+
+       if (!g_dbus_register_interface(conn, path, OFONO_VOICECALL_INTERFACE,
+                                       voicecall_methods,
+                                       voicecall_signals,
+                                       NULL, v, voicecall_destroy)) {
+               ofono_error("Could not register VoiceCall %s", path);
+               voicecall_destroy(v);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean voicecall_dbus_unregister(struct ofono_voicecall *vc,
+                                               struct voicecall *v)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = voicecall_build_path(vc, v->call);
+
+       return g_dbus_unregister_interface(conn, path,
+                                               OFONO_VOICECALL_INTERFACE);
+}
+
+
+static int voicecalls_path_list(struct ofono_voicecall *vc, GSList *call_list,
+                               char ***objlist)
+{
+       GSList *l;
+       int i;
+       struct voicecall *v;
+
+       *objlist = g_new0(char *, g_slist_length(call_list) + 1);
+
+       if (*objlist == NULL)
+               return -1;
+
+       for (i = 0, l = call_list; l; l = l->next, i++) {
+               v = l->data;
+               (*objlist)[i] = g_strdup(voicecall_build_path(vc, v->call));
+       }
+
+       return 0;
+}
+
+static GSList *voicecalls_held_list(struct ofono_voicecall *vc)
+{
+       GSList *l;
+       GSList *r = NULL;
+       struct voicecall *v;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == CALL_STATUS_HELD)
+                       r = g_slist_prepend(r, v);
+       }
+
+       if (r)
+               r = g_slist_reverse(r);
+
+       return r;
+}
+
+/*
+ * Intended to be used for multiparty, which cannot be incoming,
+ * alerting or dialing
+ */
+static GSList *voicecalls_active_list(struct ofono_voicecall *vc)
+{
+       GSList *l;
+       GSList *r = NULL;
+       struct voicecall *v;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == CALL_STATUS_ACTIVE)
+                       r = g_slist_prepend(r, v);
+       }
+
+       if (r)
+               r = g_slist_reverse(r);
+
+       return r;
+}
+
+struct ofono_call *__ofono_voicecall_find_call_with_status(
+                               struct ofono_voicecall *vc, int status)
+{
+       GSList *l;
+       struct voicecall *v;
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == status)
+                       return v->call;
+       }
+
+       return NULL;
+}
+
+static void voicecalls_multiparty_changed(GSList *old, GSList *new)
+{
+       GSList *o, *n;
+       struct voicecall *nc, *oc;
+
+       n = new;
+       o = old;
+
+       while (n || o) {
+               nc = n ? n->data : NULL;
+               oc = o ? o->data : NULL;
+
+               if (oc && (nc == NULL || (nc->call->id > oc->call->id))) {
+                       voicecall_emit_multiparty(oc, FALSE);
+                       o = o->next;
+               } else if (nc && (oc == NULL || (nc->call->id < oc->call->id))) {
+                       voicecall_emit_multiparty(nc, TRUE);
+                       n = n->next;
+               } else {
+                       n = n->next;
+                       o = o->next;
+               }
+       }
+}
+
+static void voicecalls_emit_call_removed(struct ofono_voicecall *vc,
+                                               struct voicecall *v)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *atompath = __ofono_atom_get_path(vc->atom);
+       const char *path = voicecall_build_path(vc, v->call);
+
+       g_dbus_emit_signal(conn, atompath, OFONO_VOICECALL_MANAGER_INTERFACE,
+                               "CallRemoved", DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+}
+
+static void voicecalls_emit_call_added(struct ofono_voicecall *vc,
+                                       struct voicecall *v)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *path;
+
+       notify_emulator_call_status(vc);
+
+       path = __ofono_atom_get_path(vc->atom);
+
+       signal = dbus_message_new_signal(path,
+                                       OFONO_VOICECALL_MANAGER_INTERFACE,
+                                       "CallAdded");
+
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       path = voicecall_build_path(vc, v->call);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+       append_voicecall_properties(v, &dict);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(ofono_dbus_get_connection(), signal);
+}
+
+static void voicecalls_release_queue(struct ofono_voicecall *vc, GSList *calls,
+                                       ofono_voicecall_cb_t cb,
+                                       ofono_bool_t skip_held)
+{
+       GSList *l;
+       struct voicecall *call;
+
+       g_slist_free(vc->release_list);
+       vc->release_list = NULL;
+
+       for (l = calls; l; l = l->next) {
+               call = l->data;
+
+               if (call->call->status == CALL_STATUS_WAITING)
+                       continue;
+
+               if (skip_held && call->call->status == CALL_STATUS_HELD)
+                       continue;
+
+               vc->release_list = g_slist_prepend(vc->release_list, l->data);
+       }
+
+       vc->release_queue_done_cb = cb;
+}
+
+static void voicecalls_release_next(struct ofono_voicecall *vc)
+{
+       struct voicecall *call;
+
+       if (vc->release_list == NULL)
+               return;
+
+       call = vc->release_list->data;
+
+       vc->release_list = g_slist_remove(vc->release_list, call);
+
+       if (vc->driver->hangup_active == NULL)
+               goto fallback;
+
+       if (call->call->status == CALL_STATUS_ACTIVE &&
+                                       voicecalls_num_active(vc) == 1) {
+               vc->driver->hangup_active(vc, multirelease_callback, vc);
+               return;
+       }
+
+       if (call->call->status == CALL_STATUS_ALERTING ||
+               call->call->status == CALL_STATUS_DIALING ||
+                       call->call->status == CALL_STATUS_INCOMING) {
+               vc->driver->hangup_active(vc, multirelease_callback, vc);
+               return;
+       }
+
+fallback:
+       vc->driver->release_specific(vc, call->call->id,
+                                       multirelease_callback, vc);
+}
+
+static void voicecalls_release_done(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(vc->pending);
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+}
+
+static DBusMessage *manager_get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       int i;
+       char **list;
+       GHashTableIter ht_iter;
+       gpointer key, value;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+       /* property EmergencyNumbers */
+       list = g_new0(char *, g_hash_table_size(vc->en_list) + 1);
+       g_hash_table_iter_init(&ht_iter, vc->en_list);
+
+       for (i = 0; g_hash_table_iter_next(&ht_iter, &key, &value); i++)
+               list[i] = key;
+
+       ofono_dbus_dict_append_array(&dict, "EmergencyNumbers",
+                                       DBUS_TYPE_STRING, &list);
+       g_free(list);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static ofono_bool_t clir_string_to_clir(const char *clirstr,
+                                       enum ofono_clir_option *clir)
+{
+       if (strlen(clirstr) == 0 || !strcmp(clirstr, "default")) {
+               *clir = OFONO_CLIR_OPTION_DEFAULT;
+               return TRUE;
+       } else if (!strcmp(clirstr, "disabled")) {
+               *clir = OFONO_CLIR_OPTION_SUPPRESSION;
+               return TRUE;
+       } else if (!strcmp(clirstr, "enabled")) {
+               *clir = OFONO_CLIR_OPTION_INVOCATION;
+               return TRUE;
+       } else {
+               return FALSE;
+       }
+}
+
+static struct ofono_call *synthesize_outgoing_call(struct ofono_voicecall *vc,
+                                                       const char *number)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       struct ofono_call *call;
+
+       call = g_try_new0(struct ofono_call, 1);
+       if (call == NULL)
+               return call;
+
+       call->id = __ofono_modem_callid_next(modem);
+
+       if (call->id == 0) {
+               ofono_error("Failed to alloc callid, too many calls");
+               g_free(call);
+               return NULL;
+       }
+
+       __ofono_modem_callid_hold(modem, call->id);
+
+       if (number)
+               string_to_phone_number(number, &call->phone_number);
+
+       call->direction = CALL_DIRECTION_MOBILE_ORIGINATED;
+       call->status = CALL_STATUS_DIALING;
+       call->clip_validity = CLIP_VALIDITY_VALID;
+
+       return call;
+}
+
+static struct voicecall *dial_handle_result(struct ofono_voicecall *vc,
+                                               const struct ofono_error *error,
+                                               const char *number,
+                                               gboolean *need_to_emit)
+{
+       GSList *l;
+       struct voicecall *v;
+       struct ofono_call *call;
+
+       *need_to_emit = FALSE;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("Dial callback returned error: %s",
+                       telephony_error_to_str(error));
+
+               return NULL;
+       }
+
+       /*
+        * Two things can happen, the call notification arrived before dial
+        * callback or dial callback was first. Handle here
+        */
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               if (v->call->status == CALL_STATUS_DIALING ||
+                               v->call->status == CALL_STATUS_ALERTING)
+                       goto handled;
+
+               /*
+                * Dial request may return before existing active call
+                * is put on hold or after dialed call has got active
+                */
+               if (v->call->status == CALL_STATUS_ACTIVE &&
+                               v->call->direction ==
+                               CALL_DIRECTION_MOBILE_ORIGINATED &&
+                               !v->dial_result_handled)
+                       goto handled;
+       }
+
+       call = synthesize_outgoing_call(vc, number);
+       if (call == NULL)
+               return NULL;
+
+       v = voicecall_create(vc, call);
+       if (v == NULL)
+               return NULL;
+
+       v->detect_time = time(NULL);
+
+       DBG("Registering new call: %d", call->id);
+       voicecall_dbus_register(v);
+
+       vc->call_list = g_slist_insert_sorted(vc->call_list, v,
+                               call_compare);
+
+       *need_to_emit = TRUE;
+
+handled:
+       v->dial_result_handled = TRUE;
+
+       return v;
+}
+
+static void manager_dial_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+       const char *number;
+       gboolean need_to_emit;
+       struct voicecall *v;
+
+       if (dbus_message_get_args(vc->pending, NULL, DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               number = NULL;
+
+       v = dial_handle_result(vc, error, number, &need_to_emit);
+
+       if (v) {
+               const char *path = voicecall_build_path(vc, v->call);
+
+               reply = dbus_message_new_method_return(vc->pending);
+
+               dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID);
+       } else {
+               struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+
+               if (is_emergency_number(vc, number) == TRUE)
+                       __ofono_modem_dec_emergency_mode(modem);
+
+               reply = __ofono_error_failed(vc->pending);
+       }
+
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+
+       if (need_to_emit)
+               voicecalls_emit_call_added(vc, v);
+}
+
+static int voicecall_dial(struct ofono_voicecall *vc, const char *number,
+                                       enum ofono_clir_option clir,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       struct ofono_phone_number ph;
+
+       if (g_slist_length(vc->call_list) >= MAX_VOICE_CALLS)
+               return -EPERM;
+
+       if (!valid_long_phone_number_format(number))
+               return -EINVAL;
+
+       if (ofono_modem_get_online(modem) == FALSE)
+               return -ENETDOWN;
+
+       if (vc->driver->dial == NULL)
+               return -ENOTSUP;
+
+       if (voicecalls_have_incoming(vc))
+               return -EBUSY;
+
+       /* We can't have two dialing/alerting calls, reject outright */
+       if (voicecalls_num_connecting(vc) > 0)
+               return -EBUSY;
+
+       if (voicecalls_have_active(vc) && voicecalls_have_held(vc))
+               return -EBUSY;
+
+       if (is_emergency_number(vc, number) == TRUE)
+               __ofono_modem_inc_emergency_mode(modem);
+
+       string_to_phone_number(number, &ph);
+
+       if (vc->settings) {
+               g_key_file_set_string(vc->settings, SETTINGS_GROUP,
+                                       "Number", number);
+               storage_sync(vc->imsi, SETTINGS_STORE, vc->settings);
+       }
+
+       vc->driver->dial(vc, &ph, clir, cb, vc);
+
+       return 0;
+}
+
+static DBusMessage *manager_dial(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       const char *number;
+       const char *clirstr;
+       enum ofono_clir_option clir;
+       int err;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_STRING, &clirstr,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (clir_string_to_clir(clirstr, &clir) == FALSE)
+               return __ofono_error_invalid_format(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       err = voicecall_dial(vc, number, clir, manager_dial_callback, vc);
+
+       if (err >= 0)
+               return NULL;
+
+       vc->pending = NULL;
+       dbus_message_unref(msg);
+
+       switch (err) {
+       case -EINVAL:
+               return __ofono_error_invalid_format(msg);
+
+       case -ENETDOWN:
+               return __ofono_error_not_available(msg);
+
+       case -ENOTSUP:
+               return __ofono_error_not_implemented(msg);
+       }
+
+       return __ofono_error_failed(msg);
+}
+
+static DBusMessage *manager_transfer(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       int numactive;
+       int numheld;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       numactive = voicecalls_num_active(vc);
+
+       /*
+        * According to 22.091 section 5.8, the network has the option of
+        * implementing the call transfer operation for a call that is
+        * still dialing/alerting.
+        */
+       numactive += voicecalls_num_connecting(vc);
+
+       numheld = voicecalls_num_held(vc);
+
+       if (numactive != 1 || numheld != 1)
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->transfer == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->transfer(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *manager_swap_without_accept(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->swap_without_accept(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+
+static DBusMessage *manager_swap_calls(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->driver->swap_without_accept)
+               return manager_swap_without_accept(conn, msg, data);
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (voicecalls_have_waiting(vc))
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->hold_all_active == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->hold_all_active(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *manager_release_and_answer(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (!voicecalls_have_waiting(vc))
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->release_all_active == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->release_all_active(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *manager_hold_and_answer(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (voicecalls_have_waiting(vc) == FALSE)
+               return __ofono_error_failed(msg);
+
+       /*
+        * We have waiting call and both an active and held call.  According
+        * to 22.030 we cannot use CHLD=2 in this situation.
+        */
+       if (voicecalls_have_active(vc) && voicecalls_have_held(vc))
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->hold_all_active == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->hold_all_active(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *manager_hangup_all(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->pending || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (vc->dial_req && vc->dial_req->call == NULL)
+               return __ofono_error_busy(msg);
+
+       if (vc->driver->hangup_all == NULL &&
+               (vc->driver->release_specific == NULL ||
+                       vc->driver->hangup_active == NULL))
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->call_list == NULL) {
+               DBusMessage *reply = dbus_message_new_method_return(msg);
+               return reply;
+       }
+
+       vc->pending = dbus_message_ref(msg);
+
+       if (vc->driver->hangup_all) {
+               vc->driver->hangup_all(vc, generic_callback, vc);
+               return NULL;
+       }
+
+       if (voicecalls_num_held(vc) > 0)
+               vc->driver->hangup_active(vc, hangup_all_active, vc);
+       else
+               vc->driver->hangup_active(vc, generic_callback, vc);
+
+       return NULL;
+}
+
+static void multiparty_callback_common(struct ofono_voicecall *vc,
+                                       DBusMessage *reply)
+{
+       DBusMessageIter iter;
+       DBusMessageIter array_iter;
+       char **objpath_list;
+       int i;
+
+       voicecalls_path_list(vc, vc->multiparty_list, &objpath_list);
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+               DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+       for (i = 0; objpath_list[i]; i++)
+               dbus_message_iter_append_basic(&array_iter,
+                       DBUS_TYPE_OBJECT_PATH, &objpath_list[i]);
+
+       dbus_message_iter_close_container(&iter, &array_iter);
+
+       g_strfreev(objpath_list);
+}
+
+static void private_chat_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+       const char *callpath;
+       const char *c;
+       int id;
+       GSList *l;
+       GSList *old;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("command failed with error: %s",
+                               telephony_error_to_str(error));
+               __ofono_dbus_pending_reply(&vc->pending,
+                                       __ofono_error_failed(vc->pending));
+               return;
+       }
+
+       dbus_message_get_args(vc->pending, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &callpath,
+                               DBUS_TYPE_INVALID);
+
+       c = strrchr(callpath, '/');
+       sscanf(c, "/voicecall%2u", &id);
+
+       old = g_slist_copy(vc->multiparty_list);
+
+       l = g_slist_find_custom(vc->multiparty_list, GINT_TO_POINTER(id),
+                               call_compare_by_id);
+
+       if (l) {
+               vc->multiparty_list =
+                       g_slist_remove(vc->multiparty_list, l->data);
+
+               if (vc->multiparty_list->next == NULL) {
+                       g_slist_free(vc->multiparty_list);
+                       vc->multiparty_list = NULL;
+               }
+       }
+
+       reply = dbus_message_new_method_return(vc->pending);
+       multiparty_callback_common(vc, reply);
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+
+       voicecalls_multiparty_changed(old, vc->multiparty_list);
+       g_slist_free(old);
+}
+
+static DBusMessage *multiparty_private_chat(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       const char *path = __ofono_atom_get_path(vc->atom);
+       const char *callpath;
+       const char *c;
+       unsigned int id;
+       GSList *l;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &callpath,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (strlen(callpath) == 0)
+               return __ofono_error_invalid_format(msg);
+
+       c = strrchr(callpath, '/');
+
+       if (c == NULL || strncmp(path, callpath, c-callpath))
+               return __ofono_error_not_found(msg);
+
+       if (!sscanf(c, "/voicecall%2u", &id))
+               return __ofono_error_not_found(msg);
+
+       for (l = vc->multiparty_list; l; l = l->next) {
+               struct voicecall *v = l->data;
+               if (v->call->id == id)
+                       break;
+       }
+
+       if (l == NULL)
+               return __ofono_error_not_found(msg);
+
+       /*
+        * If we found id on the list of multiparty calls, then by definition
+        * the multiparty call exists.  Only thing to check is whether we have
+        * held calls
+        */
+       if (voicecalls_have_held(vc))
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->private_chat == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->private_chat(vc, id, private_chat_callback, vc);
+
+       return NULL;
+}
+
+static void multiparty_create_callback(const struct ofono_error *error,
+                                       void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+       GSList *old;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               DBG("command failed with error: %s",
+                               telephony_error_to_str(error));
+               __ofono_dbus_pending_reply(&vc->pending,
+                                       __ofono_error_failed(vc->pending));
+               return;
+       }
+
+       /*
+        * We just created a multiparty call, gather all held
+        * active calls and add them to the multiparty list
+        */
+       old = vc->multiparty_list;
+       vc->multiparty_list = NULL;
+
+       vc->multiparty_list = g_slist_concat(vc->multiparty_list,
+                                               voicecalls_held_list(vc));
+
+       vc->multiparty_list = g_slist_concat(vc->multiparty_list,
+                                               voicecalls_active_list(vc));
+
+       vc->multiparty_list = g_slist_sort(vc->multiparty_list,
+                                               call_compare);
+
+       if (g_slist_length(vc->multiparty_list) < 2) {
+               ofono_error("Created multiparty call, but size is less than 2"
+                               " panic!");
+
+               __ofono_dbus_pending_reply(&vc->pending,
+                                       __ofono_error_failed(vc->pending));
+               return;
+       }
+
+       reply = dbus_message_new_method_return(vc->pending);
+       multiparty_callback_common(vc, reply);
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+
+       voicecalls_multiparty_changed(old, vc->multiparty_list);
+       g_slist_free(old);
+}
+
+static DBusMessage *multiparty_create(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (!voicecalls_have_held(vc) || !voicecalls_have_active(vc))
+               return __ofono_error_failed(msg);
+
+       if (vc->driver->create_multiparty == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       vc->driver->create_multiparty(vc, multiparty_create_callback, vc);
+
+       return NULL;
+}
+
+static DBusMessage *multiparty_hangup(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return __ofono_error_busy(msg);
+
+       if (vc->driver->release_specific == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->driver->release_all_held == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->driver->release_all_active == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       if (vc->multiparty_list == NULL) {
+               DBusMessage *reply = dbus_message_new_method_return(msg);
+               return reply;
+       }
+
+       vc->pending = dbus_message_ref(msg);
+
+       /* We don't have waiting calls, as we can't use +CHLD to release */
+       if (!voicecalls_have_waiting(vc)) {
+               struct voicecall *v = vc->multiparty_list->data;
+
+               if (v->call->status == CALL_STATUS_HELD) {
+                       vc->driver->release_all_held(vc, generic_callback,
+                                                       vc);
+                       goto out;
+               }
+
+               /*
+                * Multiparty is currently active, if we have held calls
+                * we shouldn't use release_all_active here since this also
+                * has the side-effect of activating held calls
+                */
+               if (!voicecalls_have_held(vc)) {
+                       vc->driver->release_all_active(vc, generic_callback,
+                                                               vc);
+                       goto out;
+               }
+       }
+
+       /* Fall back to the old-fashioned way */
+       voicecalls_release_queue(vc, vc->multiparty_list,
+                                       voicecalls_release_done, FALSE);
+       voicecalls_release_next(vc);
+
+out:
+       return NULL;
+}
+
+static void tone_callback(int error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+
+       if (error)
+               reply = __ofono_error_failed(vc->pending);
+       else
+               reply = dbus_message_new_method_return(vc->pending);
+
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+}
+
+static DBusMessage *manager_tone(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       const char *in_tones;
+       char *tones;
+       int err, len;
+
+       if (vc->pending)
+               return __ofono_error_busy(msg);
+
+       if (vc->driver->send_tones == NULL)
+               return __ofono_error_not_implemented(msg);
+
+       /* Send DTMFs only if we have at least one connected call */
+       if (!voicecalls_can_dtmf(vc))
+               return __ofono_error_failed(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &in_tones,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       len = strlen(in_tones);
+
+       if (len == 0)
+               return __ofono_error_invalid_format(msg);
+
+       tones = g_ascii_strup(in_tones, len);
+
+       err = tone_queue(vc, tones, tone_callback, vc, NULL);
+
+       g_free(tones);
+
+       if (err < 0)
+               return __ofono_error_invalid_format(msg);
+
+       vc->pending = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *manager_get_calls(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter array;
+       DBusMessageIter entry, dict;
+       const char *path;
+       GSList *l;
+       struct voicecall *v;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+
+       for (l = vc->call_list; l; l = l->next) {
+               v = l->data;
+
+               path = voicecall_build_path(vc, v->call);
+
+               dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT,
+                                                       NULL, &entry);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                                               &path);
+               dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+                                       OFONO_PROPERTIES_ARRAY_SIGNATURE,
+                                       &dict);
+
+               append_voicecall_properties(v, &dict);
+               dbus_message_iter_close_container(&entry, &dict);
+               dbus_message_iter_close_container(&array, &entry);
+       }
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static GDBusMethodTable manager_methods[] = {
+       { "GetProperties",     "",    "a{sv}",      manager_get_properties },
+       { "Dial",              "ss",  "o",          manager_dial,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Transfer",          "",    "",           manager_transfer,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SwapCalls",         "",    "",           manager_swap_calls,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "ReleaseAndAnswer",  "",    "",           manager_release_and_answer,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "HoldAndAnswer",     "",    "",           manager_hold_and_answer,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "HangupAll",         "",    "",           manager_hangup_all,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "PrivateChat",       "o",   "ao",         multiparty_private_chat,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "CreateMultiparty",  "",    "ao",         multiparty_create,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "HangupMultiparty",  "",    "",           multiparty_hangup,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "SendTones",         "s",   "",           manager_tone,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "GetCalls",          "",    "a(oa{sv})",  manager_get_calls },
+       { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+       { "Forwarded",          "s" },
+       { "BarringActive",      "s" },
+       { "PropertyChanged",    "sv" },
+       { "CallAdded",          "oa{sv}" },
+       { "CallRemoved",        "o" },
+       { }
+};
+
+void ofono_voicecall_disconnected(struct ofono_voicecall *vc, int id,
+                               enum ofono_disconnect_reason reason,
+                               const struct ofono_error *error)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       GSList *l;
+       struct voicecall *call;
+       time_t ts;
+       enum call_status prev_status;
+       const char *number;
+
+       DBG("Got disconnection event for id: %d, reason: %d", id, reason);
+
+       __ofono_modem_callid_release(modem, id);
+
+       l = g_slist_find_custom(vc->call_list, GUINT_TO_POINTER(id),
+                               call_compare_by_id);
+
+       if (l == NULL) {
+               ofono_error("Plugin notified us of call disconnect for"
+                               " unknown call");
+               return;
+       }
+
+       call = l->data;
+
+       ts = time(NULL);
+       prev_status = call->call->status;
+
+       l = g_slist_find_custom(vc->multiparty_list, GUINT_TO_POINTER(id),
+                               call_compare_by_id);
+
+       if (l) {
+               vc->multiparty_list =
+                       g_slist_remove(vc->multiparty_list, call);
+
+               if (vc->multiparty_list->next == NULL) { /* Size == 1 */
+                       struct voicecall *v = vc->multiparty_list->data;
+
+                       voicecall_emit_multiparty(v, FALSE);
+                       g_slist_free(vc->multiparty_list);
+                       vc->multiparty_list = NULL;
+               }
+       }
+
+       vc->release_list = g_slist_remove(vc->release_list, call);
+
+       if (reason != OFONO_DISCONNECT_REASON_UNKNOWN)
+               voicecall_emit_disconnect_reason(call, reason);
+
+       number = phone_number_to_string(&call->call->phone_number);
+       if (is_emergency_number(vc, number) == TRUE)
+               __ofono_modem_dec_emergency_mode(modem);
+
+       voicecall_set_call_status(call, CALL_STATUS_DISCONNECTED);
+
+       if (!call->untracked) {
+               if (prev_status == CALL_STATUS_INCOMING ||
+                               prev_status == CALL_STATUS_WAITING)
+                       __ofono_history_call_missed(modem, call->call, ts);
+               else
+                       __ofono_history_call_ended(modem, call->call,
+                                                       call->detect_time, ts);
+       }
+
+       voicecalls_emit_call_removed(vc, call);
+
+       voicecall_dbus_unregister(vc, call);
+
+       vc->call_list = g_slist_remove(vc->call_list, call);
+}
+
+void ofono_voicecall_notify(struct ofono_voicecall *vc,
+                               const struct ofono_call *call)
+{
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       GSList *l;
+       struct voicecall *v = NULL;
+       struct ofono_call *newcall;
+
+       DBG("Got a voicecall event, status: %d, id: %u, number: %s"
+                       " called_number: %s, called_name %s", call->status,
+                       call->id, call->phone_number.number,
+                       call->called_number.number, call->name);
+
+       l = g_slist_find_custom(vc->call_list, GUINT_TO_POINTER(call->id),
+                               call_compare_by_id);
+
+       if (l) {
+               DBG("Found call with id: %d", call->id);
+               voicecall_set_call_status(l->data, call->status);
+               voicecall_set_call_lineid(l->data, &call->phone_number,
+                                               call->clip_validity);
+               voicecall_set_call_calledid(l->data, &call->called_number);
+               voicecall_set_call_name(l->data, call->name,
+                                               call->cnap_validity);
+
+               return;
+       }
+
+       DBG("Did not find a call with id: %d", call->id);
+
+       __ofono_modem_callid_hold(modem, call->id);
+
+       newcall = g_memdup(call, sizeof(struct ofono_call));
+       if (newcall == NULL) {
+               ofono_error("Unable to allocate call");
+               goto error;
+       }
+
+       v = voicecall_create(vc, newcall);
+       if (v == NULL) {
+               ofono_error("Unable to allocate voicecall_data");
+               goto error;
+       }
+
+       if (vc->flags & VOICECALL_FLAG_STK_MODEM_CALLSETUP) {
+               struct dial_request *req = vc->dial_req;
+               const char *number = phone_number_to_string(&req->ph);
+
+               if (!strcmp(number, "112")) {
+                       struct ofono_modem *modem =
+                                       __ofono_atom_get_modem(vc->atom);
+
+                       __ofono_modem_inc_emergency_mode(modem);
+               }
+
+               if (v->call->clip_validity == CLIP_VALIDITY_NOT_AVAILABLE) {
+                       char *number = v->call->phone_number.number;
+
+                       v->call->phone_number.type = req->ph.type;
+                       strncpy(number, req->ph.number,
+                                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+                       v->call->clip_validity = CLIP_VALIDITY_VALID;
+                       number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+               }
+
+               v->message = req->message;
+               v->icon_id = req->icon_id;
+
+               req->message = NULL;
+               req->call = v;
+
+               /*
+                * TS 102 223 Section 6.4.13: The terminal shall not store
+                * in the UICC the call set-up details (called party number
+                * and associated parameters)
+                */
+               v->untracked = TRUE;
+               vc->flags &= ~VOICECALL_FLAG_STK_MODEM_CALLSETUP;
+       }
+
+       v->detect_time = time(NULL);
+
+       if (!voicecall_dbus_register(v)) {
+               ofono_error("Unable to register voice call");
+               goto error;
+       }
+
+       vc->call_list = g_slist_insert_sorted(vc->call_list, v, call_compare);
+
+       voicecalls_emit_call_added(vc, v);
+
+       return;
+
+error:
+       if (newcall)
+               g_free(newcall);
+
+       if (v)
+               g_free(v);
+}
+
+static void generic_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       DBusMessage *reply;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               DBG("command failed with error: %s",
+                               telephony_error_to_str(error));
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               reply = dbus_message_new_method_return(vc->pending);
+       else
+               reply = __ofono_error_failed(vc->pending);
+
+       __ofono_dbus_pending_reply(&vc->pending, reply);
+}
+
+static void hangup_all_active(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               __ofono_dbus_pending_reply(&vc->pending,
+                                       __ofono_error_failed(vc->pending));
+               return;
+       }
+
+       /*
+        * If we have waiting call, we cannot use CHLD=0 due to side effects
+        * to that call.  Instead we try to hangup all calls one by one,
+        * which might fail if the modem / AG does not support release_specific
+        * for held calls.  In that case the waiting call and held calls will
+        * remain.
+        */
+       if (vc->driver->release_all_held == NULL ||
+                       voicecalls_have_waiting(vc)) {
+               GSList *held = voicecalls_held_list(vc);
+
+               voicecalls_release_queue(vc, held,
+                                               voicecalls_release_done, FALSE);
+               voicecalls_release_next(vc);
+
+               g_slist_free(held);
+       } else
+               vc->driver->release_all_held(vc, generic_callback, vc);
+}
+
+static void multirelease_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->release_list != NULL) {
+               voicecalls_release_next(vc);
+               return;
+       }
+
+       vc->release_queue_done_cb(error, vc);
+}
+
+static void emit_en_list_changed(struct ofono_voicecall *vc)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(vc->atom);
+       char **list;
+       int i;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       list = g_new0(char *, g_hash_table_size(vc->en_list) + 1);
+       g_hash_table_iter_init(&iter, vc->en_list);
+
+       for (i = 0; g_hash_table_iter_next(&iter, &key, &value); i++)
+               list[i] = key;
+
+       ofono_dbus_signal_array_property_changed(conn, path,
+                               OFONO_VOICECALL_MANAGER_INTERFACE,
+                               "EmergencyNumbers", DBUS_TYPE_STRING, &list);
+
+       g_free(list);
+}
+
+static void set_new_ecc(struct ofono_voicecall *vc)
+{
+       g_hash_table_destroy(vc->en_list);
+
+       vc->en_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, NULL);
+
+       /* Emergency numbers from modem/network */
+       if (vc->nw_en_list)
+               add_to_en_list(vc, vc->nw_en_list);
+
+       /* Emergency numbers read from SIM */
+       if (vc->flags & VOICECALL_FLAG_SIM_ECC_READY) {
+               GSList *l;
+
+               for (l = vc->sim_en_list; l; l = l->next)
+                       g_hash_table_insert(vc->en_list, g_strdup(l->data),
+                                                       NULL);
+       } else
+               add_to_en_list(vc, (char **) default_en_list_no_sim);
+
+       /* Default emergency numbers */
+       add_to_en_list(vc, (char **) default_en_list);
+
+       emit_en_list_changed(vc);
+}
+
+static void free_sim_ecc_numbers(struct ofono_voicecall *vc, gboolean old_only)
+{
+       /*
+        * Free the currently being read EN list, just in case the
+        * we're still reading them
+        */
+       if (old_only == FALSE) {
+               if (vc->new_sim_en_list) {
+                       g_slist_foreach(vc->new_sim_en_list, (GFunc) g_free,
+                                       NULL);
+                       g_slist_free(vc->new_sim_en_list);
+                       vc->new_sim_en_list = NULL;
+               }
+
+               vc->flags &= ~VOICECALL_FLAG_SIM_ECC_READY;
+       }
+
+       if (vc->sim_en_list) {
+               g_slist_foreach(vc->sim_en_list, (GFunc) g_free, NULL);
+               g_slist_free(vc->sim_en_list);
+               vc->sim_en_list = NULL;
+       }
+}
+
+static void ecc_g2_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       char en[7];
+
+       DBG("%d", ok);
+
+       if (!ok)
+               return;
+
+       if (total_length < 3) {
+               ofono_error("Unable to read emergency numbers from SIM");
+               return;
+       }
+
+       free_sim_ecc_numbers(vc, TRUE);
+
+       total_length /= 3;
+       while (total_length--) {
+               extract_bcd_number(data, 3, en);
+               data += 3;
+
+               if (en[0] != '\0')
+                       vc->sim_en_list = g_slist_prepend(vc->sim_en_list,
+                                                               g_strdup(en));
+       }
+
+       vc->flags |= VOICECALL_FLAG_SIM_ECC_READY;
+
+       set_new_ecc(vc);
+}
+
+static void ecc_g3_read_cb(int ok, int total_length, int record,
+                               const unsigned char *data,
+                               int record_length, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       int total;
+       char en[7];
+
+       DBG("%d", ok);
+
+       if (!ok)
+               goto check;
+
+       if (record_length < 4 || total_length < record_length) {
+               ofono_error("Unable to read emergency numbers from SIM");
+               return;
+       }
+
+       total = total_length / record_length;
+       extract_bcd_number(data, 3, en);
+
+       if (en[0] != '\0')
+               vc->new_sim_en_list = g_slist_prepend(vc->new_sim_en_list,
+                                                       g_strdup(en));
+
+       if (record != total)
+               return;
+
+check:
+       if (!ok && vc->new_sim_en_list == NULL)
+               return;
+
+       free_sim_ecc_numbers(vc, TRUE);
+       vc->sim_en_list = vc->new_sim_en_list;
+       vc->new_sim_en_list = NULL;
+
+       vc->flags |= VOICECALL_FLAG_SIM_ECC_READY;
+
+       set_new_ecc(vc);
+}
+
+void ofono_voicecall_en_list_notify(struct ofono_voicecall *vc,
+                                               char **nw_en_list)
+{
+       g_strfreev(vc->nw_en_list);
+
+       vc->nw_en_list = g_strdupv(nw_en_list);
+       set_new_ecc(vc);
+}
+
+int ofono_voicecall_driver_register(const struct ofono_voicecall_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       if (d->probe == NULL)
+               return -EINVAL;
+
+       g_drivers = g_slist_prepend(g_drivers, (void *) d);
+
+       return 0;
+}
+
+void ofono_voicecall_driver_unregister(const struct ofono_voicecall_driver *d)
+{
+       DBG("driver: %p, name: %s", d, d->name);
+
+       g_drivers = g_slist_remove(g_drivers, (void *) d);
+}
+
+static void emulator_remove_handler(struct ofono_atom *atom, void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+
+       ofono_emulator_remove_handler(em, data);
+}
+
+static void emulator_hfp_unregister(struct ofono_atom *atom)
+{
+       struct ofono_voicecall *vc = __ofono_atom_get_data(atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_call_status_cb, 0);
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_callsetup_status_cb,
+                                               0);
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_callheld_status_cb, 0);
+
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "A");
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "+CHUP");
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "+CLCC");
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "+CHLD");
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "+VTS");
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "D");
+       __ofono_modem_foreach_registered_atom(modem,
+                                               OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                               emulator_remove_handler,
+                                               "+BLDN");
+
+       __ofono_modem_remove_atom_watch(modem, vc->hfp_watch);
+}
+
+static void voicecall_load_settings(struct ofono_voicecall *vc)
+{
+       const char *imsi;
+
+       imsi = ofono_sim_get_imsi(vc->sim);
+       if (imsi == NULL)
+               return;
+
+       vc->settings = storage_open(imsi, SETTINGS_STORE);
+
+       if (vc->settings == NULL)
+               return;
+
+       vc->imsi = g_strdup(imsi);
+}
+
+static void voicecall_close_settings(struct ofono_voicecall *vc)
+{
+       if (vc->settings) {
+               storage_close(vc->imsi, SETTINGS_STORE, vc->settings, TRUE);
+
+               g_free(vc->imsi);
+               vc->imsi = NULL;
+               vc->settings = NULL;
+       }
+}
+
+static void voicecall_unregister(struct ofono_atom *atom)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_voicecall *vc = __ofono_atom_get_data(atom);
+       struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+       const char *path = __ofono_atom_get_path(atom);
+       GSList *l;
+
+       emulator_hfp_unregister(atom);
+
+       voicecall_close_settings(vc);
+
+       if (vc->sim_state_watch) {
+               ofono_sim_remove_state_watch(vc->sim, vc->sim_state_watch);
+               vc->sim_state_watch = 0;
+       }
+
+       if (vc->sim_watch) {
+               __ofono_modem_remove_atom_watch(modem, vc->sim_watch);
+               vc->sim_watch = 0;
+       }
+
+       vc->sim = NULL;
+
+       free_sim_ecc_numbers(vc, FALSE);
+
+       if (vc->nw_en_list) {
+               g_strfreev(vc->nw_en_list);
+               vc->nw_en_list = NULL;
+       }
+
+       g_hash_table_destroy(vc->en_list);
+       vc->en_list = NULL;
+
+       if (vc->dial_req)
+               dial_request_finish(vc);
+
+       for (l = vc->call_list; l; l = l->next)
+               voicecall_dbus_unregister(vc, l->data);
+
+       g_slist_free(vc->call_list);
+       vc->call_list = NULL;
+
+       ofono_modem_remove_interface(modem, OFONO_VOICECALL_MANAGER_INTERFACE);
+       g_dbus_unregister_interface(conn, path,
+                                       OFONO_VOICECALL_MANAGER_INTERFACE);
+}
+
+static void voicecall_remove(struct ofono_atom *atom)
+{
+       struct ofono_voicecall *vc = __ofono_atom_get_data(atom);
+
+       DBG("atom: %p", atom);
+
+       if (vc == NULL)
+               return;
+
+       if (vc->driver && vc->driver->remove)
+               vc->driver->remove(vc);
+
+       if (vc->tone_source) {
+               g_source_remove(vc->tone_source);
+               vc->tone_source = 0;
+       }
+
+       if (vc->toneq) {
+               struct tone_queue_entry *entry;
+
+               while ((entry = g_queue_peek_head(vc->toneq)))
+                       tone_request_finish(vc, entry, ESHUTDOWN, TRUE);
+
+               g_queue_free(vc->toneq);
+       }
+
+       g_free(vc);
+}
+
+struct ofono_voicecall *ofono_voicecall_create(struct ofono_modem *modem,
+                                               unsigned int vendor,
+                                               const char *driver,
+                                               void *data)
+{
+       struct ofono_voicecall *vc;
+       GSList *l;
+
+       if (driver == NULL)
+               return NULL;
+
+       vc = g_try_new0(struct ofono_voicecall, 1);
+
+       if (vc == NULL)
+               return NULL;
+
+       vc->toneq = g_queue_new();
+
+       vc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_VOICECALL,
+                                               voicecall_remove, vc);
+
+       for (l = g_drivers; l; l = l->next) {
+               const struct ofono_voicecall_driver *drv = l->data;
+
+               if (g_strcmp0(drv->name, driver))
+                       continue;
+
+               if (drv->probe(vc, vendor, data) < 0)
+                       continue;
+
+               vc->driver = drv;
+               break;
+       }
+
+       return vc;
+}
+
+static void read_sim_ecc_numbers(int id, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+
+       /* Try both formats, only one or none will work */
+       ofono_sim_read(vc->sim_context, SIM_EFECC_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+                       ecc_g2_read_cb, vc);
+       ofono_sim_read(vc->sim_context, SIM_EFECC_FILEID,
+                       OFONO_SIM_FILE_STRUCTURE_FIXED,
+                       ecc_g3_read_cb, vc);
+}
+
+static void sim_state_watch(enum ofono_sim_state new_state, void *user)
+{
+       struct ofono_voicecall *vc = user;
+
+       switch (new_state) {
+       case OFONO_SIM_STATE_INSERTED:
+               if (vc->sim_context == NULL)
+                       vc->sim_context = ofono_sim_context_create(vc->sim);
+
+               read_sim_ecc_numbers(SIM_EFECC_FILEID, vc);
+
+               ofono_sim_add_file_watch(vc->sim_context, SIM_EFECC_FILEID,
+                                               read_sim_ecc_numbers, vc, NULL);
+               break;
+       case OFONO_SIM_STATE_NOT_PRESENT:
+               /* TODO: Must release all non-emergency calls */
+
+               if (vc->sim_context) {
+                       ofono_sim_context_free(vc->sim_context);
+                       vc->sim_context = NULL;
+               }
+
+               free_sim_ecc_numbers(vc, FALSE);
+               set_new_ecc(vc);
+
+               voicecall_close_settings(vc);
+               break;
+       case OFONO_SIM_STATE_READY:
+               voicecall_load_settings(vc);
+               break;
+       case OFONO_SIM_STATE_LOCKED_OUT:
+               voicecall_close_settings(vc);
+               break;
+       }
+}
+
+static void sim_watch(struct ofono_atom *atom,
+                       enum ofono_atom_watch_condition cond, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       struct ofono_sim *sim = __ofono_atom_get_data(atom);
+
+       if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+               voicecall_close_settings(vc);
+               vc->sim_state_watch = 0;
+               vc->sim = NULL;
+               return;
+       }
+
+       vc->sim = sim;
+       vc->sim_state_watch = ofono_sim_add_state_watch(sim,
+                                                       sim_state_watch,
+                                                       vc, NULL);
+
+       sim_state_watch(ofono_sim_get_state(sim), vc);
+}
+
+static void emulator_generic_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (vc->pending_em == NULL)
+               return;
+
+       ofono_emulator_send_final(vc->pending_em, error);
+       vc->pending_em = NULL;
+}
+
+static void emulator_mpty_join_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       GSList *old;
+
+       if (vc->pending_em != NULL)
+               ofono_emulator_send_final(vc->pending_em, error);
+
+       vc->pending_em = NULL;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               return;
+
+       /*
+        * We just created a multiparty call, gather all held
+        * active calls and add them to the multiparty list
+        */
+       old = vc->multiparty_list;
+       vc->multiparty_list = NULL;
+
+       vc->multiparty_list = g_slist_concat(vc->multiparty_list,
+                                               voicecalls_held_list(vc));
+
+       vc->multiparty_list = g_slist_concat(vc->multiparty_list,
+                                               voicecalls_active_list(vc));
+
+       vc->multiparty_list = g_slist_sort(vc->multiparty_list,
+                                               call_compare);
+
+       if (g_slist_length(vc->multiparty_list) < 2) {
+               ofono_error("Created multiparty call, but size is less than 2"
+                               " panic!");
+               g_slist_free(old);
+               return;
+       }
+
+       voicecalls_multiparty_changed(old, vc->multiparty_list);
+       g_slist_free(old);
+}
+
+static void emulator_mpty_private_chat_cb(const struct ofono_error *error,
+                                                       void *data)
+{
+       struct ofono_voicecall *vc = data;
+       GSList *old;
+       GSList *l;
+
+       if (vc->pending_em != NULL)
+               ofono_emulator_send_final(vc->pending_em, error);
+
+       vc->pending_em = NULL;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+               return;
+
+       old = g_slist_copy(vc->multiparty_list);
+
+       l = g_slist_find_custom(vc->multiparty_list,
+                       GINT_TO_POINTER(vc->pending_id), call_compare_by_id);
+
+       if (l) {
+               vc->multiparty_list =
+                       g_slist_remove(vc->multiparty_list, l->data);
+
+               if (vc->multiparty_list->next == NULL) {
+                       g_slist_free(vc->multiparty_list);
+                       vc->multiparty_list = NULL;
+               }
+       }
+
+       voicecalls_multiparty_changed(old, vc->multiparty_list);
+       g_slist_free(old);
+}
+
+#define CHECK_BUSY(vc, em, result)                             \
+       if (vc->pending || vc->dial_req || vc->pending_em) {    \
+               result.error = 126;                             \
+               result.type = OFONO_ERROR_TYPE_CME;             \
+               ofono_emulator_send_final(em, &result);         \
+       }                                                       \
+
+static void emulator_ata_cb(struct ofono_emulator *em,
+                               struct ofono_emulator_request *req,
+                               void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       struct ofono_error result;
+
+       result.error = 0;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
+               CHECK_BUSY(vc, em, result)
+
+               if (!voicecalls_have_incoming(vc))
+                       goto fail;
+
+               if (vc->driver->answer == NULL)
+                       goto fail;
+
+               vc->pending_em = em;
+               vc->driver->answer(vc, emulator_generic_cb, vc);
+               break;
+
+       default:
+fail:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               ofono_emulator_send_final(em, &result);
+       };
+}
+
+static void emulator_chup_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       struct ofono_error result;
+
+       result.error = 0;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
+               if (vc->pending || vc->pending_em)
+                       goto fail;
+
+               if (vc->dial_req && vc->dial_req->call == NULL)
+                       goto fail;
+
+               if (vc->driver->release_specific == NULL &&
+                               vc->driver->hangup_active == NULL)
+                       goto fail;
+
+               if (vc->driver->hangup_active) {
+                       vc->pending_em = em;
+                       vc->driver->hangup_active(vc, emulator_generic_cb, vc);
+                       goto done;
+               }
+
+               if (voicecalls_have_active(vc) == FALSE &&
+                               voicecalls_have_incoming(vc) == FALSE)
+                       goto fail;
+
+               vc->pending_em = em;
+               voicecalls_release_queue(vc, vc->call_list,
+                                               emulator_generic_cb, TRUE);
+               voicecalls_release_next(vc);
+
+done:
+               break;
+
+       default:
+fail:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               ofono_emulator_send_final(em, &result);
+       };
+}
+
+static void emulator_clcc_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       struct ofono_error result;
+       GSList *l;
+       /*
+        *          idx   dir  stat  mode  mpty
+        * '+CLCC: <0-7>,<0-1>,<0-5>,<0-9>,<0-1>,"",' +
+        * phone number + phone type on 3 digits + terminating null
+        */
+       char buf[20 + OFONO_MAX_PHONE_NUMBER_LENGTH + 3 + 1];
+
+       result.error = 0;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
+               for (l = vc->call_list; l; l = l->next) {
+                       struct voicecall *v = l->data;
+                       const char *number = "";
+                       int type = 128;
+                       gboolean mpty;
+
+                       if (g_slist_find_custom(vc->multiparty_list,
+                                               GINT_TO_POINTER(v->call->id),
+                                               call_compare_by_id))
+                               mpty = TRUE;
+                       else
+                               mpty = FALSE;
+
+                       if (v->call->clip_validity == CLIP_VALIDITY_VALID) {
+                               number = v->call->phone_number.number;
+                               type = v->call->phone_number.type;
+                       }
+
+                       sprintf(buf, "+CLCC: %d,%d,%d,0,%d,\"%s\",%d",
+                                       v->call->id, v->call->direction,
+                                       v->call->status, mpty, number, type);
+                       ofono_emulator_send_info(em, buf, l->next == NULL ?
+                                                       TRUE : FALSE);
+               }
+
+               result.type = OFONO_ERROR_TYPE_NO_ERROR;
+               break;
+
+       default:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+       }
+
+       ofono_emulator_send_final(em, &result);
+}
+
+#define ADD_CHLD_SUPPORT(cond, x)                      \
+       if (cond) {                                     \
+               if (info[-1] != '(')                    \
+                       *info++ = ',';                  \
+                                                       \
+               *info++ = x[0];                         \
+                                                       \
+               if (x[1])                               \
+                       *info++ = x[1];                 \
+       }                                               \
+
+static void emulator_chld_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       struct ofono_error result;
+       char buf[64];
+       char *info;
+       int chld;
+
+       result.error = 0;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_SET:
+               if (!ofono_emulator_request_next_number(req, &chld))
+                       goto fail;
+
+               CHECK_BUSY(vc, em, result)
+
+               switch (chld) {
+               case 0:
+                       if (vc->driver->set_udub == NULL)
+                               goto fail;
+
+                       if (vc->driver->release_all_held == NULL)
+                               goto fail;
+
+                       vc->pending_em = em;
+
+                       if (voicecalls_have_waiting(vc)) {
+                               vc->driver->set_udub(vc,
+                                               emulator_generic_cb, vc);
+                               return;
+                       }
+
+                       vc->driver->release_all_held(vc,
+                                       emulator_generic_cb, vc);
+                       return;
+               case 1:
+                       if (vc->driver->release_all_active == NULL)
+                               goto fail;
+
+                       vc->pending_em = em;
+                       vc->driver->release_all_active(vc,
+                                       emulator_generic_cb, vc);
+                       return;
+               case 2:
+                       if (vc->driver->hold_all_active == NULL)
+                               goto fail;
+
+                       vc->pending_em = em;
+                       vc->driver->hold_all_active(vc,
+                                       emulator_generic_cb, vc);
+                       return;
+               case 3:
+                       if (vc->driver->create_multiparty == NULL)
+                               goto fail;
+
+                       if (!voicecalls_have_held(vc) ||
+                                       !voicecalls_have_active(vc))
+                               goto fail;
+
+                       vc->pending_em = em;
+                       vc->driver->create_multiparty(vc,
+                                       emulator_mpty_join_cb, vc);
+                       return;
+               case 4:
+                       if (vc->driver->transfer == NULL)
+                               goto fail;
+
+                       vc->pending_em = em;
+                       vc->driver->transfer(vc,
+                                       emulator_generic_cb, vc);
+                       return;
+               default:
+                       break;
+               }
+
+               if (chld >= 11 && chld <= 17) {
+                       if (vc->driver->release_specific == NULL)
+                               goto fail;
+
+                       vc->pending_em = em;
+                       vc->driver->release_specific(vc, chld - 10,
+                                               emulator_generic_cb, vc);
+                       return;
+               }
+
+               if (chld >= 21 && chld <= 27) {
+                       GSList *l;
+                       unsigned int id = chld - 20;
+
+                       if (vc->driver->private_chat == NULL)
+                               goto fail;
+
+                       for (l = vc->multiparty_list; l; l = l->next) {
+                               struct voicecall *v = l->data;
+                               if (v->call->id == id)
+                                       break;
+                       }
+
+                       if (l == NULL)
+                               goto fail;
+
+                       if (voicecalls_have_held(vc))
+                               goto fail;
+
+                       vc->pending_em = em;
+                       vc->pending_id = id;
+
+                       vc->driver->private_chat(vc, id,
+                                       emulator_mpty_private_chat_cb, vc);
+                       return;
+               }
+
+               goto fail;
+
+       case OFONO_EMULATOR_REQUEST_TYPE_SUPPORT:
+               memcpy(buf, "+CHLD: (", 8);
+               info = buf + 8;
+
+               ADD_CHLD_SUPPORT(vc->driver->release_all_held &&
+                                       vc->driver->set_udub, "0")
+               ADD_CHLD_SUPPORT(vc->driver->release_all_active, "1")
+               ADD_CHLD_SUPPORT(vc->driver->release_specific, "1x")
+               ADD_CHLD_SUPPORT(vc->driver->hold_all_active, "2")
+               ADD_CHLD_SUPPORT(vc->driver->private_chat, "2x")
+               ADD_CHLD_SUPPORT(vc->driver->create_multiparty, "3")
+               ADD_CHLD_SUPPORT(vc->driver->transfer, "4")
+
+               *info++ = ')';
+               *info++ = '\0';
+
+               ofono_emulator_send_info(em, buf, TRUE);
+               result.type = OFONO_ERROR_TYPE_NO_ERROR;
+               break;
+
+       case OFONO_EMULATOR_REQUEST_TYPE_QUERY:
+       case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
+fail:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+       }
+
+       ofono_emulator_send_final(em, &result);
+}
+
+static void vts_tone_cb(int error, void *data)
+{
+       struct ofono_emulator *em = data;
+       struct ofono_error result;
+
+       result.error = 0;
+       result.type = error ? OFONO_ERROR_TYPE_FAILURE :
+                                               OFONO_ERROR_TYPE_NO_ERROR;
+
+       ofono_emulator_send_final(em, &result);
+}
+
+static void emulator_vts_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       struct ofono_error result;
+       const char *str;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_SET:
+               str = ofono_emulator_request_get_raw(req);
+               if (str == NULL)
+                       break;
+
+               if (!g_ascii_isdigit(str[0]) && str[0] != '*' &&
+                               str[0] != '#' && (str[0] < 'A' || str[0] > 'D'))
+                       break;
+
+               if (str[1] != '\0')
+                       break;
+
+               if (__ofono_voicecall_tone_send(vc, str, vts_tone_cb, em) >= 0)
+                       return;
+
+               break;
+
+       default:
+               break;
+       }
+
+       result.error = 0;
+       result.type = OFONO_ERROR_TYPE_FAILURE;
+
+       ofono_emulator_send_final(em, &result);
+}
+
+static void emulator_dial_callback(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       gboolean need_to_emit;
+       struct voicecall *v;
+       const char *number;
+       GError *err = NULL;
+
+       number = g_key_file_get_string(vc->settings, SETTINGS_GROUP,
+                                       "Number", &err);
+
+       v = dial_handle_result(vc, error, number, &need_to_emit);
+
+       if (v == NULL) {
+               struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+
+               if (is_emergency_number(vc, number) == TRUE)
+                       __ofono_modem_dec_emergency_mode(modem);
+       }
+
+       if (vc->pending_em)
+               ofono_emulator_send_final(vc->pending_em, error);
+
+       vc->pending_em = NULL;
+
+       if (need_to_emit)
+               voicecalls_emit_call_added(vc, v);
+}
+
+static void emulator_dial(struct ofono_emulator *em, struct ofono_voicecall *vc,
+                               const char *number)
+{
+       struct ofono_error result;
+       int err;
+
+       result.error = 0;
+
+       if (vc->pending || vc->dial_req || vc->pending_em) {
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               goto send;
+       }
+
+       vc->pending_em = em;
+
+       err = voicecall_dial(vc, number, OFONO_CLIR_OPTION_DEFAULT,
+                                       emulator_dial_callback, vc);
+
+       if (err >= 0)
+               return;
+
+       vc->pending_em = NULL;
+
+       switch (err) {
+       case -ENETDOWN:
+               result.error = 30;
+               result.type = OFONO_ERROR_TYPE_CME;
+               break;
+
+       default:
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+       }
+
+send:
+       ofono_emulator_send_final(em, &result);
+}
+
+static void emulator_atd_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       const char *str;
+       size_t len;
+       char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1];
+       struct ofono_error result;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_SET:
+               str = ofono_emulator_request_get_raw(req);
+
+               if (str == NULL || str[0] == '\0')
+                       goto fail;
+
+               len = strlen(str);
+
+               if (len > OFONO_MAX_PHONE_NUMBER_LENGTH + 1 ||
+                               str[len - 1] != ';')
+                       goto fail;
+
+               if (len == 3 && str[0] == '>' && str[1] == '1') {
+                       struct ofono_message_waiting *mw;
+                       const struct ofono_phone_number *ph;
+                       const char *num;
+
+                       mw = __ofono_atom_find(OFONO_ATOM_TYPE_MESSAGE_WAITING,
+                                               modem);
+                       if (mw == NULL)
+                               goto fail;
+
+                       ph = __ofono_message_waiting_get_mbdn(mw, 0);
+
+                       if (ph == NULL)
+                               goto fail;
+
+                       num = phone_number_to_string(ph);
+
+                       emulator_dial(em, vc, num);
+               } else {
+                       strncpy(number, str, len - 1);
+                       number[len - 1] = '\0';
+
+                       emulator_dial(em, vc, number);
+               }
+
+               break;
+
+       default:
+fail:
+               result.error = 0;
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               ofono_emulator_send_final(em, &result);
+       };
+}
+
+static void emulator_bldn_cb(struct ofono_emulator *em,
+                       struct ofono_emulator_request *req, void *userdata)
+{
+       struct ofono_voicecall *vc = userdata;
+       const char *number;
+       struct ofono_error result;
+       GError *error = NULL;
+
+       switch (ofono_emulator_request_get_type(req)) {
+       case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
+               if (vc->settings == NULL)
+                       goto fail;
+
+               number = g_key_file_get_string(vc->settings, SETTINGS_GROUP,
+                                               "Number", &error);
+               if (number == NULL || number[0] == '\0')
+                       goto fail;
+
+               emulator_dial(em, vc, number);
+               break;
+
+       default:
+fail:
+               result.error = 0;
+               result.type = OFONO_ERROR_TYPE_FAILURE;
+               ofono_emulator_send_final(em, &result);
+       };
+}
+
+static void emulator_hfp_watch(struct ofono_atom *atom,
+                               enum ofono_atom_watch_condition cond,
+                               void *data)
+{
+       struct ofono_emulator *em = __ofono_atom_get_data(atom);
+       struct ofono_voicecall *vc = data;
+
+       switch (cond) {
+       case OFONO_ATOM_WATCH_CONDITION_UNREGISTERED:
+               if (vc->pending_em == em)
+                       vc->pending_em = NULL;
+
+               return;
+       case OFONO_ATOM_WATCH_CONDITION_REGISTERED:
+               break;
+       }
+
+       notify_emulator_call_status(vc);
+
+       ofono_emulator_add_handler(em, "A", emulator_ata_cb, vc, NULL);
+       ofono_emulator_add_handler(em, "+CHUP", emulator_chup_cb, vc, NULL);
+       ofono_emulator_add_handler(em, "+CLCC", emulator_clcc_cb, vc, NULL);
+       ofono_emulator_add_handler(em, "+CHLD", emulator_chld_cb, vc, NULL);
+       ofono_emulator_add_handler(em, "+VTS", emulator_vts_cb, vc, NULL);
+       ofono_emulator_add_handler(em, "D", emulator_atd_cb, vc, NULL);
+       ofono_emulator_add_handler(em, "+BLDN", emulator_bldn_cb, vc, NULL);
+}
+
+void ofono_voicecall_register(struct ofono_voicecall *vc)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+       const char *path = __ofono_atom_get_path(vc->atom);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       OFONO_VOICECALL_MANAGER_INTERFACE,
+                                       manager_methods, manager_signals, NULL,
+                                       vc, NULL)) {
+               ofono_error("Could not create %s interface",
+                               OFONO_VOICECALL_MANAGER_INTERFACE);
+
+               return;
+       }
+
+       ofono_modem_add_interface(modem, OFONO_VOICECALL_MANAGER_INTERFACE);
+
+       vc->en_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, NULL);
+
+       /*
+        * Start out with the 22.101 mandated numbers, if we have a SIM and
+        * the SIM contains EFecc, then we update the list once we've read them
+        */
+       add_to_en_list(vc, (char **) default_en_list_no_sim);
+       add_to_en_list(vc, (char **) default_en_list);
+
+       vc->sim_watch = __ofono_modem_add_atom_watch(modem,
+                                               OFONO_ATOM_TYPE_SIM,
+                                               sim_watch, vc, NULL);
+
+       __ofono_atom_register(vc->atom, voicecall_unregister);
+
+       vc->hfp_watch = __ofono_modem_add_atom_watch(modem,
+                                       OFONO_ATOM_TYPE_EMULATOR_HFP,
+                                       emulator_hfp_watch, vc, NULL);
+}
+
+void ofono_voicecall_remove(struct ofono_voicecall *vc)
+{
+       __ofono_atom_free(vc->atom);
+}
+
+void ofono_voicecall_set_data(struct ofono_voicecall *vc, void *data)
+{
+       vc->driver_data = data;
+}
+
+void *ofono_voicecall_get_data(struct ofono_voicecall *vc)
+{
+       return vc->driver_data;
+}
+
+int ofono_voicecall_get_next_callid(struct ofono_voicecall *vc)
+{
+       struct ofono_modem *modem;
+       if (vc == NULL || vc->atom == NULL)
+               return 0;
+
+       modem = __ofono_atom_get_modem(vc->atom);
+
+       return __ofono_modem_callid_next(modem);
+}
+
+ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc,
+                                       enum ofono_voicecall_interaction type)
+{
+       if (vc->pending || vc->dial_req || vc->pending_em)
+               return TRUE;
+
+       switch (type) {
+       case OFONO_VOICECALL_INTERACTION_NONE:
+               return vc->call_list != NULL;
+       case OFONO_VOICECALL_INTERACTION_DISCONNECT:
+               /* Only support releasing active calls */
+               if (voicecalls_num_active(vc) == g_slist_length(vc->call_list))
+                       return FALSE;
+
+               return TRUE;
+       case OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD:
+               if (voicecalls_num_active(vc) == g_slist_length(vc->call_list))
+                       return FALSE;
+
+               if (voicecalls_num_held(vc) == g_slist_length(vc->call_list))
+                       return FALSE;
+
+               return TRUE;
+       }
+
+       return TRUE;
+}
+
+static void dial_request_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       const char *number = phone_number_to_string(&vc->dial_req->ph);
+       gboolean need_to_emit;
+       struct voicecall *v;
+
+       v = dial_handle_result(vc, error, number, &need_to_emit);
+
+       if (v == NULL) {
+               if (is_emergency_number(vc, number) == TRUE) {
+                       struct ofono_modem *modem =
+                               __ofono_atom_get_modem(vc->atom);
+
+                       __ofono_modem_dec_emergency_mode(modem);
+               }
+
+               dial_request_finish(vc);
+               return;
+       }
+
+       v->message = vc->dial_req->message;
+       v->icon_id = vc->dial_req->icon_id;
+
+       vc->dial_req->message = NULL;
+       vc->dial_req->call = v;
+
+       /*
+        * TS 102 223 Section 6.4.13: The terminal shall not store
+        * in the UICC the call set-up details (called party number
+        * and associated parameters)
+        */
+       v->untracked = TRUE;
+
+       if (v->call->status == CALL_STATUS_ACTIVE)
+               dial_request_finish(vc);
+
+       if (need_to_emit)
+               voicecalls_emit_call_added(vc, v);
+}
+
+static void dial_request(struct ofono_voicecall *vc)
+{
+       const char *number = phone_number_to_string(&vc->dial_req->ph);
+
+       if (is_emergency_number(vc, number) == TRUE) {
+               struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
+
+               __ofono_modem_inc_emergency_mode(modem);
+       }
+
+       vc->driver->dial(vc, &vc->dial_req->ph, OFONO_CLIR_OPTION_DEFAULT,
+                               dial_request_cb, vc);
+}
+
+static void dial_req_disconnect_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               dial_request_finish(vc);
+               return;
+       }
+
+       /*
+        * Note that the callback might come back fore we receive call
+        * disconnection notifications.  So it makes no sense to recheck
+        * whether we can dial here.  We simply dial and hope for the best.
+        */
+       dial_request(vc);
+}
+
+int __ofono_voicecall_dial(struct ofono_voicecall *vc,
+                               const char *addr, int addr_type,
+                               const char *message, unsigned char icon_id,
+                               enum ofono_voicecall_interaction interaction,
+                               ofono_voicecall_dial_cb_t cb, void *user_data)
+{
+       struct dial_request *req;
+
+       if (!valid_phone_number_format(addr))
+               return -EINVAL;
+
+       if (vc->driver->dial == NULL)
+               return -ENOSYS;
+
+       if (interaction == OFONO_VOICECALL_INTERACTION_DISCONNECT &&
+                       vc->driver->release_all_active == NULL)
+               return -ENOSYS;
+
+       if (__ofono_voicecall_is_busy(vc, interaction) == TRUE)
+               return -EBUSY;
+
+       /*
+        * TODO: if addr starts with "112", possibly translate into the
+        * technology-specific emergency number.
+        */
+
+       req = g_try_new0(struct dial_request, 1);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->message = g_strdup(message);
+       req->icon_id = icon_id;
+       req->interaction = interaction;
+       req->cb = cb;
+       req->user_data = user_data;
+
+       /* TODO: parse the tones to dial after call connected */
+       req->ph.type = addr_type;
+       strncpy(req->ph.number, addr, OFONO_MAX_PHONE_NUMBER_LENGTH);
+
+       vc->dial_req = req;
+
+       switch (interaction) {
+       case OFONO_VOICECALL_INTERACTION_NONE:
+               dial_request(vc);
+               break;
+
+       case OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD:
+               /* Note: dialling automatically puts active calls on hold */
+               dial_request(vc);
+               break;
+
+       case OFONO_VOICECALL_INTERACTION_DISCONNECT:
+               if (voicecalls_have_active(vc))
+                       vc->driver->release_all_active(vc,
+                                               dial_req_disconnect_cb, vc);
+               else
+                       dial_request(vc);
+
+               break;
+       }
+
+       return 0;
+}
+
+void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc)
+{
+       if (vc->dial_req == NULL || vc->dial_req->cb == NULL)
+               return;
+
+       vc->dial_req->cb = NULL;
+}
+
+static void tone_request_cb(const struct ofono_error *error, void *data)
+{
+       struct ofono_voicecall *vc = data;
+       struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq);
+       int len = 0;
+
+       if (entry == NULL)
+               return;
+
+       /*
+        * Call back with error only if the error is related to the
+        * current entry.  If the error corresponds to a cancelled
+        * request, do nothing.
+        */
+       if (error && error->type != OFONO_ERROR_TYPE_NO_ERROR &&
+                       entry->left > entry->tone_str) {
+               DBG("command failed with error: %s",
+                               telephony_error_to_str(error));
+
+               tone_request_finish(vc, entry, EIO, TRUE);
+
+               goto done;
+       }
+
+       if (*entry->left == '\0') {
+               tone_request_finish(vc, entry, 0, TRUE);
+
+               goto done;
+       }
+
+       len = strspn(entry->left, "pP");
+       entry->left += len;
+
+done:
+       /*
+        * Wait 3 seconds per PAUSE, same as for DTMF separator characters
+        * passed in a telephone number according to TS 22.101 A.21,
+        * although 27.007 claims this delay can be set using S8 and
+        * defaults to 2 seconds.
+        */
+       vc->tone_source = g_timeout_add_seconds(len * 3, tone_request_run, vc);
+}
+
+static gboolean tone_request_run(gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq);
+       char final;
+       unsigned len;
+
+       vc->tone_source = 0;
+
+       if (entry == NULL)
+               return FALSE;
+
+       len = strcspn(entry->left, "pP");
+
+       if (len) {
+               if (len > 8) /* Arbitrary length limit per request */
+                       len = 8;
+
+               /* Temporarily move the end of the string */
+               final = entry->left[len];
+               entry->left[len] = '\0';
+
+               vc->driver->send_tones(vc, entry->left, tone_request_cb, vc);
+
+               entry->left += len;
+               entry->left[0] = final;
+       } else
+               tone_request_cb(NULL, vc);
+
+       return FALSE;
+}
+
+int __ofono_voicecall_tone_send(struct ofono_voicecall *vc,
+                               const char *tone_str,
+                               ofono_voicecall_tone_cb_t cb, void *user_data)
+{
+       if (vc->driver->send_tones == NULL)
+               return -ENOSYS;
+
+       /* Send DTMFs only if we have at least one connected call */
+       if (!voicecalls_can_dtmf(vc))
+               return -ENOENT;
+
+       return tone_queue(vc, tone_str, cb, user_data, NULL);
+}
+
+void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id)
+{
+       struct tone_queue_entry *entry;
+       int n = 0;
+
+       while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL)
+               if (entry->id == id)
+                       break;
+
+       tone_request_finish(vc, entry, 0, FALSE);
+
+       /*
+        * If we were in the middle of a PAUSE, wake queue up
+        * now, else wake up when current tone finishes.
+        */
+       if (n == 1 && vc->tone_source) {
+               g_source_remove(vc->tone_source);
+               tone_request_run(vc);
+       }
+}
+
+void __ofono_voicecall_set_alpha_and_icon_id(struct ofono_voicecall *vc,
+                                               const char *addr, int addr_type,
+                                               const char *message,
+                                               unsigned char icon_id)
+{
+       struct dial_request *req;
+
+       req = g_new0(struct dial_request, 1);
+
+       req->message = g_strdup(message);
+       req->icon_id = icon_id;
+
+       req->ph.type = addr_type;
+       strncpy(req->ph.number, addr, OFONO_MAX_PHONE_NUMBER_LENGTH);
+
+       vc->dial_req = req;
+
+       vc->flags |= VOICECALL_FLAG_STK_MODEM_CALLSETUP;
+}
+
+void __ofono_voicecall_clear_alpha_and_icon_id(struct ofono_voicecall *vc)
+{
+       g_free(vc->dial_req->message);
+       vc->dial_req->message = NULL;
+
+       g_free(vc->dial_req);
+       vc->dial_req = NULL;
+
+       vc->flags &= ~VOICECALL_FLAG_STK_MODEM_CALLSETUP;
+}
+
+static void ssn_mt_forwarded_notify(struct ofono_voicecall *vc,
+                                       unsigned int id, int code,
+                                       const struct ofono_phone_number *ph)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(vc->atom);
+       char *info = "incoming";
+
+       g_dbus_emit_signal(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE,
+                               "Forwarded",
+                               DBUS_TYPE_STRING, &info,
+                               DBUS_TYPE_INVALID);
+}
+
+static struct voicecall *voicecall_select(struct ofono_voicecall *vc,
+                                               unsigned int id)
+{
+       if (id != 0) {
+               GSList *l = g_slist_find_custom(vc->call_list,
+                                               GUINT_TO_POINTER(id),
+                                               call_compare_by_id);
+
+               if (l == NULL)
+                       return NULL;
+
+               return l->data;
+       }
+
+       if (g_slist_length(vc->call_list) == 1)
+               return vc->call_list->data;
+
+       return NULL;
+}
+
+static void ssn_mt_remote_held_notify(struct ofono_voicecall *vc,
+                                       unsigned int id, gboolean held,
+                                       const struct ofono_phone_number *ph)
+{
+       struct voicecall *v = voicecall_select(vc, id);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+
+       if (v == NULL)
+               return;
+
+       if (v->remote_held == held)
+               return;
+
+       v->remote_held = held;
+       path = voicecall_build_path(vc, v->call);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                               OFONO_VOICECALL_INTERFACE,
+                                               "RemoteHeld", DBUS_TYPE_BOOLEAN,
+                                               &v->remote_held);
+}
+
+static void ssn_mt_remote_multiparty_notify(struct ofono_voicecall *vc,
+                                       unsigned int id,
+                                       const struct ofono_phone_number *ph)
+{
+       struct voicecall *v = voicecall_select(vc, id);
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path;
+
+       if (v == NULL)
+               return;
+
+       if (v->remote_multiparty == TRUE)
+               return;
+
+       v->remote_multiparty = TRUE;
+
+       path = voicecall_build_path(vc, v->call);
+
+       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_VOICECALL_INTERFACE,
+                                       "RemoteMultiparty", DBUS_TYPE_BOOLEAN,
+                                       &v->remote_multiparty);
+}
+
+void ofono_voicecall_ssn_mt_notify(struct ofono_voicecall *vc,
+                                       unsigned int id, int code, int index,
+                                       const struct ofono_phone_number *ph)
+{
+       switch (code) {
+       case SS_MT_CALL_FORWARDED:
+               ssn_mt_forwarded_notify(vc, id, code, ph);
+               break;
+       case SS_MT_VOICECALL_ON_HOLD:
+               ssn_mt_remote_held_notify(vc, id, TRUE, ph);
+               break;
+       case SS_MT_VOICECALL_RETRIEVED:
+               ssn_mt_remote_held_notify(vc, id, FALSE, ph);
+               break;
+       case SS_MT_MULTIPARTY_VOICECALL:
+               ssn_mt_remote_multiparty_notify(vc, id, ph);
+               break;
+       }
+}
+
+static void ssn_mo_call_barred_notify(struct ofono_voicecall *vc,
+                                       unsigned int id, int code)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(vc->atom);
+       const char *info;
+
+       if (code == SS_MO_INCOMING_BARRING)
+               info = "remote";
+       else
+               info = "local";
+
+       g_dbus_emit_signal(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE,
+                               "BarringActive",
+                               DBUS_TYPE_STRING, &info,
+                               DBUS_TYPE_INVALID);
+}
+
+static void ssn_mo_forwarded_notify(struct ofono_voicecall *vc,
+                                       unsigned int id, int code)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(vc->atom);
+       char *info = "outgoing";
+
+       g_dbus_emit_signal(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE,
+                               "Forwarded",
+                               DBUS_TYPE_STRING, &info,
+                               DBUS_TYPE_INVALID);
+}
+
+void ofono_voicecall_ssn_mo_notify(struct ofono_voicecall *vc,
+                                       unsigned int id, int code, int index)
+{
+       switch (code) {
+       case SS_MO_OUTGOING_BARRING:
+       case SS_MO_INCOMING_BARRING:
+               ssn_mo_call_barred_notify(vc, id, code);
+               break;
+       case SS_MO_CALL_FORWARDED:
+               ssn_mo_forwarded_notify(vc, id, code);
+               break;
+       }
+}
diff --git a/src/watch.c b/src/watch.c
new file mode 100644 (file)
index 0000000..dfb01fb
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include "ofono.h"
+
+struct ofono_watchlist *__ofono_watchlist_new(ofono_destroy_func destroy)
+{
+       struct ofono_watchlist *watchlist;
+
+       watchlist = g_new0(struct ofono_watchlist, 1);
+       watchlist->destroy = destroy;
+
+       return watchlist;
+}
+
+unsigned int __ofono_watchlist_add_item(struct ofono_watchlist *watchlist,
+                                       struct ofono_watchlist_item *item)
+{
+       item->id = ++watchlist->next_id;
+
+       watchlist->items = g_slist_prepend(watchlist->items, item);
+
+       return item->id;
+}
+
+gboolean __ofono_watchlist_remove_item(struct ofono_watchlist *watchlist,
+                                       unsigned int id)
+{
+       struct ofono_watchlist_item *item;
+       GSList *p;
+       GSList *c;
+
+       p = NULL;
+       c = watchlist->items;
+
+       while (c) {
+               item = c->data;
+
+               if (item->id != id) {
+                       p = c;
+                       c = c->next;
+                       continue;
+               }
+
+               if (p)
+                       p->next = c->next;
+               else
+                       watchlist->items = c->next;
+
+               if (item->destroy)
+                       item->destroy(item->notify_data);
+
+               if (watchlist->destroy)
+                       watchlist->destroy(item);
+               g_slist_free_1(c);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+void __ofono_watchlist_free(struct ofono_watchlist *watchlist)
+{
+       struct ofono_watchlist_item *item;
+       GSList *l;
+
+       for (l = watchlist->items; l; l = l->next) {
+               item = l->data;
+
+               if (item->destroy)
+                       item->destroy(item->notify_data);
+
+               if (watchlist->destroy)
+                       watchlist->destroy(item);
+       }
+
+       g_slist_free(watchlist->items);
+       watchlist->items = NULL;
+       g_free(watchlist);
+}
diff --git a/test/activate-context b/test/activate-context
new file mode 100755 (executable)
index 0000000..01565ad
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+
+       if (len(contexts) == 0):
+               print "No context available"
+               sys.exit(1)
+
+       connman.SetProperty("Powered", dbus.Boolean(1))
+
+       if len(sys.argv) > 1:
+               path = contexts[int(sys.argv[1])][0]
+       else:
+               path = contexts[0][0]
+
+       context = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionContext')
+
+       try:
+               context.SetProperty("Active", dbus.Boolean(1), timeout = 100)
+       except dbus.DBusException, e:
+               print "Error activating %s: %s" % (path, str(e))
+               exit(2)
diff --git a/test/answer-calls b/test/answer-calls
new file mode 100755 (executable)
index 0000000..0deb832
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       if "org.ofono.VoiceCallManager" not in properties["Interfaces"]:
+               continue
+
+       mgr = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.VoiceCallManager')
+
+       calls = mgr.GetCalls()
+
+       for path, properties in calls:
+               state = properties["State"]
+               print "[ %s ] %s" % (path, state)
+
+               if state != "incoming":
+                       continue
+
+               call = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.VoiceCall')
+
+               call.Answer()
diff --git a/test/backtrace b/test/backtrace
new file mode 100755 (executable)
index 0000000..c906f36
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+import os
+import re
+import sys
+import subprocess
+
+if (len(sys.argv) < 3):
+       print "Usage: %s [binary] [log]" % (sys.argv[0])
+       sys.exit(1)
+
+binary = sys.argv[1]
+count = 0
+frames = []
+addrs = []
+
+log_file = open(sys.argv[2], 'r')
+
+# Extract addresses
+for line in log_file:
+       matchobj = re.compile(r'\[(0x[0-9a-f]+)\]$').search(line)
+       if matchobj:
+               addrs.append(matchobj.group(1))
+
+log_file.close()
+
+# Feed into addr2line
+command = ['addr2line', '--demangle', '--functions', '--basename',
+                                                       '-e', binary]
+command.extend(addrs)
+
+p = subprocess.Popen(command, shell=False, bufsize=0,
+               stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
+(child_stdin, child_stdout) = (p.stdin, p.stdout)
+
+child_stdin.close()
+
+# Backtrace display
+for line in child_stdout:
+
+       if line.startswith("??"):
+               continue
+
+       line = line.strip()
+
+       frames.append(line)
+
+child_stdout.close()
+
+frame_count = len(frames);
+
+count = 0
+print "-------- backtrace --------"
+while count < frame_count:
+       print "[%d]: %s() [%s]" % (count/2, frames[count], frames[count + 1])
+       count = count + 2
+print "---------------------------"
diff --git a/test/cancel-ussd b/test/cancel-ussd
new file mode 100755 (executable)
index 0000000..5246591
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+if (len(sys.argv) == 2):
+       path = sys.argv[1]
+else:
+       path = modems[0][0]
+
+ussd = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.SupplementaryServices')
+
+properties = ussd.GetProperties()
+state = properties["State"]
+
+print "State: %s" % (state)
+
+if state != "idle":
+       ussd.Cancel()
diff --git a/test/cdma-connman-disable b/test/cdma-connman-disable
new file mode 100755 (executable)
index 0000000..a1c9568
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Disconnecting CDMA Packet Data Service on modem %s..." % path
+cm = dbus.Interface(bus.get_object('org.ofono', path),
+               'org.ofono.cdma.ConnectionManager')
+
+cm.SetProperty("Powered", dbus.Boolean(0))
diff --git a/test/cdma-connman-enable b/test/cdma-connman-enable
new file mode 100755 (executable)
index 0000000..699240d
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Connecting CDMA Packet Data Service on modem %s..." % path
+cm = dbus.Interface(bus.get_object('org.ofono', path),
+               'org.ofono.cdma.ConnectionManager')
+
+cm.SetProperty("Powered", dbus.Boolean(1))
diff --git a/test/cdma-dial-number b/test/cdma-dial-number
new file mode 100755 (executable)
index 0000000..d6dc0c4
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+if len(sys.argv) > 2:
+       path = sys.argv[1]
+       number = sys.argv[2]
+else:
+       modems = manager.GetModems()
+       path, properties = modems[0]
+       number = sys.argv[1]
+
+print "Using modem %s" % path
+
+manager = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.cdma.VoiceCallManager')
+
+manager.Dial(number)
diff --git a/test/cdma-hangup b/test/cdma-hangup
new file mode 100755 (executable)
index 0000000..493ece4
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+if len(sys.argv) > 2:
+       path = sys.argv[1]
+else:
+       modems = manager.GetModems()
+       path, properties = modems[0]
+
+manager = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.cdma.VoiceCallManager')
+
+manager.Hangup()
diff --git a/test/cdma-list-call b/test/cdma-list-call
new file mode 100755 (executable)
index 0000000..c941383
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       if "org.ofono.cdma.VoiceCallManager" not in properties["Interfaces"]:
+               continue
+
+       mgr = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.cdma.VoiceCallManager')
+
+       properties = mgr.GetProperties()
+
+       for key in properties.keys():
+               val = str(properties[key])
+               print "    %s = %s" % (key, val)
diff --git a/test/cdma-set-credentials b/test/cdma-set-credentials
new file mode 100755 (executable)
index 0000000..249ac11
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.cdma.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       cm = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.cdma.ConnectionManager')
+
+       print "Connecting CDMA Packet Data Service on modem %s..." % path
+
+       if len(sys.argv) > 1:
+               cm.SetProperty("Username", (sys.argv[1]))
+               print "Setting Username to %s" % (sys.argv[1])
+
+       if len(sys.argv) > 2:
+               cm.SetProperty("Password", (sys.argv[2]))
+               print "Setting Password to %s" % (sys.argv[2])
diff --git a/test/create-internet-context b/test/create-internet-context
new file mode 100755 (executable)
index 0000000..3d548d0
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+       path = "";
+
+       for i, properties in contexts:
+               if properties["Type"] == "internet":
+                       path = i
+                       break
+
+       if path == "":
+               path = connman.AddContext("internet")
+               print "Created new context %s" % (path)
+       else:
+               print "Found context %s" % (path)
+
+       context = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionContext')
+
+       if len(sys.argv) > 1:
+               context.SetProperty("AccessPointName", sys.argv[1])
+               print "Setting APN to %s" % (sys.argv[1])
+
+       if len(sys.argv) > 2:
+               context.SetProperty("Username", sys.argv[2])
+               print "Setting username to %s" % (sys.argv[2])
+
+       if len(sys.argv) > 3:
+               context.SetProperty("Password", sys.argv[3])
+               print "Setting password to %s" % (sys.argv[3])
diff --git a/test/create-mms-context b/test/create-mms-context
new file mode 100755 (executable)
index 0000000..861ca7c
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+       path = "";
+
+       for i, properties in contexts:
+               if properties["Type"] == "mms":
+                       path = i
+                       break
+
+       if path == "":
+               path = connman.AddContext("mms")
+               print "Created new context %s" % (path)
+       else:
+               print "Found context %s" % (path)
+
+       context = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionContext')
+
+       if len(sys.argv) > 1:
+               context.SetProperty("AccessPointName", sys.argv[1])
+               print "Setting APN to %s" % (sys.argv[1])
+
+       if len(sys.argv) > 2:
+               context.SetProperty("Username", sys.argv[2])
+               print "Setting username to %s" % (sys.argv[2])
+
+       if len(sys.argv) > 3:
+               context.SetProperty("Password", sys.argv[3])
+               print "Setting password to %s" % (sys.argv[3])
diff --git a/test/create-multiparty b/test/create-multiparty
new file mode 100755 (executable)
index 0000000..79e98a6
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+path, properties = modems[0]
+
+manager = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.VoiceCallManager')
+
+mpty = manager.CreateMultiparty()
+
+for path in mpty:
+       print path
diff --git a/test/deactivate-all b/test/deactivate-all
new file mode 100755 (executable)
index 0000000..427009e
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       connman.DeactivateAll()
diff --git a/test/deactivate-context b/test/deactivate-context
new file mode 100755 (executable)
index 0000000..e3754c2
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+
+       if (len(contexts) == 0):
+               print "No context available"
+               sys.exit(1)
+
+       if len(sys.argv) > 1:
+               path = contexts[int(sys.argv[1])][0]
+       else:
+               path = contexts[0][0]
+
+       context = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionContext')
+
+       try:
+               context.SetProperty("Active", dbus.Boolean(0))
+       except dbus.DBusException, e:
+               print "Error activating %s: %s" % (path, str(e))
+               exit(2)
diff --git a/test/dial-number b/test/dial-number
new file mode 100755 (executable)
index 0000000..8e250ea
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s [modem] <number> [hide_callerid]" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+modem = modems[0][0]
+
+hide_callerid = "default"
+
+if (len(sys.argv) == 2):
+       number = sys.argv[1]
+elif (len(sys.argv) == 3):
+       if (sys.argv[2] == "default") or (sys.argv[2] == "enabled") or \
+                       (sys.argv[2] == "disabled"):
+               number = sys.argv[1]
+               hide_callerid = sys.argv[2]
+       else:
+               modem = sys.argv[1]
+               number = sys.argv[2]
+else:
+       modem = sys.argv[1]
+       number = sys.argv[2]
+       hide_callerid = sys.argv[3]
+
+print "Using modem %s" % modem
+
+vcm = dbus.Interface(bus.get_object('org.ofono', modem),
+                                               'org.ofono.VoiceCallManager')
+
+path = vcm.Dial(number, hide_callerid)
+
+print path
diff --git a/test/disable-call-forwarding b/test/disable-call-forwarding
new file mode 100755 (executable)
index 0000000..ca0ba90
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+import sys
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(property, value):
+       if len(value.__str__()) > 0:
+               print "CF property %s changed to %s" % (property, value)
+       else:
+               print "CF property %s changed to disabled" % (property)
+
+       if canexit:
+               mainloop.quit();
+
+if __name__ == "__main__":
+       if len(sys.argv) < 2:
+               print "Usage: %s <type>" % (sys.argv[0])
+               print "Type can be: all, conditional"
+               sys.exit(1)
+
+       canexit = False
+
+       type = sys.argv[1]
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cf = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                               'org.ofono.CallForwarding')
+
+       cf.connect_to_signal("PropertyChanged", property_changed)
+
+       try:
+               cf.DisableAll(type, timeout = 100)
+       except dbus.DBusException, e:
+               print "Unable to DisableAll", e
+               sys.exit(1);
+
+       print "DisableAll successful"
+
+       canexit = True
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/disable-gprs b/test/disable-gprs
new file mode 100755 (executable)
index 0000000..cca2c78
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Disconnecting GPRS on modem %s..." % path
+cm = dbus.Interface(bus.get_object('org.ofono', path),
+               'org.ofono.ConnectionManager')
+
+cm.SetProperty("Powered", dbus.Boolean(0))
diff --git a/test/disable-modem b/test/disable-modem
new file mode 100755 (executable)
index 0000000..945359b
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Disconnecting modem %s..." % path
+modem = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.Modem')
+
+modem.SetProperty("Powered", dbus.Boolean(0), timeout = 120)
diff --git a/test/enable-cbs b/test/enable-cbs
new file mode 100755 (executable)
index 0000000..9b5b57b
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Enabling cell broadcast on modem %s..." % path
+cbs = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.CellBroadcast')
+
+cbs.SetProperty("Powered", dbus.Boolean(1))
diff --git a/test/enable-gprs b/test/enable-gprs
new file mode 100755 (executable)
index 0000000..2b273a7
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Connecting modem %s..." % path
+cm = dbus.Interface(bus.get_object('org.ofono', path),
+               'org.ofono.ConnectionManager')
+
+cm.SetProperty("Powered", dbus.Boolean(1))
diff --git a/test/enable-modem b/test/enable-modem
new file mode 100755 (executable)
index 0000000..665ced2
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Connecting modem %s..." % path
+modem = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.Modem')
+
+modem.SetProperty("Powered", dbus.Boolean(1), timeout = 120)
diff --git a/test/enter-pin b/test/enter-pin
new file mode 100755 (executable)
index 0000000..fea15e0
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 4:
+       path = sys.argv[1]
+       pin_type = sys.argv[2]
+       pin = sys.argv[3]
+elif len(sys.argv) == 3:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       pin_type = sys.argv[1]
+       pin = sys.argv[2]
+else:
+       print "%s [PATH] pin_type pin" % (sys.argv[0])
+       sys.exit(0)
+
+print "Enter Pin for modem %s..." % path
+simmanager = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.SimManager')
+
+simmanager.EnterPin(pin_type, pin)
diff --git a/test/get-icon b/test/get-icon
new file mode 100755 (executable)
index 0000000..326d562
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       id = sys.argv[1]
+else:
+       print "%s <icon id>" % (sys.argv[0])
+       sys.exit(0)
+
+manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+                                                       "org.ofono.Manager")
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.SimManager" not in properties["Interfaces"]:
+               continue
+
+sim = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.SimManager')
+
+icon = sim.GetIcon(dbus.Byte(int(sys.argv[1])))
+
+xpm = ""
+for byte in icon:
+       xpm += str(byte)
+print xpm
diff --git a/test/get-operators b/test/get-operators
new file mode 100755 (executable)
index 0000000..f5bac12
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+netreg = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.NetworkRegistration')
+
+operators = netreg.GetOperators()
+
+for entry in operators:
+       path = entry[0]
+       properties = entry[1]
+
+       print "[ %s ]" % (path)
+
+       for key in properties.keys():
+               if key in ["Technologies"]:
+                       val = ""
+                       for i in properties[key]:
+                               val += i + " "
+               else:
+                       val = str(properties[key])
+               print "    %s = %s" % (key, val)
+
+       print
+
diff --git a/test/get-tech-preference b/test/get-tech-preference
new file mode 100755 (executable)
index 0000000..fc65536
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import dbus, sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+radiosettings = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.RadioSettings')
+
+properties = radiosettings.GetProperties()
+
+print "Technology preference: %s" % (properties["TechnologyPreference"])
diff --git a/test/hangup-active b/test/hangup-active
new file mode 100755 (executable)
index 0000000..6444b23
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+path = modems[0][0]
+
+manager = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.VoiceCallManager')
+
+calls = manager.GetCalls()
+
+for path, properties in calls:
+               state = properties["State"]
+               print "[ %s ] %s" % (path, state)
+
+               if state != "active":
+                       continue
+
+               call = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.VoiceCall')
+
+               call.Hangup()
diff --git a/test/hangup-all b/test/hangup-all
new file mode 100755 (executable)
index 0000000..32933db
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+modem = modems[0][0]
+
+if (len(sys.argv) == 2):
+       modem = sys.argv[1]
+
+manager = dbus.Interface(bus.get_object('org.ofono', modem),
+                                               'org.ofono.VoiceCallManager')
+
+manager.HangupAll()
diff --git a/test/initiate-ussd b/test/initiate-ussd
new file mode 100755 (executable)
index 0000000..098ec87
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s [modem] <ussd-string>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+if (len(sys.argv) == 2):
+       path = modems[0][0]
+       ussdstring = sys.argv[1]
+else:
+       path = sys.argv[1]
+       ussdstring = sys.argv[2]
+
+ussd = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.SupplementaryServices')
+
+properties = ussd.GetProperties()
+state = properties["State"]
+
+print "State: %s" % (state)
+
+if state != "idle":
+       sys.exit(1);
+
+result = ussd.Initiate(ussdstring, timeout=100)
+
+properties = ussd.GetProperties()
+state = properties["State"]
+
+print result[0] + ": " + result[1]
+
+if state == "idle":
+       sys.exit(0)
+
+print "State: %s" % (state)
+
+while state == "user-response":
+       response = raw_input("Enter response: ")
+
+       result = ussd.Respond(response, timeout=100)
+
+       properties = ussd.GetProperties()
+       state = properties["State"]
+
+       print result
+
+       if state != "idle":
+               print "State: %s" % (state)
diff --git a/test/list-calls b/test/list-calls
new file mode 100755 (executable)
index 0000000..53124dc
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       if "org.ofono.VoiceCallManager" not in properties["Interfaces"]:
+               continue
+
+       mgr = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.VoiceCallManager')
+
+       calls = mgr.GetCalls()
+
+       for path, properties in calls:
+               print "    [ %s ]" % (path)
+
+               for key in properties.keys():
+                       val = str(properties[key])
+                       print "        %s = %s" % (key, val)
+
+               print
diff --git a/test/list-contexts b/test/list-contexts
new file mode 100755 (executable)
index 0000000..ed4af88
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+
+       for path, properties in contexts:
+               print "    [ %s ]" % (path)
+
+               for key in properties.keys():
+                       if key in ["Settings"] or key in ["IPv6.Settings"]:
+                               val = "{"
+                               for i in properties[key].keys():
+                                       val += " " + i + "="
+                                       if i in ["DomainNameServers"]:
+                                               for n in properties[key][i]:
+                                                       val += n + ","
+                                       elif i in ["PrefixLength"]:
+                                               p = int(properties[key][i])
+                                               val += str(p)
+                                       else:
+                                               val += properties[key][i]
+                               val += " }"
+                       else:
+                               val = str(properties[key])
+                       print "        %s = %s" % (key, val)
+
+               print
diff --git a/test/list-messages b/test/list-messages
new file mode 100755 (executable)
index 0000000..d3e95e6
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       if "org.ofono.MessageManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.MessageManager')
+
+       contexts = connman.GetMessages()
+
+       for path, properties in contexts:
+               print "    [ %s ]" % (path)
+
+               for key in properties.keys():
+                       val = str(properties[key])
+                       print "        %s = %s" % (key, val)
+
+               print
diff --git a/test/list-modems b/test/list-modems
new file mode 100755 (executable)
index 0000000..7e92474
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       for key in properties.keys():
+               if key in ["Interfaces", "Features"]:
+                       val = ""
+                       for i in properties[key]:
+                               val += i + " "
+               else:
+                       val = str(properties[key])
+               print "    %s = %s" % (key, val)
+
+       for interface in properties["Interfaces"]:
+               object = dbus.Interface(bus.get_object('org.ofono', path),
+                                                               interface)
+
+               print "    [ %s ]" % (interface)
+
+               try:
+                       properties = object.GetProperties()
+               except:
+                       continue
+
+               for key in properties.keys():
+                       if key in ["Calls",
+                                       "MultipartyCalls",
+                                       "EmergencyNumbers",
+                                       "SubscriberNumbers",
+                                       "PreferredLanguages",
+                                       "PrimaryContexts",
+                                       "LockedPins",
+                                       "Features"]:
+                               val = ""
+                               for i in properties[key]:
+                                       val += i + " "
+                       elif key in ["ServiceNumbers"]:
+                               val = ""
+                               for i in properties[key]:
+                                       val += "[" + i + "] = '"
+                                       val += properties[key][i] + "' "
+                       elif key in ["MobileNetworkCodeLength",
+                                               "VoicemailMessageCount",
+                                               "MicrophoneVolume",
+                                               "SpeakerVolume",
+                                               "Strength",
+                                               "DataStrength"]:
+                               val = int(properties[key])
+                       elif key in ["MainMenu"]:
+                               val = ", ".join([ text + " (" + str(int(icon)) +
+                                       ")" for text, icon in properties[key] ])
+                       elif key in ["Retries"]:
+                               val = ""
+                               for i in properties[key]:
+                                       val +=  "[" + i + " = "
+                                       val += str(int(properties[key][i])) + "] "
+                       elif key in ["Settings"]:
+                               val = "{"
+                               for i in properties[key].keys():
+                                       val += " " + i + "="
+                                       if i in ["DomainNameServers"]:
+                                               for n in properties[key][i]:
+                                                       val += n + ","
+                                       else:
+                                               val += properties[key][i]
+                               val += " }"
+                       else:
+                               val = str(properties[key])
+                       print "        %s = %s" % (key, val)
+
+       print
diff --git a/test/list-operators b/test/list-operators
new file mode 100755 (executable)
index 0000000..be00c5b
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       if "org.ofono.NetworkRegistration" not in properties["Interfaces"]:
+               continue
+
+       netreg = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.NetworkRegistration')
+
+       if len(sys.argv) == 2 and sys.argv[1] == 'scan':
+               operators = netreg.Scan()
+       else:
+               operators = netreg.GetOperators()
+
+       for path, properties in operators:
+               print "    [ %s ]" % (path)
+
+               for key in properties.keys():
+                       if key in ["Technologies"]:
+                               val = ""
+                               for i in properties[key]:
+                                       val += i + " "
+                       else:
+                               val = str(properties[key])
+                       print "        %s = %s" % (key, val)
+
+               print
diff --git a/test/lock-pin b/test/lock-pin
new file mode 100755 (executable)
index 0000000..60c3afb
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 4:
+       path = sys.argv[1]
+       pin_type = sys.argv[2]
+       pin = sys.argv[3]
+elif len(sys.argv) == 3:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       pin_type = sys.argv[1]
+       pin = sys.argv[2]
+else:
+       print "%s [PATH] pin_type pin" % (sys.argv[0])
+       sys.exit(0)
+
+print "Lock %s %s for modem %s..." % (pin_type, pin, path)
+
+simmanager = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.SimManager')
+simmanager.LockPin(pin_type, pin)
diff --git a/test/lockdown-modem b/test/lockdown-modem
new file mode 100755 (executable)
index 0000000..5d98154
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Locking and disconnecting modem %s..." % path
+modem = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.Modem')
+
+modem.SetProperty("Lockdown", dbus.Boolean(1))
+
+print "press ENTER to unlock the modem %s" % path
+sys.stdin.readline()
+
+modem.SetProperty("Lockdown", dbus.Boolean(0))
diff --git a/test/monitor-ofono b/test/monitor-ofono
new file mode 100755 (executable)
index 0000000..8570c34
--- /dev/null
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+_dbus2py = {
+       dbus.String : unicode,
+       dbus.UInt32 : int,
+       dbus.Int32 : int,
+       dbus.Int16 : int,
+       dbus.UInt16 : int,
+       dbus.UInt64 : int,
+       dbus.Int64 : int,
+       dbus.Byte : int,
+       dbus.Boolean : bool,
+       dbus.ByteArray : str,
+       dbus.ObjectPath : str
+    }
+
+def dbus2py(d):
+       t = type(d)
+       if t in _dbus2py:
+               return _dbus2py[t](d)
+       if t is dbus.Dictionary:
+               return dict([(dbus2py(k), dbus2py(v)) for k, v in d.items()])
+       if t is dbus.Array and d.signature == "y":
+               return "".join([chr(b) for b in d])
+       if t is dbus.Array or t is list:
+               return [dbus2py(v) for v in d]
+       if t is dbus.Struct or t is tuple:
+               return tuple([dbus2py(v) for v in d])
+       return d
+
+def pretty(d):
+       d = dbus2py(d)
+       t = type(d)
+
+       if t in (dict, tuple, list) and len(d) > 0:
+               if t is dict:
+                       d = ", ".join(["%s = %s" % (k, pretty(v))
+                                       for k, v in d.items()])
+                       return "{ %s }" % d
+
+               d = " ".join([pretty(e) for e in d])
+
+               if t is tuple:
+                       return "( %s )" % d
+
+       return str(d)
+
+def property_changed(name, value, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       print "{%s} [%s] %s = %s" % (iface, path, name, pretty(value))
+
+def added(name, value, member, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       print "{%s} [%s] %s %s" % (iface, member, name, pretty(value))
+
+def removed(name, member, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       print "{%s} [%s] %s" % (iface, name, member)
+
+def event(member, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       print "{%s} [%s] %s" % (iface, path, member)
+
+def message(msg, args, member, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       print "{%s} [%s] %s %s (%s)" % (iface, path, member,
+                                       str(msg), pretty(args))
+
+def ussd(msg, member, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       print "{%s} [%s] %s %s" % (iface, path, member, str(msg))
+
+def value(value, member, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       print "{%s} [%s] %s %s" % (iface, path, member, str(value))
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(property_changed,
+                                       bus_name="org.ofono",
+                                       signal_name = "PropertyChanged",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       for member in ["IncomingBarringInEffect",
+                       "OutgoingBarringInEffect",
+                       "NearMaximumWarning"]:
+               bus.add_signal_receiver(event,
+                                       bus_name="org.ofono",
+                                       signal_name = member,
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(added,
+                                       bus_name="org.ofono",
+                                       signal_name = "ModemAdded",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(removed,
+                                       bus_name="org.ofono",
+                                       signal_name = "ModemRemoved",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(added,
+                                       bus_name="org.ofono",
+                                       signal_name = "ContextAdded",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(removed,
+                                       bus_name="org.ofono",
+                                       signal_name = "ContextRemoved",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(added,
+                               bus_name="org.ofono",
+                                       signal_name = "CallAdded",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+       bus.add_signal_receiver(removed,
+                               bus_name="org.ofono",
+                                       signal_name = "CallRemoved",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(added,
+                               bus_name="org.ofono",
+                                       signal_name = "MessageAdded",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+       bus.add_signal_receiver(removed,
+                               bus_name="org.ofono",
+                                       signal_name = "MessageRemoved",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(value,
+                               bus_name="org.ofono",
+                                       signal_name = "DisconnectReason",
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       for member in ["IncomingBroadcast", "EmergencyBroadcast",
+                       "IncomingMessage", "ImmediateMessage"]:
+               bus.add_signal_receiver(message,
+                                       bus_name="org.ofono",
+                                       signal_name = member,
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       for member in ["NotificationReceived", "RequestReceived"]:
+               bus.add_signal_receiver(ussd,
+                                       bus_name="org.ofono",
+                                       signal_name = member,
+                                               member_keyword="member",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/offline-modem b/test/offline-modem
new file mode 100755 (executable)
index 0000000..aa9c692
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+
+import dbus, sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Setting modem %s offline..." % path
+modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem')
+modem.SetProperty("Online", dbus.Boolean(0), timeout = 120)
diff --git a/test/online-modem b/test/online-modem
new file mode 100755 (executable)
index 0000000..813b176
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+
+import dbus, sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Setting modem %s online..." % path
+modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem')
+modem.SetProperty("Online", dbus.Boolean(1), timeout = 120)
diff --git a/test/private-chat b/test/private-chat
new file mode 100755 (executable)
index 0000000..4938a25
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+path = modems[0][0]
+
+manager = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.VoiceCallManager')
+
+mpty = manager.PrivateChat(sys.argv[1])
+
+for path in mpty:
+       print path
diff --git a/test/process-context-settings b/test/process-context-settings
new file mode 100755 (executable)
index 0000000..09635b3
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+import os
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+
+       for path, properties in contexts:
+               if properties["Active"] == dbus.Boolean(0):
+                       continue
+
+               print "Configuring %s" % (path)
+
+               settings = properties["Settings"]
+
+               interface = settings["Interface"]
+               address = settings["Address"]
+               try:
+                       gateway = settings["Gateway"]
+               except:
+                       gateway = "0.0.0.0";
+
+               if settings["Method"] == "dhcp":
+                       print "    Run DHCP on interface %s" % (interface)
+               else:
+                       print "    Interface is %s" % (interface)
+                       print "    IP address is %s" % (address)
+                       print "    Gateway is %s" % (gateway)
+
+                       cmd = "ifconfig " + interface + " " + address
+                       cmd += " netmask 255.255.255.255"
+                       os.system(cmd);
+
+                       for i in settings["DomainNameServers"]:
+                               print "    Nameserver is %s" % (i)
+
+                               cmd = "route add -host " + i
+                               cmd +=" dev " + interface
+                               os.system(cmd);
+               print
diff --git a/test/receive-sms b/test/receive-sms
new file mode 100755 (executable)
index 0000000..a658c58
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def incoming_message(message, details, path, interface):
+       print "%s" % (message)
+
+       for key in details:
+               val = details[key]
+               print "    %s = %s" % (key, val)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(incoming_message,
+                                       bus_name="org.ofono",
+                                       signal_name = "ImmediateMessage",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(incoming_message,
+                                       bus_name="org.ofono",
+                                       signal_name = "IncomingMessage",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/reject-calls b/test/reject-calls
new file mode 100755 (executable)
index 0000000..405eb44
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       print "[ %s ]" % (path)
+
+       if "org.ofono.VoiceCallManager" not in properties["Interfaces"]:
+               continue
+
+       mgr = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.VoiceCallManager')
+
+       calls = mgr.GetCalls()
+
+       for path, properties in calls:
+               state = properties["State"]
+               print "[ %s ] %s" % (path, state)
+
+               if state != "incoming":
+                       continue
+
+               call = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.VoiceCall')
+
+               call.Hangup()
diff --git a/test/remove-contexts b/test/remove-contexts
new file mode 100755 (executable)
index 0000000..a600d0b
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+
+       for path, properties in contexts:
+               connman.RemoveContext(path)
+               print"Removed: [ %s ]" % (path)
diff --git a/test/reset-pin b/test/reset-pin
new file mode 100755 (executable)
index 0000000..d0145fe
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 5:
+       path, puk_type, puk, pin = sys.argv[1:]
+elif len(sys.argv) == 4:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       puk_type, puk, pin = sys.argv[1:]
+else:
+       print "%s [PATH] puk_type puk pin" % (sys.argv[0])
+
+print "Reset pin for modem %s..." % path
+simmanager = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.SimManager')
+
+simmanager.ResetPin(puk_type, puk, pin)
diff --git a/test/scan-for-operators b/test/scan-for-operators
new file mode 100755 (executable)
index 0000000..82fe3f4
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Scanning operators on modem %s..." % path
+netreg = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.NetworkRegistration')
+
+operators = netreg.Scan(timeout=100);
+
+for entry in operators:
+       path = entry[0]
+       properties = entry[1]
+
+       print "[ %s ]" % (path)
+
+       for key in properties.keys():
+               if key in ["Technologies"]:
+                       val = ""
+                       for i in properties[key]:
+                               val += i + " "
+               else:
+                       val = str(properties[key])
+               print "    %s = %s" % (key, val)
+
+       print
+
diff --git a/test/send-sms b/test/send-sms
new file mode 100755 (executable)
index 0000000..968824b
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 4:
+       print "Usage: %s [modem] <to> <message> <delivery report>" %\
+                                       (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 5:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Send message using modem %s ..." % path
+
+
+mm = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.MessageManager')
+
+if len(sys.argv) == 5:
+       mm.SetProperty("UseDeliveryReports",
+                                       dbus.Boolean(int(sys.argv[4])))
+       path = mm.SendMessage(sys.argv[2], sys.argv[3])
+else:
+       path = mm.SendMessage(sys.argv[1], sys.argv[2])
+
+print path
diff --git a/test/send-vcal b/test/send-vcal
new file mode 100755 (executable)
index 0000000..7ccd2f6
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 3:
+       print "Usage: %s [modem] <to> <vcal file>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 4:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Send vcal using modem %s ..." % path
+
+sm = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.SmartMessaging')
+
+if len(sys.argv) == 4:
+       vcal = file(sys.argv[3]).read()
+       path = sm.SendAppointment(sys.argv[2], vcal)
+else:
+       vcal = file(sys.argv[2]).read()
+       path = sm.SendAppointment(sys.argv[1], vcal)
+
+print path
diff --git a/test/send-vcard b/test/send-vcard
new file mode 100755 (executable)
index 0000000..3955990
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 3:
+       print "Usage: %s [modem] <to> <vcard file>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 4:
+       path = sys.argv[1]
+else:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+print "Send vcard using modem %s ..." % path
+
+sm = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.SmartMessaging')
+
+if len(sys.argv) == 4:
+       vcard = file(sys.argv[3]).read()
+       path = sm.SendBusinessCard(sys.argv[2], vcard)
+else:
+       vcard = file(sys.argv[2]).read()
+       path = sm.SendBusinessCard(sys.argv[1], vcard)
+
+print path
diff --git a/test/set-call-forwarding b/test/set-call-forwarding
new file mode 100755 (executable)
index 0000000..1c06b68
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+import sys
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(property, value):
+       if len(value.__str__()) > 0:
+               print "CF property %s changed to %s" % (property, value)
+       else:
+               print "CF property %s changed to disabled" % (property)
+
+       if canexit:
+               mainloop.quit();
+
+if __name__ == "__main__":
+       if len(sys.argv) < 3:
+               print "Usage: %s <property> <value>" % (sys.argv[0])
+               print "Properties can be: VoiceUnconditional, VoiceBusy,"
+               print " VoiceNoReply, VoiceNoReplyTimeout, VoiceNotReachable"
+               print "Value: number to or the timeout"
+               sys.exit(1)
+
+       property = sys.argv[1]
+       value = sys.argv[2]
+
+       canexit = False
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cf = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                               'org.ofono.CallForwarding')
+
+       cf.connect_to_signal("PropertyChanged", property_changed)
+
+       if (property == "VoiceNoReplyTimeout"):
+               try:
+                       cf.SetProperty(property, dbus.UInt16(value),
+                                                       timeout = 100)
+               except dbus.DBusException, e:
+                       print "Unable SetProperty", e
+                       sys.exit(1);
+       else:
+               try:
+                       cf.SetProperty(property, value, timeout = 100)
+               except dbus.DBusException, e:
+                       print "Unable SetProperty", e
+                       sys.exit(1);
+
+       print "Set Property successful"
+
+       canexit = True
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/set-cbs-topics b/test/set-cbs-topics
new file mode 100755 (executable)
index 0000000..fbf7aa4
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+       path = sys.argv[1]
+       topics = sys.argv[2]
+elif len(sys.argv) == 2:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       topics = sys.argv[1]
+else:
+       print "%s [PATH] topics" % (sys.argv[0])
+
+print "Setting cell broadcast topics for modem %s..." % path
+cbs = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.CellBroadcast')
+
+cbs.SetProperty("Topics", topics);
diff --git a/test/set-context-property b/test/set-context-property
new file mode 100755 (executable)
index 0000000..cfd6c68
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if len(sys.argv) < 4:
+       print "Usage: set-context-property <context> <name> <value>"
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+
+       if (len(contexts) == 0):
+               print "No context available"
+               sys.exit(1)
+
+       path = contexts[int(sys.argv[1])][0]
+       context = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionContext')
+
+       try:
+               context.SetProperty(sys.argv[2], sys.argv[3])
+       except dbus.DBusException, e:
+               print "Error setting context %s property %s: %s" %\
+                               (path, sys.argv[2], str(e))
+               exit(2)
diff --git a/test/set-fast-dormancy b/test/set-fast-dormancy
new file mode 100755 (executable)
index 0000000..6951813
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+       path = sys.argv[1]
+       enable = int(sys.argv[2])
+elif len(sys.argv) == 2:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       enable = int(sys.argv[1])
+else:
+       print "%s [PATH] {0|1}" % (sys.argv[0])
+       exit(1)
+
+print "Setting fast dormancy for modem %s..." % path
+radiosettings = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.RadioSettings')
+
+radiosettings.SetProperty("FastDormancy", dbus.Boolean(enable));
diff --git a/test/set-gsm-band b/test/set-gsm-band
new file mode 100755 (executable)
index 0000000..9586d81
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+       path = sys.argv[1]
+       band = sys.argv[2]
+elif len(sys.argv) == 2:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       band = sys.argv[1]
+else:
+       print "%s [PATH] band" % (sys.argv[0])
+       exit(1)
+
+print "Setting gsm band for modem %s..." % path
+radiosettings = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.RadioSettings')
+
+radiosettings.SetProperty("GsmBand", band);
diff --git a/test/set-mic-volume b/test/set-mic-volume
new file mode 100755 (executable)
index 0000000..e0bff49
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+path = modems[0][0]
+
+cv = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.CallVolume')
+
+cv.SetProperty("MicrophoneVolume", dbus.Byte(int(sys.argv[1])))
diff --git a/test/set-mms-details b/test/set-mms-details
new file mode 100755 (executable)
index 0000000..7540948
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       contexts = connman.GetContexts()
+       path = "";
+
+       for i, properties in contexts:
+               if properties["Type"] == "mms":
+                       path = i
+                       break
+
+       if path == "":
+               print "No MMS context"
+               exit(1)
+
+       context = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionContext')
+
+       if len(sys.argv) < 3:
+               print "Usage: %s <proxy> <center>" % (sys.argv[0])
+               exit(1)
+
+       context.SetProperty("MessageProxy", sys.argv[1])
+       print "Setting MMS Proxy to %s" % (sys.argv[1])
+
+       context.SetProperty("MessageCenter", sys.argv[2])
+       print "Setting MMSC to %s" % (sys.argv[2])
diff --git a/test/set-roaming-allowed b/test/set-roaming-allowed
new file mode 100755 (executable)
index 0000000..f7f9c9d
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+       if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+               continue
+
+       connman = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.ConnectionManager')
+
+       if len(sys.argv) > 1:
+               allowed = dbus.Boolean(int(sys.argv[1]))
+       else:
+               allowed = dbus.Boolean(1)
+
+       connman.SetProperty("RoamingAllowed", allowed)
+
+       print "Setting %s to RoamingAllowed=%d" % (path, allowed)
diff --git a/test/set-speaker-volume b/test/set-speaker-volume
new file mode 100755 (executable)
index 0000000..7962f39
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+path = modems[0][0]
+
+cv = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.CallVolume')
+
+cv.SetProperty("SpeakerVolume", dbus.Byte(int(sys.argv[1])))
diff --git a/test/set-tech-preference b/test/set-tech-preference
new file mode 100755 (executable)
index 0000000..cc735ce
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+       path = sys.argv[1]
+       tech = sys.argv[2]
+elif len(sys.argv) == 2:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       tech = sys.argv[1]
+else:
+       print "%s [PATH] technology" % (sys.argv[0])
+
+print "Setting technology preference for modem %s..." % path
+radiosettings = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.RadioSettings')
+
+radiosettings.SetProperty("TechnologyPreference", tech);
diff --git a/test/set-tty b/test/set-tty
new file mode 100755 (executable)
index 0000000..67876af
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+       path = sys.argv[1]
+       enable = int(sys.argv[2])
+elif len(sys.argv) == 2:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       enable = int(sys.argv[1])
+else:
+       print "%s [PATH] {0|1}" % (sys.argv[0])
+       exit(1)
+
+print "Setting TTY for modem %s..." % path
+texttelephony = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.TextTelephony')
+
+texttelephony.SetProperty("Enabled", dbus.Boolean(enable));
diff --git a/test/set-umts-band b/test/set-umts-band
new file mode 100755 (executable)
index 0000000..510a40b
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+       path = sys.argv[1]
+       band = sys.argv[2]
+elif len(sys.argv) == 2:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       band = sys.argv[1]
+else:
+       print "%s [PATH] band" % (sys.argv[0])
+       exit(1)
+
+print "Setting umts band for modem %s..." % path
+radiosettings = dbus.Interface(bus.get_object('org.ofono', path),
+                                               'org.ofono.RadioSettings')
+
+radiosettings.SetProperty("UmtsBand", band);
diff --git a/test/set-use-sms-reports b/test/set-use-sms-reports
new file mode 100755 (executable)
index 0000000..41ad59e
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 3:
+       path = sys.argv[1]
+       enabled = sys.argv[2]
+       if sys.argv[2] == "off":
+               enabled = 0
+elif len(sys.argv) == 2:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       enabled = sys.argv[1]
+       if sys.argv[1] == "off":
+               enabled = 0
+else:
+       print "%s [PATH] on/off" % (sys.argv[0])
+       sys.exit(1)
+
+print "Setting delivery report use for modem %s..." % path
+sms = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.MessageManager')
+
+sms.SetProperty("UseDeliveryReports", dbus.Boolean(enabled));
diff --git a/test/swap-calls b/test/swap-calls
new file mode 100755 (executable)
index 0000000..4996e0c
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+modems = manager.GetModems()
+modem = modems[0][0]
+
+if (len(sys.argv) == 2):
+       modem = sys.argv[1]
+
+manager = dbus.Interface(bus.get_object('org.ofono', modem),
+                                               'org.ofono.VoiceCallManager')
+
+manager.SwapCalls()
diff --git a/test/test-advice-of-charge b/test/test-advice-of-charge
new file mode 100755 (executable)
index 0000000..9f3f655
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+import gobject
+import sys
+import dbus
+import dbus.mainloop.glib
+
+
+def cm_property_changed(name, value):
+       print "CallMeter property: '%s' changed to '%s'" % (name, str(value))
+       if canexit:
+               mainloop.quit()
+
+def cm_maximum_reached():
+       print "Only 30 seconds call time remains, recharge."
+
+def print_useage(s):
+       print "Usage: %s <property> <newvalue> <password>" % (s)
+       print "Usage: %s reset <password>" % (s)
+       sys.exit(1);
+
+if __name__ == "__main__":
+       if len(sys.argv) != 3 and len(sys.argv) != 4:
+               print_useage(sys.argv[0])
+
+       if (sys.argv[1] == 'reset'):
+               pin = sys.argv[2]
+       else:
+               if (len(sys.argv) != 4):
+                       print_useage(sys.argv[0])
+               property = sys.argv[1]
+               newvalue = sys.argv[2]
+               pin = sys.argv[3]
+
+       canexit = False
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cm = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                       'org.ofono.CallMeter')
+
+       cm.connect_to_signal("PropertyChanged", cm_property_changed)
+       cm.connect_to_signal("NearMaximumWarning", cm_maximum_reached)
+
+       properties = cm.GetProperties()
+
+       print "Currency: %s" % (properties['Currency'])
+       print "PricePerUnit %s" % (properties['PricePerUnit'])
+       print "Call meter for the current call: %s" % (properties['CallMeter'])
+       print "Call meter for current and previous calls: %s" %\
+               properties['AccumulatedCallMeter']
+       print "Call meter maximum, once reached calls are not possible: %s" %\
+               properties['AccumulatedCallMeterMaximum']
+
+       total = properties['PricePerUnit'] * properties['AccumulatedCallMeter']
+       print "Accumulated Meter in Currency: %s %s" %\
+               (total, properties['Currency'])
+
+       if (sys.argv[1] == 'reset'):
+               print "Resetting Accumulated Call Meter"
+               try:
+                       cm.Reset(pin)
+               except dbus.DBusException, e:
+                       print "Unable to reset ACM: ", e
+                       sys.exit(1)
+       else:
+               try:
+                       if property == 'AccumulatedCallMeterMaximum':
+                               newvalue = dbus.UInt32(newvalue)
+                       elif property == 'PricePerUnit':
+                               newvalue = float(newvalue)
+                       cm.SetProperty(property, newvalue, pin)
+               except dbus.DBusException, e:
+                       print "Unable to set property: ", e
+                       sys.exit(1)
+
+       canexit = True
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-call-barring b/test/test-call-barring
new file mode 100755 (executable)
index 0000000..5dd566e
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+import gobject
+import sys
+import dbus
+import dbus.mainloop.glib
+
+
+def property_changed(name, value):
+       print "CallBarring property: '%s' changed to '%s'" % (name, str(value))
+       if canexit:
+               mainloop.quit()
+
+def print_useage(s):
+       print "Usage: %s <property> <newvalue> <password>" % (s)
+       print "Usage: %s disableall <password>" % (s)
+       print "Usage: %s passwd <old_password> <new_password>" % (s)
+       sys.exit(1);
+
+if __name__ == "__main__":
+       if len(sys.argv) != 3 and len(sys.argv) != 4:
+               print_useage(sys.argv[0])
+
+       if (sys.argv[1] == 'disableall'):
+               pin = sys.argv[2]
+       elif (sys.argv[1] == 'passwd'):
+               old_password = sys.argv[2]
+               new_password = sys.argv[3]
+       else:
+               if (len(sys.argv) != 4):
+                       print_useage(sys.argv[0])
+               property = sys.argv[1]
+               newvalue = sys.argv[2]
+               pin = sys.argv[3]
+
+       canexit = False
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cb = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                       'org.ofono.CallBarring')
+
+       cb.connect_to_signal("PropertyChanged", property_changed)
+
+       properties = cb.GetProperties()
+
+       print "Barring settings for Incoming Voice calls: %s" %\
+               (properties['VoiceIncoming'])
+       print "Barring settings for Outgoing Calls: %s" %\
+               (properties['VoiceOutgoing'])
+
+       if (sys.argv[1] == 'disableall'):
+               print "Disabling all barrings"
+               try:
+                       cb.DisableAll(pin)
+               except dbus.DBusException, e:
+                       print "Unable to Disable All barrings: ", e
+                       sys.exit(1)
+       elif (sys.argv[1] == 'passwd'):
+               try:
+                       cb.ChangePassword(old_password, new_password)
+               except dbus.DBusException, e:
+                       print "Unable to change password: ", e
+                       sys.exit(1)
+               print "Password changed"
+               sys.exit(0)
+       else:
+               try:
+                       cb.SetProperty(property, newvalue, pin)
+               except dbus.DBusException, e:
+                       print "Unable to set property: ", e
+                       sys.exit(1)
+
+       canexit = True
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-call-forwarding b/test/test-call-forwarding
new file mode 100755 (executable)
index 0000000..85aca23
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(property, value):
+       print "CallForwarding property %s changed to %s" % (property, value)
+
+def print_properties(cf):
+       properties = cf.GetProperties()
+
+       for p in properties:
+               if len(properties[p].__str__()) > 0:
+                       print "%s call forwarding rule is: %s" % (p, properties[p])
+               else:
+                       print "%s call forwarding rule disabled" % (p)
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cf = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                               'org.ofono.CallForwarding')
+
+       cf.connect_to_signal("PropertyChanged", property_changed)
+
+       print_properties(cf)
+
+       try:
+               cf.SetProperty("FoobarNoReplyTimeout", dbus.UInt16(19))
+       except dbus.DBusException, e:
+               print "Unable to set timeout - Good"
+
+       try:
+               cf.SetProperty("VoiceNotReachableTimeout", dbus.UInt16(19))
+       except dbus.DBusException, e:
+               print "Unable to set timeout - Good"
+
+       try:
+               cf.SetProperty("VoiceNoReplyTimeout", dbus.UInt16(19))
+       except dbus.DBusException, e:
+               print "Unable to set timeout - Good"
+
+       try:
+               cf.SetProperty("DataNoReplyTimeout", dbus.UInt16(19))
+       except dbus.DBusException, e:
+               print "Unable to set timeout - Good"
+
+       try:
+               cf.SetProperty("FaxNoReplyTimeout", dbus.UInt16(19))
+       except dbus.DBusException, e:
+               print "Unable to set timeout - Good"
+
+       try:
+               cf.SetProperty("SmsNoReplyTimeout", dbus.UInt16(19))
+       except dbus.DBusException, e:
+               print "Unable to set timeout - Good"
+
+       try:
+               cf.SetProperty("VoiceNoReply", "")
+       except dbus.DBusException, e:
+               print "Unable to erase voice no reply rule - Bad"
+
+       try:
+               cf.SetProperty("VoiceNoReply", "+134444")
+       except dbus.DBusException, e:
+               print "Unable to register voice no reply rule - Bad"
+
+       try:
+               cf.SetProperty("VoiceNoReplyTimeout", dbus.UInt16(30))
+       except dbus.DBusException, e:
+               print "Unable to set voice no reply timeout - Bad"
+
+       properties = cf.GetProperties()
+
+       print properties["VoiceNoReply"]
+       print properties["VoiceNoReplyTimeout"]
+
+       try:
+               cf.SetProperty("VoiceUnconditional", "+155555")
+       except dbus.DBusException, e:
+               print "Unable to set Voice Unconditional - Bad"
+
+       properties = cf.GetProperties()
+
+       print properties["VoiceUnconditional"]
+
+       try:
+               cf.DisableAll("foobar")
+       except dbus.DBusException, e:
+               print "Unable to delete invalids - Good"
+
+       try:
+               cf.DisableAll("conditional")
+       except dbus.DBusException, e:
+               print "Unable to delete all conditional - Bad"
+
+       properties = cf.GetProperties()
+
+       print properties["VoiceNoReply"]
+       print properties["VoiceNoReplyTimeout"]
+
+       try:
+               cf.DisableAll("all")
+       except dbus.DBusException, e:
+               print "Unable to delete all conditional - Bad"
+
+       print properties["VoiceUnconditional"]
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-call-settings b/test/test-call-settings
new file mode 100755 (executable)
index 0000000..9008614
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+import sys
+
+def property_changed(name, value):
+       print "CallSettings property: '%s' changed to '%s'" % (name, value)
+
+       if canexit:
+               mainloop.quit();
+
+if __name__ == "__main__":
+       if len(sys.argv) < 3:
+               print "Usage: %s [modem] <property> <newvalue>" % (sys.argv[0])
+               print "Properties can be: VoiceCallWaiting,"
+               print " ConnectedLineRestriction, CallingLineRestriction,"
+               print " CallingLinePresentation, CalledLinePresentation,"
+               print " ConnectedLinePresentation, HideCallerId"
+               sys.exit(1)
+
+       canexit = False
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+       modem = modems[0][0]
+
+       if (len(sys.argv) == 4):
+               modem = sys.argv[1]
+               property = sys.argv[2]
+               newvalue = sys.argv[3]
+       else:
+               property = sys.argv[1]
+               newvalue = sys.argv[2]
+
+       print "Using modem %s" % modem
+
+       cs = dbus.Interface(bus.get_object('org.ofono', modem),
+                                               'org.ofono.CallSettings')
+
+       cs.connect_to_signal("PropertyChanged", property_changed)
+
+       properties = cs.GetProperties()
+
+       print "Current Property values:"
+       print "Network Status of Call Waiting - Voice: %s" %\
+               (properties['VoiceCallWaiting'])
+       print "Network Status of Connected Line Restriction: %s" %\
+               (properties['ConnectedLineRestriction'])
+       print "Network Status of Calling Line Restriction: %s" %\
+               (properties['CallingLineRestriction'])
+       print "Network Status of Calling Line Presentation: %s" %\
+               (properties['CallingLinePresentation'])
+       print "Network Status of Called Line Presentation: %s" %\
+               (properties['CalledLinePresentation'])
+       print "Network Status of Connected Line Presentation: %s" %\
+               (properties['ConnectedLinePresentation'])
+       print "Hide my Caller Id: %s" % (properties['HideCallerId'])
+
+       try:
+               cs.SetProperty(property, newvalue)
+       except dbus.DBusException, e:
+               print "Unable to set property: ", e
+               sys.exit(1);
+
+       print "Setting successful"
+
+       if (properties[property] == newvalue):
+               print "Setting was already set to this value"
+               sys.exit(1);
+
+       canexit = True
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-cbs b/test/test-cbs
new file mode 100755 (executable)
index 0000000..af25b89
--- /dev/null
@@ -0,0 +1,188 @@
+#!/usr/bin/python
+
+import dbus
+import dbus.mainloop.glib
+import sys
+import gobject
+import os
+
+def print_menu():
+       print "Select test case"
+       print "----------------------------------------------------------------"
+       print "[0] Activate cbs"
+       print "[1] Deactivate cbs"
+       print "[2] Get cbs properties"
+       print "[3] Set/Register topics"
+       print "        If several - give topics separated with comma. \
+               \n        E.g. 20,50-51,60"
+       print "[4] Clear/Unregister topics"
+       print "[5] NetReg Base Station - Get current serving cell"
+       print "[x] Exit"
+       print "----------------------------------------------------------------"
+
+def property_changed(property, value):
+       if value == "" and property == "Topics":
+               print "User selected Topics have been cleared. \
+                       \nRegistered for emergency topics only."
+       else:
+               print "Cell Broadcast property %s is changed to %s" % (property, value)
+       print "\nPress ENTER to continue"
+
+def incoming_broadcast(text, topic):
+       print "Broadcast msg: %s \n Topic channel: %s" % (text, topic)
+       print "\nPress ENTER to continue"
+
+def emergency_broadcast(text, properties):
+       emergType = properties["EmergencyType"]
+       emergAlert = properties["EmergencyAlert"]
+
+       print "Broadcast msg: %s \n\t Type: %s \n\t Alert: %s \n\t Popup: %s" \
+               % (text, emergType, emergAlert, popup)
+
+       if properties["Popup"] == True:
+               print "Popup required."
+
+       print "\nPress ENTER to continue"
+
+def set_cbs_state(cbs, state):
+       if state == True:
+               print "Activating cell broadcast..."
+               cbs.SetProperty("Powered", dbus.Boolean(1))
+       else:
+               print "Deactivating cell broadcast..."
+               cbs.SetProperty("Powered", dbus.Boolean(0))
+       print "-----------------------------------------------------------"
+
+def print_cbs_properties(cbs):
+       properties = cbs.GetProperties()
+       print "---------------------PROPERTIES----------------------------"
+       for p in properties:
+               if len(properties[p].__str__()) > 0:
+                       if p == "Powered":
+                               if properties[p] == True:
+                                       print "Cell Broadcast is Activated."
+                               else:
+                                       print "Cell Broadcast is Deactivated."
+                       elif p == "Topics":
+                               print "Currently set CBS %s are: %s" \
+                                       % (p, properties[p])
+                               topics_available = True
+               else:
+                       print "Cell Broadcast %s value empty" % (p)
+       print "-----------------------------------------------------------"
+
+def set_topics(cbs):
+       print_cbs_properties(cbs)
+
+       topicTemp = ""
+       invalidData = False;
+       index = 0
+
+       topics = raw_input('Enter the topic ID(s) you want to register to: ')
+
+       while index < len(topics):
+               if topics[index] == ',' or topics[index] == '-':
+                       topicTemp = ""
+               elif topics[index] >= '0' and topics[index] <= '9':
+                       topicTemp = topicTemp + topics[index]
+               else:
+                       print "Invalid char. \"%s\" entered. Topic not set." \
+                               % (topics[index])
+                       invalidData = True
+                       break
+
+               if topicTemp:
+                       if int(topicTemp) > 999:
+                               invalidData = True
+                               print "Invalid Topic ID %s (range 0-999). \
+                                       \nCould not register." % topicTemp
+
+               index = index + 1
+
+       if invalidData == False:
+               try:
+                       print "Setting Cell Broadcast topics..."
+                       cbs.SetProperty("Topics", topics);
+               except dbus.DBusException, e:
+                       print "Unable to set topic: ", e
+
+       print "-----------------------------------------------------------"
+
+def get_serving_cell_name(netReg):
+       wasFound = False;
+       properties = netReg.GetProperties()
+
+       for p in properties:
+               if p == "BaseStation":
+                       if len(properties[p].__str__()) > 0:
+                               print "Current serving cell name: %s" \
+                                       % (properties["BaseStation"])
+                               wasFound = True;
+                       else:
+                               print "Current Serving cell name empty. \
+                                       Base Station CBS not available."
+
+       if wasFound == False:
+               print "Base Station parameter not found. \
+                       \nBase Station CBS not available."
+       print "-----------------------------------------------------------"
+
+def stdin_handler(fd, condition, cbs, netReg):
+       in_key = os.read(fd.fileno(), 160).rstrip()
+
+       if in_key == '0':
+               set_cbs_state(cbs, True)
+
+       elif in_key == '1':
+               set_cbs_state(cbs, False)
+
+       elif in_key == '2':
+               print_cbs_properties(cbs)
+
+       elif in_key == '3':
+               set_topics(cbs)
+
+       elif in_key == '4':
+               cbs.SetProperty("Topics", "")
+
+       elif in_key == '5':
+               get_serving_cell_name(netReg)
+
+       elif in_key == 'x':
+               sys.exit(1)
+
+       print '\n' * 2
+       print_menu()
+
+       return True
+
+if __name__ == "__main__":
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                               'org.ofono.Manager')
+
+       modems = manager.GetModems()
+       path = modems[0][0]
+
+       cbs = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.CellBroadcast')
+
+       netReg = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.NetworkRegistration')
+
+       cbs.connect_to_signal("PropertyChanged", property_changed)
+       cbs.connect_to_signal("IncomingBroadcast", incoming_broadcast)
+       cbs.connect_to_signal("EmergencyBroadcast", emergency_broadcast)
+
+       print '\n' * 2
+
+       print_menu()
+
+       gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_handler, cbs, \
+                               netReg)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-gnss b/test/test-gnss
new file mode 100755 (executable)
index 0000000..aff7554
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/python
+
+import gobject
+import sys
+import os
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+GNSS_INTERFACE = "org.ofono.AssistedSatelliteNavigation"
+AGENT_INTERFACE = "org.ofono.PositioningRequestAgent"
+
+class PositioningAgent(dbus.service.Object):
+       @dbus.service.method(AGENT_INTERFACE,
+                                       in_signature="", out_signature="")
+       def Release(self):
+               print "Release"
+               mainloop.quit()
+
+       @dbus.service.method(AGENT_INTERFACE,
+                                       in_signature="s", out_signature="")
+       def Request(self, xml):
+               print "positioning data: %s" % (xml)
+
+       @dbus.service.method(AGENT_INTERFACE,
+                                       in_signature="", out_signature="")
+       def ResetAssistanceData(self):
+               print "Reset Assistance Data request received"
+
+def print_menu():
+       print "Select test case"
+       print "-----------------------------------------------------------"
+       print "[0] SendPositioningElement"
+       print "[1] RegisterPositioningRequestAgent"
+       print "[2] UnregisterPositioningRequestAgent"
+       print "[x] Exit"
+       print "-----------------------------------------------------------"
+
+def stdin_handler(fd, condition, gnss, path):
+       in_key = os.read(fd.fileno(), 8).rstrip()
+       if in_key == '0':
+               xml = raw_input('type the element and press enter: ')
+               try:
+                       gnss.SendPositioningElement(dbus.String(xml))
+                       print "ok"
+               except dbus.DBusException, e:
+                       print "Unable to send positioning element"
+
+       elif in_key == '1':
+               try:
+                       gnss.RegisterPositioningRequestAgent("/test/posagent")
+                       print "ok"
+               except dbus.DBusException, e:
+                       print "Unable to register positioning agent"
+
+       elif in_key == '2':
+               try:
+                       gnss.UnregisterPositioningRequestAgent(path)
+                       print "ok"
+               except dbus.DBusException, e:
+                       print "Unable to unregister positioning agent"
+       elif in_key == 'x':
+               sys.exit(1)
+
+       return True
+
+if __name__ == "__main__":
+       if len(sys.argv) < 1:
+               sys.exit(1)
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+       for path, properties in modems:
+               if GNSS_INTERFACE not in properties["Interfaces"]:
+                       continue
+
+               gnss = dbus.Interface(bus.get_object('org.ofono', path),
+                                               GNSS_INTERFACE)
+
+       path = "/test/posagent"
+       agent = PositioningAgent(bus, path)
+
+       print_menu()
+
+       gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_handler,
+                               gnss, path)
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-message-waiting b/test/test-message-waiting
new file mode 100755 (executable)
index 0000000..3abe0ba
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import gobject
+import sys
+import dbus
+import dbus.mainloop.glib
+
+def mw_property_changed(name, value):
+       if name == 'VoicemailMessageCount':
+               print "MessageWaiting property: '%s' changed to '%d'" %\
+                       (name,value)
+       else:
+               print "MessageWaiting property: '%s' changed to '%s'" %\
+                       (name,value)
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       mw = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                       'org.ofono.MessageWaiting')
+
+       mw.connect_to_signal("PropertyChanged", mw_property_changed)
+
+       properties = mw.GetProperties()
+
+       print "Voicemail waiting: %s" % (properties['VoicemailWaiting'])
+       print "Voicemail message count: %d" %\
+               (properties['VoicemailMessageCount'])
+       print "Voicemail mailbox number: %s" %\
+               (properties['VoicemailMailboxNumber'])
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-modem b/test/test-modem
new file mode 100755 (executable)
index 0000000..c91caa3
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(name, value):
+       print "Modem property %s changed to %s" % (name, value)
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+       modem = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                                                       'org.ofono.Modem')
+
+       modem.connect_to_signal("PropertyChanged", property_changed)
+
+       properties = modem.GetProperties()
+
+       if properties.has_key('Name'):
+               print "Name: %s" % (properties['Name'])
+
+       if properties.has_key('Manufacturer'):
+               print "Manufacturer: %s" % (properties['Manufacturer'])
+
+       if properties.has_key('Model'):
+               print "Model: %s" % (properties['Model'])
+
+       if properties.has_key('Revision'):
+               print "Revision: %s" % (properties['Revision'])
+
+       if properties.has_key('Serial'):
+               print "Serial: %s" % (properties['Serial'])
+
+       if properties.has_key('Powered'):
+               print "Powered: %s" % (properties['Powered'])
+
+       if properties.has_key('Online'):
+               print "Online: %s" % (properties['Online'])
+
+       if properties.has_key('Lockdown'):
+               print "Lockdown: %s" % (properties['Lockdown'])
+
+       if properties.has_key('Emergency'):
+               print "Emergency: %s" % (properties['Emergency'])
+
+       if properties.has_key('Features'):
+               print "Features:"
+               for feature in properties["Features"]:
+                       print "    [ %s ]" % (feature)
+
+       if properties.has_key('Interfaces'):
+               print "Interfaces:"
+               for interface in properties["Interfaces"]:
+                       print "    [ %s ]" % (interface)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-network-registration b/test/test-network-registration
new file mode 100755 (executable)
index 0000000..cfc1a43
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+
+import gobject
+import sys
+import dbus
+import dbus.mainloop.glib
+
+def network_property_changed(name, value):
+       print "Network Registration property '%s' changed to '%s'" %\
+               (name, value)
+
+       if name == 'Name' and canexit:
+               mainloop.quit()
+
+if __name__ == "__main__":
+       if len(sys.argv) < 2:
+               print "Usage: %s [modem] <path> - Register to PLMN on <path>" %\
+                       (sys.argv[0])
+               print "Usage: %s [modem] default - Register to default PLMN" %\
+                       (sys.argv[0])
+               sys.exit(1)
+
+       canexit = False
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       if len(sys.argv) == 3:
+               path = sys.argv[1]
+               plmn = sys.argv[2]
+       else:
+               modems = manager.GetModems()
+               path = modems[0][0]
+               plmn = sys.argv[1]
+
+       netreg = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.NetworkRegistration')
+
+       netreg.connect_to_signal("PropertyChanged", network_property_changed)
+
+       props = netreg.GetProperties()
+
+       print "Status is: '%s', Operator Name is: '%s'" %\
+               (props['Status'], props['Name'])
+
+       if props.has_key('LocationAreaCode') and props.has_key('CellId'):
+               print "Location: '%d', Cell: '%d'" %\
+                       (props['LocationAreaCode'], props['CellId'])
+
+       if props.has_key('Technology'):
+               print "Technology: '%s'" % (props['Technology'])
+
+       try:
+               if plmn == 'default':
+                       netreg.Register()
+               else:
+                       obj = bus.get_object('org.ofono', plmn);
+                       op = dbus.Interface(obj, 'org.ofono.NetworkOperator')
+                       op.Register()
+       except dbus.DBusException, e:
+               print "Unable to register: ", e
+               sys.exit(1)
+
+       canexit = True
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-phonebook b/test/test-phonebook
new file mode 100755 (executable)
index 0000000..345ba67
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import dbus, sys
+
+if __name__ == "__main__":
+       bus = dbus.SystemBus()
+
+       if len(sys.argv) == 2:
+               path = sys.argv[1]
+       else:
+               manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                               'org.ofono.Manager')
+               modems = manager.GetModems()
+               path = modems[0][0]
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+       phonebook = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.Phonebook')
+
+       print phonebook.Import(timeout=100)
diff --git a/test/test-push-notification b/test/test-push-notification
new file mode 100755 (executable)
index 0000000..11d384c
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class PushNotificationAgent(dbus.service.Object):
+       @dbus.service.method("org.ofono.PushNotificationAgent",
+                                       in_signature="", out_signature="")
+       def Release(self):
+               print "Release"
+               mainloop.quit()
+
+       @dbus.service.method("org.ofono.PushNotificationAgent",
+                               in_signature="aya{sv}", out_signature="")
+       def ReceiveNotification(self, data, props):
+               for key in props.keys():
+                       print "Key: %s, Value: %s" % (key, props[key])
+
+               print "Received notification of size: %d" % len(data)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+                                                       "org.ofono.Manager")
+
+       modems = manager.GetModems()
+
+       for path, properties in modems:
+               if "org.ofono.PushNotification" not in properties["Interfaces"]:
+                       continue
+
+               pn = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.PushNotification')
+
+       path = "/test/agent"
+       agent = PushNotificationAgent(bus, path)
+       pn.RegisterAgent(path)
+       print "Agent registered"
+
+       mainloop = gobject.MainLoop()
+
+       try:
+               mainloop.run()
+       except KeyboardInterrupt:
+               pn.UnregisterAgent(path)
+               mainloop.run()
diff --git a/test/test-smart-messaging b/test/test-smart-messaging
new file mode 100755 (executable)
index 0000000..0a37ba1
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class SmartMessagingAgent(dbus.service.Object):
+       @dbus.service.method("org.ofono.SmartMessagingAgent",
+                                       in_signature="", out_signature="")
+       def Release(self):
+               print "Release"
+               mainloop.quit()
+
+       @dbus.service.method("org.ofono.SmartMessagingAgent",
+                               in_signature="aya{sv}", out_signature="")
+       def ReceiveBusinessCard(self, data, props):
+               for key in props.keys():
+                       print "Key: %s, Value: %s" % (key, props[key])
+
+               string = ""
+               for byte in data:
+                       string += str(byte)
+
+               print "Received Business Card:"
+               print string
+
+       @dbus.service.method("org.ofono.SmartMessagingAgent",
+                               in_signature="aya{sv}", out_signature="")
+       def ReceiveAppointment(self, data, props):
+               for key in props.keys():
+                       print "Key: %s, Value: %s" % (key, props[key])
+
+               string = ""
+               for byte in data:
+                       string += str(byte)
+
+               print "Received Appointment:"
+               print string
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+                                                       "org.ofono.Manager")
+
+       modems = manager.GetModems()
+
+       for path, properties in modems:
+               if "org.ofono.SmartMessaging" not in properties["Interfaces"]:
+                       continue
+
+               pn = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.SmartMessaging')
+
+       path = "/test/agent"
+       agent = SmartMessagingAgent(bus, path)
+       pn.RegisterAgent(path)
+       print "Agent registered"
+
+       mainloop = gobject.MainLoop()
+
+       try:
+               mainloop.run()
+       except KeyboardInterrupt:
+               pn.UnregisterAgent(path)
+               mainloop.run()
diff --git a/test/test-sms b/test/test-sms
new file mode 100755 (executable)
index 0000000..794d46a
--- /dev/null
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import gobject
+import sys
+import os
+
+import dbus
+import dbus.mainloop.glib
+
+SCA = ""
+lock = "off"
+
+def clear_screen(numlines=100):
+       import os
+       if os.name == "posix":
+               os.system('clear')
+
+       elif os.name in ("nt", "dos", "ce"):
+               os.system('CLS')
+
+       else:
+               print '\n' * numlines
+
+def print_menu():
+       print "Select test case"
+       print "-----------------------------------------------------------"
+       print "[0] Send SMS"
+       print "[1] Enable delivery reports"
+       print "[2] Disable delivery reports"
+       print "[3] Set Service Center Address"
+       print "[4] Set PS-only Bearer"
+       print "[5] Set CS-only Bearer"
+       print "[6] Set PS-preferred Bearer"
+       print "[7] Set CS-preferred Bearer"
+       print "[x] Exit"
+       print "-----------------------------------------------------------"
+
+def print_send_sms_menu():
+       print "Select SMS type"
+       print "-----------------------------------------------------------"
+       print "[1] Default SMS"
+       print "[2] Long SMS > 160 characters"
+       print "[3] SMS with national characters"
+       print "-----------------------------------------------------------"
+
+def message_delivery_report(sms, value):
+       try:
+               sms.SetProperty("UseDeliveryReports", dbus.Boolean(value))
+
+       except dbus.DBusException, e:
+               if value == 1:
+                       print "Unable to activate Delivery Reports - FAIL"
+
+               if value == 0:
+                       print "Unable to deactivate Delivery Reports - FAIL"
+
+def message_service_center_address(sms, value):
+       try:
+               sms.SetProperty("ServiceCenterAddress",dbus.String(value))
+       except dbus.DBusException, e:
+               print "Unable to set correct Service Center Address - FAIL"
+
+def message_bearer(sms, value):
+       try:
+               sms.SetProperty("Bearer", dbus.String(value))
+       except dbus.DBusException, e:
+               print "Unable to set Bearer[%s] - FAIL" % (value)
+
+
+def message_send(sms, number, value):
+       sms.SendMessage(dbus.String(number), value)
+
+def property_changed(property, value):
+       print "[1]:Message Manager property %s changed to %s" %\
+                       (property, value)
+       print "[1]:press ENTER"
+
+def immediate_message(property, value):
+       print "[2]:Message Manager immediate message"
+       print "[2]:Text::%s" % (property)
+       for key in value.keys():
+               val = str(value[key])
+               print "[2]:%s = %s" % (key, val)
+
+       print "[2]:press ENTER"
+
+def incoming_message(property, value):
+       print "[3]:Message Manager incoming message"
+       print "[3]:Text::%s" % (property)
+       for key in value.keys():
+               val = str(value[key])
+               print "[3]:%s = %s" % (key, val)
+
+       print "[3]:press ENTER"
+
+def message_added(property, value):
+       print "[4]:Message Manager[Added]"
+       print "[4]:%s"% (property)
+
+       for key in value.keys():
+               val = str(value[key])
+               print "[4]:%s = %s" % (key, val)
+
+       print "[4]:press ENTER"
+
+def message_removed(property):
+       print "[5]:Message Manager [removed]"
+       print "[5]: %s" % (property)
+       print "[5]:press ENTER"
+
+def print_sms_properties(sms):
+       global SCA
+       properties = sms.GetProperties()
+       print "---------------------PROPERTIES----------------------------"
+       for p in properties:
+               if len(properties[p].__str__()) > 0:
+                       print "%s Message Manager rule is: %s" %\
+                                       (p, properties[p])
+                       print "------------------------------------------" \
+                               "-----------------"
+                       if p == "ServiceCenterAddress":
+                               SCA = properties[p]
+               else:
+                       print "%s Message Manager rule disabled" % (p)
+
+def stdin_handler(fd, condition, sms, value, number):
+       global lock
+       in_key = os.read(fd.fileno(), 160).rstrip()
+
+       if lock == "off":
+               lock = "on"
+               if in_key == '0':
+                       print_send_sms_menu()
+                       sms_type = raw_input('Select SMS type: ')
+
+                       if sms_type == '1':
+                               message_send(sms, number, value)
+
+                       elif sms_type == '2':
+                               val = "abcde12345"
+                               for i in range(30):
+                                       value = value + val
+
+                               message_send(sms, number, value)
+
+                       elif sms_type == '3':
+                               value = "ÖÄÅöäåµʒ×cvcvbŋ"
+                               message_send(sms, number, value)
+
+               elif in_key == '1':
+                       message_delivery_report(sms, 1)
+                       send_msg = raw_input('Send test message[y/n]?: ')
+                       if send_msg == 'y':
+                               message_send(sms, number, ("(1)" + value +
+                                               ": UseDeliveryReports[TRUE]"))
+
+               elif in_key == '2':
+                       message_delivery_report(sms, 0)
+                       send_msg = raw_input('Send test message[y/n]?: ')
+                       if send_msg == 'y':
+                               message_send(sms, number, ("(2) " + value +
+                                               ": UseDeliveryReports[FALSE]"))
+
+               elif in_key == '3':
+                       message_service_center_address(sms, SCA)
+                       send_msg = raw_input('Send test message[y/n]?: ')
+                       if send_msg == 'y':
+                               message_send(sms, number, ("(3) " + value +
+                                               ": ServiceCenterAddress"))
+
+               elif in_key == '4':
+                       message_bearer(sms, "ps-only")
+                       send_msg = raw_input('Send test message[y/n]?: ')
+                       if send_msg == 'y':
+                               message_send(sms, number, ("(4) " + value +
+                                               ": Bearer[ps-only]"))
+
+               elif in_key == '5':
+                       message_bearer(sms, "cs-only")
+                       send_msg = raw_input('Send test message[y/n]?: ')
+                       if send_msg == 'y':
+                               message_send(sms, number, ("(5) " + value +
+                                               ": Bearer[cs-only]"))
+
+               elif in_key == '6':
+                       message_bearer(sms, "ps-preferred")
+                       send_msg = raw_input('Send test message[y/n]?: ')
+                       if send_msg == 'y':
+                               message_send(sms, number, ("(6) " + value +
+                                               ": Bearer[ps-preferred]"))
+
+               elif in_key == '7':
+                       message_bearer(sms, "cs-preferred")
+                       send_msg = raw_input('Send test message[y/n]?: ')
+                       if send_msg == 'y':
+                               message_send(sms,number, ("(7) " + value +
+                                               ": Bearer[cs-preferred]"))
+
+               elif in_key == 'x':
+                       sys.exit(1)
+
+               clear_screen()
+               print_sms_properties(sms)
+               print_menu()
+               lock = "off"
+
+       return True
+
+if __name__ == "__main__":
+
+       if (len(sys.argv) < 3):
+               print "Usage: %s  [modem] <phone_number> <test_message>" % (sys.argv[0])
+               sys.exit(1)
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       if (len(sys.argv) == 4):
+               path = sys.argv[1]
+               number = sys.argv[2]
+               value  = sys.argv[3]
+       else:
+               modems = manager.GetModems()
+               path = modems[0][0]
+               number = sys.argv[1]
+               value  = sys.argv[2]
+
+       sms = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.MessageManager')
+
+       sms.connect_to_signal("PropertyChanged", property_changed)
+       sms.connect_to_signal("ImmediateMessage", immediate_message)
+       sms.connect_to_signal("IncomingMessage", incoming_message)
+       sms.connect_to_signal("MessageAdded", message_added)
+       sms.connect_to_signal("MessageRemoved", message_removed)
+
+       clear_screen()
+       print_sms_properties(sms)
+       print_menu()
+
+       gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_handler,
+                               sms, value, number)
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-ss-control-cb b/test/test-ss-control-cb
new file mode 100755 (executable)
index 0000000..f855635
--- /dev/null
@@ -0,0 +1,95 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(property, value):
+       print "CallBarring property %s changed to %s" % (property, value)
+
+def print_properties(cb):
+        properties = cb.GetProperties()
+
+       for p in properties:
+               print "property %s, value: %s" % (p, properties[p])
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cb = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                                                        'org.ofono.CallBarring')
+
+        cb.connect_to_signal("PropertyChanged", property_changed)
+
+       ss = dbus.Interface(bus.get_object('org.ofono', modems[0]),
+                                                       'org.ofono.SupplementaryServices')
+
+       print_properties(cb)
+
+       print "Trying invalid SS request for CB"
+       try:
+               print ss.Initiate("*33#456666")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Trying invalid SS request for CB"
+       try:
+               print ss.Initiate("*33*ABC#")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Trying invalid SS request for CB"
+       try:
+               print ss.Initiate("*33**ABC#")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Trying invalid SS request for CB"
+       try:
+               print ss.Initiate("*33***12#")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Query Outgoing All"
+       print ss.Initiate("*#33#")
+
+       print "Query Outgoing International"
+       print ss.Initiate("*#331#")
+
+       print "Query Outgoing except home country"
+       print ss.Initiate("*#332#")
+
+       print "Query Incoming All"
+       print ss.Initiate("*#35#")
+
+       print "Query Incoming while Roaming"
+       print ss.Initiate("*#351#")
+
+       print "Query All Outgoing"
+       print ss.Initiate("*#333#")
+
+       print "Query All Incoming"
+       print ss.Initiate("*#353#")
+
+       print "Query All"
+       print ss.Initiate("*#330#")
+
+       print "Enable Barring for Outgoing International calls for Voice"
+       print ss.Initiate("*33*3579*11#")
+
+       print_properties(cb)
+
+       print "Disable All Barrings"
+       print ss.Initiate("#330*3579#")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
+
diff --git a/test/test-ss-control-cf b/test/test-ss-control-cf
new file mode 100755 (executable)
index 0000000..6391bdd
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(property, value):
+       print "CallForwarding property %s changed to %s" % (property, value)
+
+def print_properties(cf):
+       properties = cf.GetProperties()
+
+       for p in properties:
+               if len(properties[p].__str__()) > 0:
+                       value = properties[p]
+               else:
+                       value = "disabled"
+
+               print "%s call forwarding rule: %s" % (p, value)
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cf = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                               'org.ofono.CallForwarding')
+
+       cf.connect_to_signal("PropertyChanged", property_changed)
+
+       ss = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                               'org.ofono.SupplementaryServices')
+
+       # Clear everything
+       ss.Initiate("##002#")
+       print_properties(cf)
+
+       # Busy To +155542, for Voice
+       print "Setting Busy Voice rule to +155542"
+       print ss.Initiate("*67*+155542*11#")
+       print_properties(cf)
+
+       # Not Reachable to +155543, Voice
+       print "Setting Voice Not Reachable rule to +155543"
+       print ss.Initiate("**62*+155543*11#")
+
+       # Not Reachable to +155544, Voice service
+       print "Setting Voice No Reply rule to +155544, timeout=30"
+       print ss.Initiate("**61*+155544*11*30#")
+
+       # Unconditional to +155547, Voice
+       print "Setting Unconditional for Voice to +155545"
+       print ss.Initiate("*21*+155545*10#")
+
+       print_properties(cf)
+
+       print "Query all voice forwardings"
+       print ss.Initiate("*#002**11#")
+
+       print "Query no reply voice forwardings"
+       print ss.Initiate("*#61**11#")
+
+       # Deactivate everything
+       print "Deactivating everything"
+       print ss.Initiate("##002#")
+       print_properties(cf)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-ss-control-cs b/test/test-ss-control-cs
new file mode 100755 (executable)
index 0000000..35db591
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(property, value):
+       print "CallSettings property %s changed to %s" % (property, value)
+
+def print_properties(cs):
+        properties = cs.GetProperties()
+
+       for p in properties:
+               print "property %s, value: %s" % (p, properties[p])
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+
+       cs = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                                                        'org.ofono.CallSettings')
+
+        cs.connect_to_signal("PropertyChanged", property_changed)
+
+       ss = dbus.Interface(bus.get_object('org.ofono', modems[0][0]),
+                                                       'org.ofono.SupplementaryServices')
+
+       print_properties(cs)
+
+       print "Trying invalid SS request for CLIR"
+       try:
+               print ss.Initiate("*31#456666")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Trying invalid SS request for CLIR"
+       try:
+               print ss.Initiate("*31*455*4#")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Trying invalid SS request for CLIR"
+       try:
+               print ss.Initiate("*31**44435#")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Query CLIP"
+       print ss.Initiate("*#30#")
+
+       print "Query CNAP"
+       print ss.Initiate("*#300#")
+
+       print "Query COLP"
+       print ss.Initiate("*#76#")
+
+       print "Query CLIR"
+       print ss.Initiate("*#31#")
+
+       print "Activate CLIR"
+       print ss.Initiate("*31#")
+
+       print_properties(cs)
+
+       print "Deactivate CLIR"
+       print ss.Initiate("#31#")
+
+       print_properties(cs)
+
+       print "Trying invalid SS request for CW"
+       try:
+               print ss.Initiate("*43#456666")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Trying invalid SS request for CW"
+       try:
+               print ss.Initiate("*43*455*4#")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Trying invalid SS request for CW"
+       try:
+               print ss.Initiate("*43**44435#")
+       except dbus.DBusException, e:
+               print "Failed with %s - Good" % e
+
+       print "Query CW"
+       print ss.Initiate("*#43#")
+
+       print "Query CW, only Voice"
+       print ss.Initiate("*#43*11#")
+
+       print "Query CW, only Fax"
+       print ss.Initiate("*#43*13#")
+
+       print "Disable CW for everything"
+       print ss.Initiate("#43#");
+
+       print_properties(cs)
+
+       print "Enable CW for Voice"
+       print ss.Initiate("*43*11#")
+
+       print_properties(cs)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
+
diff --git a/test/test-stk-menu b/test/test-stk-menu
new file mode 100755 (executable)
index 0000000..a9f92e8
--- /dev/null
@@ -0,0 +1,283 @@
+#!/usr/bin/python
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class GoBack(dbus.DBusException):
+       _dbus_error_name = "org.ofono.Error.GoBack"
+
+class EndSession(dbus.DBusException):
+       _dbus_error_name = "org.ofono.Error.EndSession"
+
+class Busy(dbus.DBusException):
+       _dbus_error_name = "org.ofono.Error.Busy"
+
+class StkAgent(dbus.service.Object):
+       exit_on_release = True
+
+       def set_exit_on_release(self, exit_on_release):
+               self.exit_on_release = exit_on_release
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="", out_signature="")
+       def Release(self):
+               print "Release"
+               if self.exit_on_release:
+                       mainloop.quit()
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sya(sy)n", out_signature="y")
+       def RequestSelection(self, title, icon, items, default):
+               print "Title: (%s)" % (title)
+               print "Icon: (%d)" % (icon)
+               index = 0;
+               for item in items:
+                       print "%d. %s (icon: %d)" % (index, item[0], item[1])
+                       index += 1
+
+               print "\nDefault: %d" % (default)
+               select = raw_input("Enter Selection (t, b):")
+
+               if select == 'b':
+                       raise GoBack("User wishes to go back")
+               elif select == 't':
+                       raise EndSession("User wishes to terminate session")
+               else:
+                       return int(select);
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="syb", out_signature="")
+       def DisplayText(self, title, icon, urgent):
+               print "DisplayText (%s)" % (title)
+               print "Icon: (%d)" % (icon)
+               print "Urgent: (%d)" % (urgent)
+               key = raw_input("Press return to clear ('t' terminates, "
+                               "'b' goes back, 'n' busy):")
+
+               if key == 'b':
+                       raise GoBack("User wishes to go back")
+               elif key == 't':
+                       raise EndSession("User wishes to terminate session")
+               elif key == 'n':
+                       raise Busy("User wishes to simulate busy screen")
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sysyyb", out_signature="s")
+       def RequestInput(self, title, icon, default, min_chars, max_chars,
+                               hide_typing):
+               print "Title: (%s)" % (title)
+               print "Icon: (%d)" % (icon)
+               print "Default: (%s)" % (default)
+               print "Hide typing: (%s)" % (hide_typing)
+               print "Enter characters, min: %d, max: %d:" % (min_chars,
+                                                               max_chars)
+               userin = raw_input("");
+
+               return userin
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sysyyb", out_signature="s")
+       def RequestDigits(self, title, icon, default, min_chars, max_chars,
+                               hide_typing):
+               print "Title: (%s)" % (title)
+               print "Icon: (%d)" % (icon)
+               print "Default: (%s)" % (default)
+               print "Hide typing: (%s)" % (hide_typing)
+               print "Enter digits, min: %d, max: %d:" % (min_chars,
+                                                               max_chars)
+               userin = raw_input("'t' terminates, 'b' goes back:");
+
+               if userin == 'b':
+                       raise GoBack("User wishes to go back")
+               elif userin == 't':
+                       raise EndSession("User wishes to terminate session")
+               else:
+                       return userin
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sy", out_signature="s")
+       def RequestKey(self, title, icon):
+               print "Title: (%s)" % (title)
+               print "Icon: (%d)" % (icon)
+               key = raw_input("Enter Key (t, b):")
+
+               if key == 'b':
+                       raise GoBack("User wishes to go back");
+               elif key == 't':
+                       raise EndSession("User wishes to terminate session");
+               else:
+                       return key
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sy", out_signature="s")
+       def RequestDigit(self, title, icon):
+               print "Title: (%s)" % (title)
+               print "Icon: (%d)" % (icon)
+               key = raw_input("Enter Digit (t, b):")
+
+               if key == 'b':
+                       raise GoBack("User wishes to go back");
+               elif key == 't':
+                       raise EndSession("User wishes to terminate session");
+               else:
+                       return key
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sy", out_signature="b")
+       def RequestConfirmation(self, title, icon):
+               print "Title: (%s)" % (title)
+               print "Icon: (%d)" % (icon)
+               key = raw_input("Enter Confirmation (t, b, y, n):")
+
+               if key == 'b':
+                       raise GoBack("User wishes to go back");
+               elif key == 't':
+                       raise EndSession("User wishes to terminate session");
+               elif key == 'y':
+                       return True
+               else:
+                       return False
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sy", out_signature="b")
+       def ConfirmCallSetup(self, info, icon):
+               print "Information: (%s)" % (info)
+               print "Icon: (%d)" % (icon)
+               key = raw_input("Enter Confirmation (t, y, n):")
+
+               if key == 't':
+                       raise EndSession("User wishes to terminate session");
+               elif key == 'y':
+                       return True
+               else:
+                       return False
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                               in_signature="sys", out_signature="b")
+       def ConfirmLaunchBrowser(self, info, icon, url):
+               print "Information: (%s)" % (info)
+               print "Icon: (%d)" % (icon)
+               print "URL (%s)" % (url)
+               key = raw_input("Enter Confirmation (y, n):")
+
+               if key == 'y':
+                       return True
+               else:
+                       return False
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="", out_signature="")
+       def Cancel(self):
+               print "Cancel"
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="ssy", out_signature="")
+       def PlayTone(self, tone, text, icon):
+               print "PlayTone: %s" % (tone)
+               print "Text: %s" % (text)
+               print "Icon: %d" % (icon)
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="ssy", out_signature="")
+       def LoopTone(self, tone, text, icon):
+               print "LoopTone: %s" % (tone)
+               print "Text: %s" % (text)
+               print "Icon: %d" % (icon)
+               key = raw_input("Press return to end before timeout (t):")
+
+               if key == 't':
+                       raise EndSession("User wishes to terminate session");
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="sy", out_signature="")
+       def DisplayActionInformation(self, text, icon):
+               print "Text: %s" % (text)
+               print "Icon: %d" % (icon)
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="sy", out_signature="")
+       def DisplayAction(self, text, icon):
+               print "Text: (%s)" % (text)
+               print "Icon: (%d)" % (icon)
+               key = raw_input("Press 't' to terminate the session ")
+
+               if key == 't':
+                       raise EndSession("User wishes to terminate session")
+
+       @dbus.service.method("org.ofono.SimToolkitAgent",
+                                       in_signature="sy", out_signature="b")
+       def ConfirmOpenChannel(self, info, icon):
+               print "Open channel confirmation: (%s)" % (info)
+               print "Icon: (%d)" % (icon)
+               key = raw_input("Enter Confirmation (t, y, n):")
+
+               if key == 't':
+                       raise EndSession("User wishes to terminate session");
+               elif key == 'y':
+                       return True
+               else:
+                       return False
+
+def property_changed(name, value):
+       print "SimToolKit property: %s changed to '%s'" % (name, value)
+
+if __name__ == '__main__':
+       if len(sys.argv) == 2:
+               mode = sys.argv[1]
+       else:
+               mode = 'menu'
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+                                                       "org.ofono.Manager")
+
+       modems = manager.GetModems()
+
+       for path, properties in modems:
+               if "org.ofono.SimToolkit" not in properties["Interfaces"]:
+                       continue
+
+               stk = dbus.Interface(bus.get_object('org.ofono', path),
+                                       'org.ofono.SimToolkit')
+
+       stk.connect_to_signal("PropertyChanged", property_changed)
+
+       properties = stk.GetProperties()
+
+       if mode == 'menu':
+               if "MainMenuTitle" in properties:
+                       print "Main Menu:"
+                       print "%s" % (properties["MainMenuTitle"])
+                       print "\n"
+
+               if "MainMenu" in properties:
+                       print "Items:"
+                       index = 0
+                       for item in properties["MainMenu"]:
+                               print "%d. %s" % (index, item[0])
+                               index += 1
+
+               path = "/test/agent"
+               agent = StkAgent(bus, path)
+
+               select = int(raw_input("Enter Selection: "))
+               stk.SelectItem(select, path)
+       elif mode == 'agent':
+               path = "/test/agent"
+               agent = StkAgent(bus, path)
+
+               stk.RegisterAgent(path)
+
+               print "Default Agent registered - Waiting for STK command..."
+       else:
+               print "%s [menu|agent]" % (sys.argv[0])
+               exit(0)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-ussd b/test/test-ussd
new file mode 100755 (executable)
index 0000000..c21f5e3
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+
+import sys
+import gobject
+import os
+
+import dbus
+import dbus.mainloop.glib
+
+state = None
+
+def ussd_notification_received(content):
+       print("Network sent a Notification: " + content)
+
+def ussd_request_received(content):
+       print("Network sent a Request: " + content)
+       ss.Cancel()
+
+def ussd_property_changed(name, value):
+       global state
+       if name != "State":
+               return
+       print("USSD session state is " + value)
+       state = str(value)
+
+def stdin_handler(fd, condition):
+       s = os.read(fd.fileno(), 160).rstrip()
+       if not s:
+               ss.Cancel()
+       elif state == "user-response":
+               print ss.Respond(s, timeout = 100)
+       elif state == "idle":
+               print ss.Initiate(s, timeout = 100)
+       else:
+               print "Invalid state", state
+       return True
+
+if __name__ == "__main__":
+       if (len(sys.argv) < 2):
+               print "Usage: %s [modem] <ussd-string>" % (sys.argv[0])
+               sys.exit(1)
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+       modem = modems[0][0]
+
+       if (len(sys.argv) == 2):
+               ussd = sys.argv[1]
+       else:
+               modem = sys.argv[1]
+               ussd = sys.argv[2]
+
+       ss = dbus.Interface(bus.get_object('org.ofono', modem),
+                                       'org.ofono.SupplementaryServices')
+
+       props = ss.GetProperties()
+       for p in props:
+               ussd_property_changed(p, props[p])
+
+       ss.connect_to_signal("NotificationReceived", ussd_notification_received)
+       ss.connect_to_signal("RequestReceived", ussd_request_received)
+       ss.connect_to_signal("PropertyChanged", ussd_property_changed)
+
+       print ss.Initiate(ussd, timeout=100)
+
+       gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_handler)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-voicecall b/test/test-voicecall
new file mode 100755 (executable)
index 0000000..3b0d432
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+import sys
+
+def hangup_all():
+       print "Hanging up"
+       vcmanager.HangupAll()
+
+def print_calls():
+       calls = vcmanager.GetCalls()
+       if (len(calls) != 0):
+               print "No calls available"
+       else:
+               for path, properties in calls:
+                       print "    [ %s ]" % (path)
+
+                       for key in properties.keys():
+                               val = str(properties[key])
+                               print "        %s = %s" % (key, val)
+                       print
+
+def voicecalls_call_added(path, properties):
+       print "    Voice Call [ %s ] Added" % (path)
+
+       for key in properties.keys():
+               val = str(properties[key])
+               print "        %s = %s" % (key, val)
+       print
+
+def voicecalls_call_removed(path):
+       print "    Voice Call [ %s ] Removed" % (path)
+
+def voicecall_property_changed(name, value):
+       print "Voicecall property: '%s' changed to '%s'" % (name, value)
+
+def voicecall_disconnect_reason(reason):
+       print "Voicecall disconnect reason: '%s'" % (reason)
+
+if __name__ == "__main__":
+       global vcmanager
+
+       if (len(sys.argv) < 2):
+               print "Usage: %s [modem] <number>" % (sys.argv[0])
+               sys.exit(1)
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                                       'org.ofono.Manager')
+
+       modems = manager.GetModems()
+       modem = modems[0][0]
+
+       if (len(sys.argv) == 3):
+               modem = sys.argv[1]
+               number = sys.argv[2]
+       else:
+               number = sys.argv[1]
+       print "Using modem %s" % modem
+
+       vcmanager = dbus.Interface(bus.get_object('org.ofono', modem),
+                                               'org.ofono.VoiceCallManager')
+
+       vcmanager.connect_to_signal("CallAdded", voicecalls_call_added)
+
+       vcmanager.connect_to_signal("CallRemoved", voicecalls_call_removed)
+
+       print_calls()
+
+       print "Dialing %s..." % number
+       obj = vcmanager.Dial(number, "")
+       print "Dialing in progress, got obj: %s" % (obj)
+
+       call = dbus.Interface(bus.get_object('org.ofono', obj),
+                                               'org.ofono.VoiceCall')
+
+       properties = call.GetProperties()
+
+       print "State: %s, Number: %s" %\
+               (properties['State'], properties['LineIdentification'])
+
+       call.connect_to_signal("PropertyChanged", voicecall_property_changed)
+       call.connect_to_signal("DisconnectReason", voicecall_disconnect_reason)
+
+       gobject.timeout_add(1000000, hangup_all)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/unlock-pin b/test/unlock-pin
new file mode 100755 (executable)
index 0000000..d77841a
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 4:
+       path = sys.argv[1]
+       pin_type = sys.argv[2]
+       pin = sys.argv[3]
+elif len(sys.argv) == 3:
+       manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+                                       'org.ofono.Manager')
+       modems = manager.GetModems()
+       path = modems[0][0]
+       pin_type = sys.argv[1]
+       pin = sys.argv[2]
+else:
+       print "%s [PATH] pin_type pin" % (sys.argv[0])
+       sys.exit(0)
+
+print "Unlock %s %s for modem %s..." % (pin_type, pin, path)
+
+simmanager = dbus.Interface(bus.get_object('org.ofono', path),
+                               'org.ofono.SimManager')
+simmanager.UnlockPin(pin_type, pin)
diff --git a/tools/auto-enable.c b/tools/auto-enable.c
new file mode 100644 (file)
index 0000000..1d5b53b
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <gdbus.h>
+
+#define OFONO_SERVICE  "org.ofono"
+
+#define OFONO_MANAGER_INTERFACE                OFONO_SERVICE ".Manager"
+#define OFONO_MODEM_INTERFACE          OFONO_SERVICE ".Modem"
+#define OFONO_SIM_INTERFACE            OFONO_SERVICE ".SimManager"
+
+struct modem_data {
+       char *path;
+       DBusConnection *conn;
+       guint sim_changed_watch;
+       dbus_bool_t has_powered;
+       dbus_bool_t has_online;
+       dbus_bool_t has_sim;
+};
+
+static GHashTable *modem_list;
+
+static gboolean option_online = FALSE;
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError err;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply) == TRUE) {
+               g_printerr("%s: %s\n", err.name, err.message);
+               dbus_error_free(&err);
+       }
+
+       dbus_message_unref(reply);
+}
+
+static int set_property(struct modem_data *modem, const char *key,
+                                               int type, const void *val)
+{
+       DBusConnection *conn = modem->conn;
+       DBusMessage *msg;
+       DBusMessageIter iter, value;
+       DBusPendingCall *call;
+       const char *signature;
+
+       msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
+                                       OFONO_MODEM_INTERFACE, "SetProperty");
+       if (msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(msg, FALSE);
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
+
+       switch (type) {
+       case DBUS_TYPE_BOOLEAN:
+               signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+               break;
+       default:
+               dbus_message_unref(msg);
+               return -EINVAL;
+       }
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(&iter, &value);
+
+       if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return -EIO;
+       }
+
+       dbus_message_unref(msg);
+
+       if (call == NULL)
+               return -EINVAL;
+
+       dbus_pending_call_set_notify(call, set_property_reply, modem, NULL);
+
+       dbus_pending_call_unref(call);
+
+       return 0;
+}
+
+static gboolean sim_changed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "SubscriberIdentity") == FALSE)
+               return TRUE;
+
+       if (modem->has_online == FALSE) {
+               dbus_bool_t online = TRUE;
+               set_property(modem, "Online", DBUS_TYPE_BOOLEAN, &online);
+       }
+
+       return TRUE;
+}
+
+static void check_interfaces(struct modem_data *modem, DBusMessageIter *iter)
+{
+       DBusMessageIter entry;
+       dbus_bool_t has_sim = FALSE;
+
+       dbus_message_iter_recurse(iter, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *interface;
+
+               dbus_message_iter_get_basic(&entry, &interface);
+
+               if (g_str_equal(interface, OFONO_SIM_INTERFACE) == TRUE)
+                       has_sim = TRUE;
+
+               dbus_message_iter_next(&entry);
+       }
+
+       if (modem->has_sim == has_sim)
+               return;
+
+       modem->has_sim = has_sim;
+}
+
+static void check_property(struct modem_data *modem, const char *key,
+                                               DBusMessageIter *value)
+{
+       if (g_str_equal(key, "Interfaces") == TRUE) {
+               check_interfaces(modem, value);
+               return;
+       }
+
+       if (g_str_equal(key, "Powered") == TRUE) {
+               dbus_bool_t powered;
+
+               dbus_message_iter_get_basic(value, &powered);
+
+               if (powered == TRUE) {
+                       g_print("modem enabled (%s)\n", modem->path);
+
+                       modem->has_powered = TRUE;
+               } else {
+                       g_print("modem disabled (%s)\n", modem->path);
+
+                       if (modem->has_powered == FALSE) {
+                               powered = TRUE;
+
+                               set_property(modem, "Powered",
+                                               DBUS_TYPE_BOOLEAN, &powered);
+                       }
+               }
+       } else if (g_str_equal(key, "Online") == TRUE) {
+               dbus_bool_t online;
+
+               dbus_message_iter_get_basic(value, &online);
+
+               if (online == TRUE) {
+                       g_print("modem online (%s)\n", modem->path);
+
+                       modem->has_online = TRUE;
+               } else
+                       g_print("modem offline (%s)\n", modem->path);
+       } else if (g_str_equal(key, "Lockdown") == TRUE) {
+               dbus_bool_t lockdown;
+
+               dbus_message_iter_get_basic(value, &lockdown);
+
+               if (lockdown == TRUE)
+                       g_print("modem locked (%s)\n", modem->path);
+               else
+                       g_print("modem unlocked (%s)\n", modem->path);
+       }
+}
+
+static void destroy_modem(gpointer data)
+{
+       struct modem_data *modem = data;
+
+       g_print("modem removed (%s)\n", modem->path);
+
+       g_dbus_remove_watch(modem->conn, modem->sim_changed_watch);
+
+       dbus_connection_unref(modem->conn);
+
+       g_free(modem->path);
+       g_free(modem);
+}
+
+static void create_modem(DBusConnection *conn,
+                               const char *path, DBusMessageIter *iter)
+{
+       struct modem_data *modem;
+       DBusMessageIter dict;
+
+       modem = g_try_new0(struct modem_data, 1);
+       if (modem == NULL)
+               return;
+
+       modem->path = g_strdup(path);
+       modem->conn = dbus_connection_ref(conn);
+
+       modem->sim_changed_watch = g_dbus_add_signal_watch(conn,
+                               NULL, NULL, OFONO_SIM_INTERFACE,
+                               "PropertyChanged", sim_changed, modem, NULL);
+
+       g_hash_table_replace(modem_list, modem->path, modem);
+
+       g_print("modem added (%s)\n", modem->path);
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               check_property(modem, key, &value);
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static gboolean modem_added(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       DBusMessageIter iter, dict;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &dict);
+
+       create_modem(conn, path, &iter);
+
+       return TRUE;
+}
+
+static gboolean modem_removed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       DBusMessageIter iter;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       g_hash_table_remove(modem_list, path);
+
+       return TRUE;
+}
+
+static gboolean modem_changed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *path, *key;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       path = dbus_message_get_path(msg);
+
+       modem = g_hash_table_lookup(modem_list, path);
+       if (modem == NULL)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       check_property(modem, key, &value);
+
+       return TRUE;
+}
+
+static void get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusConnection *conn = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter, list;
+       DBusError err;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply) == TRUE) {
+               g_printerr("%s: %s\n", err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE)
+               goto done;
+
+       if (dbus_message_iter_init(reply, &iter) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&iter, &list);
+
+       while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter entry, dict;
+               const char *path;
+
+               dbus_message_iter_recurse(&list, &entry);
+               dbus_message_iter_get_basic(&entry, &path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &dict);
+
+               create_modem(conn, path, &entry);
+
+               dbus_message_iter_next(&list);
+       }
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int get_modems(DBusConnection *conn)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+
+       msg = dbus_message_new_method_call(OFONO_SERVICE, "/",
+                                       OFONO_MANAGER_INTERFACE, "GetModems");
+       if (msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(msg, FALSE);
+
+       g_print("getting modems\n");
+
+       if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return -EIO;
+       }
+
+       dbus_message_unref(msg);
+
+       if (call == NULL)
+               return -EINVAL;
+
+       dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL);
+
+       dbus_pending_call_unref(call);
+
+       return 0;
+}
+
+static gboolean ofono_running = FALSE;
+
+static guint modem_added_watch;
+static guint modem_removed_watch;
+static guint modem_changed_watch;
+
+static void ofono_connect(DBusConnection *conn, void *user_data)
+{
+       g_print("starting telephony interface\n");
+
+       ofono_running = TRUE;
+
+       modem_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, destroy_modem);
+
+       modem_added_watch = g_dbus_add_signal_watch(conn, NULL, NULL,
+                               OFONO_MANAGER_INTERFACE, "ModemAdded",
+                                               modem_added, NULL, NULL);
+       modem_removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL,
+                               OFONO_MANAGER_INTERFACE, "ModemRemoved",
+                                               modem_removed, NULL, NULL);
+       modem_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL,
+                               OFONO_MODEM_INTERFACE, "PropertyChanged",
+                                               modem_changed, NULL, NULL);
+
+       get_modems(conn);
+}
+
+static void ofono_disconnect(DBusConnection *conn, void *user_data)
+{
+       g_print("stopping telephony interface\n");
+
+       ofono_running = FALSE;
+
+       g_dbus_remove_watch(conn, modem_added_watch);
+       modem_added_watch = 0;
+       g_dbus_remove_watch(conn, modem_removed_watch);
+       modem_removed_watch = 0;
+       g_dbus_remove_watch(conn, modem_changed_watch);
+       modem_changed_watch = 0;
+
+       g_hash_table_destroy(modem_list);
+       modem_list = NULL;
+}
+
+static GMainLoop *main_loop = NULL;
+
+static volatile sig_atomic_t __terminated = 0;
+
+static void sig_term(int sig)
+{
+       if (__terminated > 0)
+               return;
+
+       __terminated = 1;
+
+       g_print("Terminating\n");
+
+       g_main_loop_quit(main_loop);
+}
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+       g_printerr("D-Bus disconnect\n");
+
+       g_main_loop_quit(main_loop);
+}
+
+static gboolean option_version = FALSE;
+
+static GOptionEntry options[] = {
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { "online", 'o', 0, G_OPTION_ARG_NONE, &option_online,
+                               "Bring device online if possible" },
+       { NULL },
+};
+
+int main(int argc, char **argv)
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       DBusConnection *conn;
+       DBusError err;
+       guint watch;
+       struct sigaction sa;
+
+#ifdef NEED_THREADS
+       if (g_thread_supported() == FALSE)
+               g_thread_init(NULL);
+#endif
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               printf("%s\n", VERSION);
+               exit(0);
+       }
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+#ifdef NEED_THREADS
+       if (dbus_threads_init_default() == FALSE) {
+               fprintf(stderr, "Can't init usage of threads\n");
+               exit(1);
+       }
+#endif
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't register with system bus\n");
+               exit(1);
+       }
+
+       g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       watch = g_dbus_add_service_watch(conn, OFONO_SERVICE,
+                               ofono_connect, ofono_disconnect, NULL, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_dbus_remove_watch(conn, watch);
+
+       if (ofono_running == TRUE)
+               ofono_disconnect(conn, NULL);
+
+       dbus_connection_unref(conn);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/tools/get-location.c b/tools/get-location.c
new file mode 100644 (file)
index 0000000..620a7cc
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define OFONO_SERVICE "org.ofono"
+
+#define MANAGER_PATH   "/"
+#define MANAGER_INTERFACE OFONO_SERVICE ".Manager"
+#define LOCATION_REPORTING_INTERFACE OFONO_SERVICE ".LocationReporting"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+static GMainLoop *event_loop;
+
+static char *get_first_modem_path(DBusConnection *conn)
+{
+       DBusMessage *msg, *reply;
+       DBusMessageIter iter, array, entry;
+       DBusError error;
+       int arg_type;
+       const char *path;
+
+       msg = dbus_message_new_method_call(OFONO_SERVICE, MANAGER_PATH,
+                                               MANAGER_INTERFACE, "GetModems");
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1,
+                                                                       &error);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error)) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       fprintf(stderr, "GetModems failed");
+               }
+
+
+               return NULL;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       dbus_message_iter_recurse(&iter, &array);
+       dbus_message_iter_recurse(&array, &entry);
+
+       arg_type = dbus_message_iter_get_arg_type(&entry);
+       while (arg_type != DBUS_TYPE_INVALID &&
+                                       arg_type != DBUS_TYPE_OBJECT_PATH) {
+               dbus_message_iter_next(&entry);
+               arg_type = dbus_message_iter_get_arg_type(&entry);
+       }
+
+       if (arg_type != DBUS_TYPE_OBJECT_PATH) {
+               fprintf(stderr, "modem not found\n");
+               return NULL;
+       }
+
+       dbus_message_iter_get_basic(&entry, &path);
+       fprintf(stderr, "Using modem: %s\n", path);
+
+       return strdup(path);
+}
+
+static gboolean data_read_cb(GIOChannel *channel, GIOCondition cond,
+                                                               gpointer data)
+{
+       int fd = GPOINTER_TO_INT(data);
+       char buf[128];
+       int ret;
+
+       while ((ret = read(fd, buf, sizeof(buf) - 1)) >= 0) {
+               buf[ret] = '\0';
+               printf("%s", buf);
+       }
+
+       if (errno != EAGAIN && errno != EWOULDBLOCK)
+               fprintf(stderr, "Error reading fd");
+
+       return TRUE;
+}
+
+static int setup_data_channel(DBusConnection *conn, const char *path)
+{
+       DBusMessage *msg, *reply;
+       DBusError error;
+       int fd, fd_source;
+       GIOChannel *channel;
+
+       msg = dbus_message_new_method_call(OFONO_SERVICE, path,
+                               LOCATION_REPORTING_INTERFACE, "Request");
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1,
+                                                                       &error);
+       dbus_message_unref(msg);
+
+       printf("Requesting location-reporting...\n");
+       if (!reply) {
+               if (dbus_error_is_set(&error)) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       fprintf(stderr, "Request() failed");
+               }
+
+               return -1;
+       }
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_UNIX_FD, &fd,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               fprintf(stderr, "%s\n", error.message);
+               dbus_error_free(&error);
+
+               return -1;
+       }
+
+       printf("Using fd=%d\n", fd);
+       fcntl(fd, F_SETFL, O_NONBLOCK);
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       fd_source = g_io_add_watch(channel, G_IO_IN, data_read_cb,
+                                                       GINT_TO_POINTER(fd));
+       g_io_channel_unref(channel);
+
+       return fd_source;
+}
+
+static gboolean signal_cb(GIOChannel *channel, GIOCondition cond, gpointer data)
+{
+       int signal_fd = GPOINTER_TO_INT(data);
+       struct signalfd_siginfo si;
+       ssize_t len;
+
+       len = read(signal_fd, &si, sizeof(si));
+       if (len < 0)
+               return TRUE;
+
+       g_main_loop_quit(event_loop);
+
+       return TRUE;
+}
+
+static int setup_signals(void)
+{
+       sigset_t mask;
+       int signal_fd, signal_source;
+       GIOChannel *signal_io;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGTERM);
+       sigaddset(&mask, SIGINT);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               fprintf(stderr, "Can't set signal mask - %m");
+
+               return -1;
+       }
+
+       signal_fd = signalfd(-1, &mask, 0);
+       if (signal_fd < 0) {
+               fprintf(stderr, "Can't create signal filedescriptor - %m");
+
+               return -1;
+       }
+
+       signal_io = g_io_channel_unix_new(signal_fd);
+       g_io_channel_set_close_on_unref(signal_io, TRUE);
+       signal_source = g_io_add_watch(signal_io,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       signal_cb, GINT_TO_POINTER(signal_fd));
+       g_io_channel_unref(signal_io);
+
+       return signal_source;
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       char *modem_path;
+       int signal_source;
+       int data_source;
+       int ret;
+
+       if (DBUS_TYPE_UNIX_FD < 0) {
+               fprintf(stderr, "File-descriptor passing not supported\n");
+               exit(1);
+       }
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               fprintf(stderr, "Can't get on system bus\n");
+               exit(1);
+       }
+
+       if (argc > 1)
+               modem_path = strdup(argv[1]);
+       else
+               modem_path = get_first_modem_path(conn);
+
+       if (modem_path == NULL) {
+               ret = 1;
+               goto out;
+       }
+
+       signal_source = setup_signals();
+       if (signal_source < 0)
+               goto out;
+
+       data_source = setup_data_channel(conn, modem_path);
+       if (data_source < 0) {
+               g_source_remove(signal_source);
+               goto out;
+       }
+
+       event_loop = g_main_loop_new(NULL, FALSE);
+
+       g_main_loop_run(event_loop);
+
+       ret = 0;
+
+       g_source_remove(signal_source);
+       g_source_remove(data_source);
+       g_main_loop_unref(event_loop);
+
+out:
+       if (modem_path)
+               free(modem_path);
+
+       dbus_connection_unref(conn);
+
+       return ret;
+}
diff --git a/tools/huawei-audio.c b/tools/huawei-audio.c
new file mode 100644 (file)
index 0000000..8f3c572
--- /dev/null
@@ -0,0 +1,844 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include <gdbus.h>
+
+#define OFONO_SERVICE  "org.ofono"
+
+#define OFONO_MANAGER_INTERFACE                OFONO_SERVICE ".Manager"
+#define OFONO_MODEM_INTERFACE          OFONO_SERVICE ".Modem"
+#define OFONO_CALLMANAGER_INTERFACE    OFONO_SERVICE ".VoiceCallManager"
+#define OFONO_CALL_INTERFACE           OFONO_SERVICE ".VoiceCall"
+#define OFONO_AUDIO_INTERFACE          OFONO_SERVICE ".AudioSettings"
+
+struct modem_data {
+       char *path;
+       GHashTable *call_list;
+
+       DBusConnection *conn;
+       guint call_added_watch;
+       guint call_removed_watch;
+       guint call_changed_watch;
+       guint audio_changed_watch;
+
+       gboolean has_callmanager;
+       gboolean has_audiosettings;
+       gboolean is_huawei;
+       gint audio_users;
+       guint audio_watch;
+
+       int format;
+       int channels;
+       int speed;
+       int dsp_out;
+};
+
+struct call_data {
+       char *path;
+       struct modem_data *modem;
+};
+
+static GHashTable *modem_list;
+
+static gboolean audio_receive(GIOChannel *channel,
+                               GIOCondition condition, gpointer user_data)
+{
+       struct modem_data *modem = user_data;
+       char buf[512];
+       ssize_t rlen, wlen;
+       int fd;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR)) {
+               modem->audio_watch = 0;
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       rlen = read(fd, buf, sizeof(buf));
+       if (rlen < 0)
+               return TRUE;
+
+       wlen = write(modem->dsp_out, buf, rlen);
+       if (wlen < 0) {
+               modem->audio_watch = 0;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void open_audio(struct modem_data *modem)
+{
+       GIOChannel *channel;
+       struct termios ti;
+       int fd;
+
+       if (modem->is_huawei == FALSE)
+               return;
+
+       if (modem->audio_users > 0)
+               return;
+
+       g_print("enabling audio\n");
+
+       modem->dsp_out = open("/dev/dsp", O_WRONLY, 0);
+       if (modem->dsp_out < 0) {
+               g_printerr("Failed to open DSP device\n");
+               return;
+       }
+
+       if (ioctl(modem->dsp_out, SNDCTL_DSP_SETFMT, &modem->format) < 0)
+               g_printerr("Failed to set DSP format\n");
+
+       if (ioctl(modem->dsp_out, SNDCTL_DSP_CHANNELS, &modem->channels) < 0)
+               g_printerr("Failed to set DSP channels\n");
+
+       if (ioctl(modem->dsp_out, SNDCTL_DSP_SPEED, &modem->speed) < 0)
+               g_printerr("Failed to set DSP speed\n");
+
+       fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY);
+       if (fd < 0) {
+               g_printerr("Failed to open audio port\n");
+               close(modem->dsp_out);
+               modem->dsp_out = -1;
+               return;
+       }
+
+       /* Switch TTY to raw mode */
+       memset(&ti, 0, sizeof(ti));
+       cfmakeraw(&ti);
+
+       tcflush(fd, TCIOFLUSH);
+       tcsetattr(fd, TCSANOW, &ti);
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL) {
+               g_printerr("Failed to create IO channel\n");
+               close(modem->dsp_out);
+               modem->dsp_out = -1;
+               close(fd);
+               return;
+       }
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       modem->audio_watch = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               audio_receive, modem);
+
+       g_io_channel_unref(channel);
+
+       modem->audio_users++;
+}
+
+static void close_audio(struct modem_data *modem)
+{
+       if (modem->is_huawei == FALSE)
+               return;
+
+       modem->audio_users--;
+
+       if (modem->audio_users > 0)
+               return;
+
+       g_print("disabling audio\n");
+
+       if (modem->audio_watch > 0) {
+               g_source_remove(modem->audio_watch);
+               modem->audio_watch = 0;
+       }
+
+       close(modem->dsp_out);
+}
+
+static void audio_set(struct modem_data *modem, const char *key,
+                                               DBusMessageIter *iter)
+{
+       const char *str = NULL;
+
+       if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING)
+               dbus_message_iter_get_basic(iter, &str);
+
+       if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BOOLEAN) {
+               dbus_bool_t val;
+
+               dbus_message_iter_get_basic(iter, &val);
+               str = (val == TRUE) ? "yes" : "no";
+       }
+
+       g_print("updating audio (%s) [ %s = %s ]\n", modem->path,
+                                               key, str ? str : "...");
+}
+
+static void call_set(struct call_data *call, const char *key,
+                                               DBusMessageIter *iter)
+{
+       const char *str = NULL;
+
+       if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING)
+               dbus_message_iter_get_basic(iter, &str);
+
+       if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BOOLEAN) {
+               dbus_bool_t val;
+
+               dbus_message_iter_get_basic(iter, &val);
+               str = (val == TRUE) ? "yes" : "no";
+       }
+
+       g_print("updating call (%s) [ %s = %s ]\n", call->path,
+                                               key, str ? str : "...");
+}
+
+static void destroy_call(gpointer data)
+{
+       struct call_data *call = data;
+
+       g_print("call removed (%s)\n", call->path);
+
+       close_audio(call->modem);
+
+       g_free(call->path);
+       g_free(call);
+}
+
+static void create_call(struct modem_data *modem,
+                               const char *path, DBusMessageIter *iter)
+{
+       struct call_data *call;
+       DBusMessageIter dict;
+
+       call = g_try_new0(struct call_data, 1);
+       if (call == NULL)
+               return;
+
+       call->path = g_strdup(path);
+
+       g_hash_table_replace(modem->call_list, call->path, call);
+
+       g_print("call added (%s)\n", call->path);
+
+       call->modem = modem;
+
+       open_audio(modem);
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               call_set(call, key, &value);
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static gboolean call_added(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       DBusMessageIter iter, dict;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &dict);
+
+       create_call(modem, path, &iter);
+
+       return TRUE;
+}
+
+static gboolean call_removed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       DBusMessageIter iter;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       g_hash_table_remove(modem->call_list, path);
+
+       return TRUE;
+}
+
+static gboolean call_changed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       struct call_data *call;
+       DBusMessageIter iter, value;
+       const char *path, *key;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       path = dbus_message_get_path(msg);
+
+       call = g_hash_table_lookup(modem->call_list, path);
+       if (call == NULL)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       call_set(call, key, &value);
+
+       return TRUE;
+}
+
+static gboolean audio_changed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       audio_set(modem, key, &value);
+
+       return TRUE;
+}
+
+static void get_calls_reply(DBusPendingCall *call, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter, list;
+       DBusError err;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply) == TRUE) {
+               g_printerr("%s: %s\n", err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE)
+               goto done;
+
+       if (dbus_message_iter_init(reply, &iter) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&iter, &list);
+
+       while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter entry, dict;
+               const char *path;
+
+               dbus_message_iter_recurse(&list, &entry);
+               dbus_message_iter_get_basic(&entry, &path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &dict);
+
+               create_call(modem, path, &dict);
+
+               dbus_message_iter_next(&list);
+       }
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int get_calls(struct modem_data *modem)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+
+       msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
+                               OFONO_CALLMANAGER_INTERFACE, "GetCalls");
+       if (msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(msg, FALSE);
+
+       g_print("getting calls (%s)\n", modem->path);
+
+       if (dbus_connection_send_with_reply(modem->conn, msg,
+                                               &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return -EIO;
+       }
+
+       dbus_message_unref(msg);
+
+       if (call == NULL)
+               return -EINVAL;
+
+       dbus_pending_call_set_notify(call, get_calls_reply, modem, NULL);
+
+       dbus_pending_call_unref(call);
+
+       return 0;
+}
+
+static void check_interfaces(struct modem_data *modem, DBusMessageIter *iter)
+{
+       DBusMessageIter entry;
+       gboolean has_callmanager = FALSE;
+       gboolean has_audiosettings = FALSE;
+
+       dbus_message_iter_recurse(iter, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *interface;
+
+               dbus_message_iter_get_basic(&entry, &interface);
+
+               if (g_str_equal(interface, OFONO_CALLMANAGER_INTERFACE) == TRUE)
+                       has_callmanager = TRUE;
+
+               if (g_str_equal(interface, OFONO_AUDIO_INTERFACE) == TRUE)
+                       has_audiosettings = TRUE;
+
+               dbus_message_iter_next(&entry);
+       }
+
+       modem->has_audiosettings = has_audiosettings;
+
+       if (modem->has_callmanager == has_callmanager)
+               return;
+
+       modem->has_callmanager = has_callmanager;
+       if (modem->has_callmanager == TRUE)
+               get_calls(modem);
+}
+
+static void check_manufacturer(struct modem_data *modem, DBusMessageIter *iter)
+{
+       const char *manufacturer;
+
+       dbus_message_iter_get_basic(iter, &manufacturer);
+
+       if (g_str_equal(manufacturer, "huawei") == TRUE) {
+               g_print("found Huawei modem\n");
+               modem->is_huawei = TRUE;
+       }
+}
+
+static void destroy_modem(gpointer data)
+{
+       struct modem_data *modem = data;
+
+       g_dbus_remove_watch(modem->conn, modem->call_added_watch);
+       g_dbus_remove_watch(modem->conn, modem->call_removed_watch);
+       g_dbus_remove_watch(modem->conn, modem->call_changed_watch);
+       g_dbus_remove_watch(modem->conn, modem->audio_changed_watch);
+
+       g_hash_table_destroy(modem->call_list);
+
+       g_print("modem removed (%s)\n", modem->path);
+
+       g_free(modem->path);
+       g_free(modem);
+}
+
+static void create_modem(DBusConnection *conn,
+                               const char *path, DBusMessageIter *iter)
+{
+       struct modem_data *modem;
+       DBusMessageIter dict;
+
+       modem = g_try_new0(struct modem_data, 1);
+       if (modem == NULL)
+               return;
+
+       modem->path = g_strdup(path);
+
+       modem->format = AFMT_S16_LE;
+       modem->channels = 1;
+       modem->speed = 8000;
+       modem->dsp_out = -1;
+
+       modem->call_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, destroy_call);
+
+       modem->conn = conn;
+
+       modem->call_added_watch = g_dbus_add_signal_watch(conn, NULL,
+                               modem->path, OFONO_CALLMANAGER_INTERFACE,
+                               "CallAdded", call_added, modem, NULL);
+       modem->call_removed_watch = g_dbus_add_signal_watch(conn, NULL,
+                               modem->path, OFONO_CALLMANAGER_INTERFACE,
+                               "CallRemoved", call_removed, modem, NULL);
+       modem->call_changed_watch = g_dbus_add_signal_watch(conn, NULL,
+                               NULL, OFONO_CALL_INTERFACE,
+                               "PropertyChanged", call_changed, modem, NULL);
+       modem->audio_changed_watch = g_dbus_add_signal_watch(conn, NULL,
+                               NULL, OFONO_AUDIO_INTERFACE,
+                               "PropertyChanged", audio_changed, modem, NULL);
+
+       g_hash_table_replace(modem_list, modem->path, modem);
+
+       g_print("modem added (%s)\n", modem->path);
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Interfaces") == TRUE)
+                       check_interfaces(modem, &value);
+               else if (g_str_equal(key, "Manufacturer") == TRUE)
+                       check_manufacturer(modem, &value);
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static gboolean modem_added(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       DBusMessageIter iter, dict;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &dict);
+
+       create_modem(conn, path, &iter);
+
+       return TRUE;
+}
+
+static gboolean modem_removed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       DBusMessageIter iter;
+       const char *path;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       g_hash_table_remove(modem_list, path);
+
+       return TRUE;
+}
+
+static gboolean modem_changed(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *path, *key;
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return TRUE;
+
+       path = dbus_message_get_path(msg);
+
+       modem = g_hash_table_lookup(modem_list, path);
+       if (modem == NULL)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Interfaces") == TRUE)
+               check_interfaces(modem, &value);
+       else if (g_str_equal(key, "Manufacturer") == TRUE)
+               check_manufacturer(modem, &value);
+
+       return TRUE;
+}
+
+static void get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusConnection *conn = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusMessageIter iter, list;
+       DBusError err;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply) == TRUE) {
+               g_printerr("%s: %s\n", err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE)
+               goto done;
+
+       if (dbus_message_iter_init(reply, &iter) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&iter, &list);
+
+       while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter entry, dict;
+               const char *path;
+
+               dbus_message_iter_recurse(&list, &entry);
+               dbus_message_iter_get_basic(&entry, &path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &dict);
+
+               create_modem(conn, path, &entry);
+
+               dbus_message_iter_next(&list);
+       }
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int get_modems(DBusConnection *conn)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+
+       msg = dbus_message_new_method_call(OFONO_SERVICE, "/",
+                                       OFONO_MANAGER_INTERFACE, "GetModems");
+       if (msg == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(msg, FALSE);
+
+       g_print("getting modems\n");
+
+       if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return -EIO;
+       }
+
+       dbus_message_unref(msg);
+
+       if (call == NULL)
+               return -EINVAL;
+
+       dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL);
+
+       dbus_pending_call_unref(call);
+
+       return 0;
+}
+
+static gboolean ofono_running = FALSE;
+
+static guint modem_added_watch;
+static guint modem_removed_watch;
+static guint modem_changed_watch;
+
+static void ofono_connect(DBusConnection *conn, void *user_data)
+{
+       g_print("starting telephony interface\n");
+
+       ofono_running = TRUE;
+
+       modem_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, destroy_modem);
+
+       modem_added_watch = g_dbus_add_signal_watch(conn, NULL, NULL,
+                               OFONO_MANAGER_INTERFACE, "ModemAdded",
+                                               modem_added, NULL, NULL);
+       modem_removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL,
+                               OFONO_MANAGER_INTERFACE, "ModemRemoved",
+                                               modem_removed, NULL, NULL);
+       modem_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL,
+                               OFONO_MODEM_INTERFACE, "PropertyChanged",
+                                               modem_changed, NULL, NULL);
+
+       get_modems(conn);
+}
+
+static void ofono_disconnect(DBusConnection *conn, void *user_data)
+{
+       g_print("stopping telephony interface\n");
+
+       ofono_running = FALSE;
+
+       g_dbus_remove_watch(conn, modem_added_watch);
+       modem_added_watch = 0;
+       g_dbus_remove_watch(conn, modem_removed_watch);
+       modem_removed_watch = 0;
+       g_dbus_remove_watch(conn, modem_changed_watch);
+       modem_changed_watch = 0;
+
+       g_hash_table_destroy(modem_list);
+       modem_list = NULL;
+}
+
+static GMainLoop *main_loop = NULL;
+
+static volatile sig_atomic_t __terminated = 0;
+
+static void sig_term(int sig)
+{
+       if (__terminated > 0)
+               return;
+
+       __terminated = 1;
+
+       g_print("Terminating\n");
+
+       g_main_loop_quit(main_loop);
+}
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+       g_printerr("D-Bus disconnect\n");
+
+       g_main_loop_quit(main_loop);
+}
+
+static gboolean option_version = FALSE;
+
+static GOptionEntry options[] = {
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { NULL },
+};
+
+int main(int argc, char **argv)
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       DBusConnection *conn;
+       DBusError err;
+       guint watch;
+       struct sigaction sa;
+
+#ifdef NEED_THREADS
+       if (g_thread_supported() == FALSE)
+               g_thread_init(NULL);
+#endif
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               printf("%s\n", VERSION);
+               exit(0);
+       }
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+#ifdef NEED_THREADS
+       if (dbus_threads_init_default() == FALSE) {
+               fprintf(stderr, "Can't init usage of threads\n");
+               exit(1);
+       }
+#endif
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't register with system bus\n");
+               exit(1);
+       }
+
+       g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       watch = g_dbus_add_service_watch(conn, OFONO_SERVICE,
+                               ofono_connect, ofono_disconnect, NULL, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_dbus_remove_watch(conn, watch);
+
+       if (ofono_running == TRUE)
+               ofono_disconnect(conn, NULL);
+
+       dbus_connection_unref(conn);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/tools/lookup-apn.c b/tools/lookup-apn.c
new file mode 100644 (file)
index 0000000..884b32a
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/modem.h>
+#include <ofono/gprs-provision.h>
+
+#include "plugins/mbpi.h"
+
+static void lookup_apn(const char *match_mcc, const char *match_mnc,
+                                               gboolean allow_duplicates)
+{
+       GSList *l;
+       GSList *apns;
+       GError *error = NULL;
+
+       g_print("Searching for info for network: %s%s\n", match_mcc, match_mnc);
+
+       apns = mbpi_lookup_apn(match_mcc, match_mnc, allow_duplicates, &error);
+
+       if (apns == NULL) {
+               if (error != NULL) {
+                       g_printerr("Lookup failed: %s\n", error->message);
+                       g_error_free(error);
+               }
+
+               return;
+       }
+
+       for (l = apns; l; l = l->next) {
+               struct ofono_gprs_provision_data *ap = l->data;
+
+               g_print("\n");
+               g_print("Name: %s\n", ap->name);
+               g_print("APN: %s\n", ap->apn);
+               g_print("Type: %s\n", mbpi_ap_type(ap->type));
+               g_print("Username: %s\n", ap->username);
+               g_print("Password: %s\n", ap->password);
+
+               mbpi_ap_free(ap);
+       }
+
+       g_slist_free(apns);
+}
+
+static gboolean option_version = FALSE;
+static gboolean option_duplicates = FALSE;
+
+static GOptionEntry options[] = {
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { "allow-duplicates", 0, 0, G_OPTION_ARG_NONE, &option_duplicates,
+                               "Allow duplicate access point types" },
+       { NULL },
+};
+
+int main(int argc, char **argv)
+{
+       GOptionContext *context;
+       GError *error = NULL;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               g_print("%s\n", VERSION);
+               exit(0);
+       }
+
+       if (argc < 2) {
+               g_printerr("Missing parameters\n");
+               exit(1);
+       }
+
+       lookup_apn(argv[1], argv[2], option_duplicates);
+
+       return 0;
+}
diff --git a/tools/lookup-provider-name.c b/tools/lookup-provider-name.c
new file mode 100644 (file)
index 0000000..596b6c1
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/modem.h>
+#include <ofono/gprs-provision.h>
+
+#include "plugins/mbpi.h"
+
+static void lookup_cdma_provider_name(const char *match_sid)
+{
+       GError *error = NULL;
+       char *name;
+
+       g_print("Searching for serving network name with SID: %s\n", match_sid);
+
+       name = mbpi_lookup_cdma_provider_name(match_sid, &error);
+
+       if (name == NULL) {
+               if (error != NULL) {
+                       g_printerr("Lookup failed: %s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("Not found\n");
+
+               return;
+       }
+
+       g_print("CDMA provider name: %s\n", name);
+
+       g_free(name);
+}
+
+static gboolean option_version = FALSE;
+
+static GOptionEntry options[] = {
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { NULL },
+};
+
+int main(int argc, char **argv)
+{
+       GOptionContext *context;
+       GError *error = NULL;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               g_print("%s\n", VERSION);
+               exit(0);
+       }
+
+       if (argc < 1) {
+               g_printerr("Missing parameters\n");
+               exit(1);
+       }
+
+       lookup_cdma_provider_name(argv[1]);
+
+       return 0;
+}
diff --git a/unit/test-caif.c b/unit/test-caif.c
new file mode 100644 (file)
index 0000000..49487d3
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include <gatchat.h>
+
+#include <drivers/stemodem/caif_socket.h>
+#include <drivers/stemodem/if_caif.h>
+
+static GMainLoop *mainloop;
+
+static int do_open(void)
+{
+       int fd;
+
+       fd = open("/dev/chnlat11", O_RDWR);
+       if (fd < 0) {
+               g_printerr("Open of chnlat11 failed (%d)\n", errno);
+               return -EIO;
+       }
+
+       return fd;
+}
+
+static int do_connect(void)
+{
+       struct sockaddr_caif addr;
+       int sk, err;
+
+       /* Create a CAIF socket for AT Service */
+       sk = socket(AF_CAIF, SOCK_SEQPACKET, CAIFPROTO_AT);
+       if (sk < 0) {
+               g_printerr("CAIF socket creation failed (%d)\n", errno);
+               return -EIO;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.family = AF_CAIF;
+       addr.u.at.type = CAIF_ATTYPE_PLAIN;
+
+       /* Connect to the AT Service at the modem */
+       err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0) {
+               g_printerr("CAIF socket connect failed (%d)\n", errno);
+               close(sk);
+               return err;
+       }
+
+       return sk;
+}
+
+static void caif_debug(const char *str, void *data)
+{
+       g_print("%s\n", str);
+}
+
+static void caif_init(gboolean ok, GAtResult *result, gpointer data)
+{
+       GAtChat *chat = data;
+
+       g_print("caif_init: %d\n", ok);
+
+       if (ok == FALSE) {
+               g_at_chat_unref(chat);
+               g_main_loop_quit(mainloop);
+               return;
+       }
+
+       g_at_chat_unref(chat);
+       g_main_loop_quit(mainloop);
+}
+
+static void test_connect(gboolean use_socket)
+{
+       GIOChannel *io;
+       GAtChat *chat;
+       GAtSyntax *syntax;
+       int fd;
+
+       if (use_socket == TRUE)
+               fd = do_connect();
+       else
+               fd = do_open();
+
+       if (fd < 0)
+               return;
+
+       io = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new_blocking(io, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(io);
+
+       if (chat == NULL) {
+               g_printerr("Chat creation failed\n");
+               return;
+       }
+
+       g_at_chat_set_debug(chat, caif_debug, NULL);
+       g_at_chat_send(chat, "ATE0 +CMEE=1", NULL, caif_init, chat, NULL);
+
+       mainloop = g_main_loop_new(NULL, FALSE);
+
+       g_main_loop_run(mainloop);
+       g_main_loop_unref(mainloop);
+}
+
+static void test_basic(void)
+{
+       if (g_test_trap_fork(60 * 1000 * 1000, 0) == TRUE) {
+               test_connect(TRUE);
+               exit(0);
+       }
+
+       g_test_trap_assert_passed();
+       //g_test_trap_assert_stderr("failed");
+}
+
+static void test_chnlat(void)
+{
+       if (g_test_trap_fork(60 * 1000 * 1000, 0) == TRUE) {
+               test_connect(FALSE);
+               exit(0);
+       }
+
+       g_test_trap_assert_passed();
+       //g_test_trap_assert_stderr("failed");
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/testcaif/basic", test_basic);
+       g_test_add_func("/testcaif/chnlat", test_chnlat);
+
+       return g_test_run();
+}
diff --git a/unit/test-cdmasms.c b/unit/test-cdmasms.c
new file mode 100644 (file)
index 0000000..ffb3ce4
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "cdma-smsutil.h"
+
+static inline void check_text(const char *decoded, const char *expected)
+{
+       if (expected == NULL) {
+               g_assert(decoded == NULL);
+               return;
+       }
+
+       g_assert(decoded != NULL);
+       g_assert(g_str_equal(decoded, expected));
+}
+
+struct wmt_deliver_test {
+       const guint8 *tpdu;
+       guint8 tpdu_len;
+       const char *text;
+       const char *oaddr;
+};
+
+guint8 wmt_deliver_1[] = { 0x00, 0x00, 0x02, 0x10, 0x02, 0x02, 0x05, 0x01,
+                               0xC4, 0x8D, 0x15, 0x9C, 0x08, 0x0D, 0x00,
+                               0x03, 0x1B, 0xEE, 0xF0, 0x01, 0x06, 0x10,
+                               0x2C, 0x8C, 0xBB, 0x36, 0x6F };
+
+guint8 wmt_deliver_2[] = { 0x00, 0x00, 0x02, 0x10, 0x02, 0x02, 0x07, 0x02,
+                               0xA1, 0x62, 0x51, 0x55, 0xA6, 0x40, 0x08,
+                               0x18, 0x00, 0x03, 0x10, 0x00, 0x40, 0x01,
+                               0x06, 0x10, 0x25, 0x4C, 0xBC, 0xFA, 0x00,
+                               0x03, 0x06, 0x03, 0x08, 0x20, 0x13, 0x43,
+                               0x12, 0x0D, 0x01, 0x01 };
+
+static struct wmt_deliver_test wmt_deliver_data_1 = {
+       .tpdu = wmt_deliver_1,
+       .tpdu_len = sizeof(wmt_deliver_1),
+       .text = "Hello",
+       .oaddr = "1234567"
+};
+
+static struct wmt_deliver_test wmt_deliver_data_2 = {
+       .tpdu = wmt_deliver_2,
+       .tpdu_len = sizeof(wmt_deliver_2),
+       .text = "Test",
+       .oaddr = "8589455699"
+};
+
+static void test_wmt_deliver(gconstpointer data)
+{
+       const struct wmt_deliver_test *test = data;
+       gboolean ret;
+       struct cdma_sms s;
+       const char *addr;
+       char *message;
+
+       memset(&s, 0, sizeof(struct cdma_sms));
+
+       ret = cdma_sms_decode(test->tpdu, test->tpdu_len, &s);
+
+       g_assert(ret == TRUE);
+
+       g_assert(s.type == CDMA_SMS_TP_MSG_TYPE_P2P);
+
+       g_assert(s.p2p_msg.teleservice_id == CDMA_SMS_TELESERVICE_ID_WMT);
+
+       addr = cdma_sms_address_to_string(&s.p2p_msg.oaddr);
+       check_text(addr, test->oaddr);
+
+       message = cdma_sms_decode_text(&s.p2p_msg.bd.wmt_deliver.ud);
+       check_text(message, test->text);
+
+       g_free(message);
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_data_func("/test-cdmasms/WMT DELIVER 1",
+                       &wmt_deliver_data_1, test_wmt_deliver);
+
+       g_test_add_data_func("/test-cdmasms/WMT DELIVER 2",
+                       &wmt_deliver_data_2, test_wmt_deliver);
+
+       return g_test_run();
+}
diff --git a/unit/test-common.c b/unit/test-common.c
new file mode 100644 (file)
index 0000000..63355ea
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <glib.h>
+
+#include <ofono/types.h>
+
+#include "common.h"
+
+static const char *invalid_strings[] = {
+       "33",
+       "*",
+       "**",
+       "***",
+       "*****",
+       "******",
+       "#",
+       "##",
+       "###",
+       "####",
+       "#####",
+       "*#",
+       "**#",
+       "****#",
+       "*****#",
+       "**#",
+       "*#",
+       "##",
+       "*04*98*0000*00000*00000#",
+       NULL
+};
+
+static void test_invalid(void)
+{
+       char *sc;
+       char *sia;
+       char *sib;
+       char *sic;
+       char *sid;
+       char *dn;
+       int type;
+
+       char *str;
+       int i;
+       gboolean ret;
+
+       for (i = 0; invalid_strings[i]; i++) {
+               if (g_test_verbose())
+                       g_print("%s...\n", invalid_strings[i]);
+
+               str = strdup(invalid_strings[i]);
+
+               ret = parse_ss_control_string(str, &type, &sc,
+                                               &sia, &sib, &sic, &sid, &dn);
+               if (ret == TRUE && strlen(sid))
+                       ret = FALSE;
+
+               g_assert(ret == FALSE);
+
+               free(str);
+       }
+}
+
+static const char *valid_strings[] = {
+       "*31#",
+       "*31#+55555",
+       "#31#",
+       "#31#+55555",
+       "*21*+55555*10*20#",
+       "*21*+55555*10#",
+       "*21**20#",
+       "*21*+55555#",
+       "*21**10*20#",
+       "*21**10#",
+       "*21***20#",
+       "*21#",
+       "**21#",
+       "*#21#",
+       "#21#",
+       "##21#",
+       NULL
+};
+
+static void test_valid(void)
+{
+       char *sc;
+       char *sia;
+       char *sib;
+       char *sic;
+       char *sid;
+       char *dn;
+       int type;
+       gboolean ret;
+
+       char *str;
+       int i;
+
+       for (i = 0; valid_strings[i]; i++) {
+               if (g_test_verbose())
+                       g_print("%s...", valid_strings[i]);
+
+               str = strdup(valid_strings[i]);
+
+               ret = parse_ss_control_string(str, &type, &sc,
+                                               &sia, &sib, &sic, &sid, &dn);
+               if (strlen(sid))
+                       ret = FALSE;
+
+               g_assert(ret == TRUE);
+
+               if (g_test_verbose())
+                       g_print("parsed as: %d, %s, %s, %s, %s, %s\n",
+                                       type, sc, sia, sib, sic, dn);
+
+               free(str);
+       }
+}
+
+static const char *valid_apns[] = {
+       "wap.cingular",
+       "vodafone.co.uk",
+       "vodafone.com",
+       NULL
+};
+
+static const char *invalid_apns[] = {
+       ".",
+       "..",
+       "f..f",
+       "foo.bar.#",
+       "",
+       NULL
+};
+
+static void test_apn(void)
+{
+       int i;
+       gboolean res;
+
+       for (i = 0; valid_apns[i]; i++) {
+               if (g_test_verbose())
+                       g_print("Test Valid:%s\n", valid_apns[i]);
+
+               res = is_valid_apn(valid_apns[i]);
+
+               g_assert(res == TRUE);
+       }
+
+       for (i = 0; invalid_apns[i]; i++) {
+               if (g_test_verbose())
+                       g_print("Test Invalid:%s\n", invalid_apns[i]);
+
+               res = is_valid_apn(invalid_apns[i]);
+
+               g_assert(res == FALSE);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/testutil/Invalid", test_invalid);
+       g_test_add_func("/testutil/Valid", test_valid);
+       g_test_add_func("/testutil/APN", test_apn);
+
+       return g_test_run();
+}
diff --git a/unit/test-idmap.c b/unit/test-idmap.c
new file mode 100644 (file)
index 0000000..b072933
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "idmap.h"
+
+static void test_alloc(void)
+{
+       struct idmap *idmap;
+       unsigned int bit;
+
+       idmap = idmap_new(2);
+
+       g_assert(idmap);
+
+       bit = idmap_alloc(idmap);
+       g_assert(bit == 1);
+
+       bit = idmap_alloc(idmap);
+       g_assert(bit == 2);
+
+       bit = idmap_alloc(idmap);
+       g_assert(bit == 3);
+
+       idmap_put(idmap, 3);
+       bit = idmap_alloc(idmap);
+       g_assert(bit == 3);
+
+       idmap_put(idmap, 0);
+       bit = idmap_alloc(idmap);
+       g_assert(bit == 3);
+
+       idmap_put(idmap, 1);
+       bit = idmap_alloc(idmap);
+       g_assert(bit == 1);
+
+       idmap_put(idmap, 1);
+       idmap_put(idmap, 2);
+       bit = idmap_alloc(idmap);
+       g_assert(bit == 1);
+
+       idmap_free(idmap);
+}
+
+static void test_alloc_next(void)
+{
+       struct idmap *idmap;
+       unsigned int bit;
+
+       idmap = idmap_new(256);
+
+       g_assert(idmap);
+
+       bit = idmap_alloc_next(idmap, 255);
+       g_assert(bit == 256);
+
+       bit = idmap_alloc_next(idmap, 255);
+       g_assert(bit == 1);
+
+       bit = idmap_alloc_next(idmap, 1);
+       g_assert(bit == 2);
+
+       idmap_free(idmap);
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/testidmap/alloc", test_alloc);
+       g_test_add_func("/testidmap/alloc_next", test_alloc_next);
+
+       return g_test_run();
+}
diff --git a/unit/test-mux.c b/unit/test-mux.c
new file mode 100644 (file)
index 0000000..4b7ed4b
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "gatmux.h"
+#include "gsm0710.h"
+
+static int do_connect(const char *address, unsigned short port)
+{
+       struct sockaddr_in addr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0)
+               return sk;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(address);
+       addr.sin_port = htons(port);
+
+       err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0) {
+               close(sk);
+               return err;
+       }
+
+       return sk;
+}
+
+static GMainLoop *mainloop;
+static GAtMux *mux;
+
+static gboolean cleanup_callback(gpointer data)
+{
+       g_at_mux_unref(mux);
+
+       g_main_loop_quit(mainloop);
+
+       return FALSE;
+}
+
+static gboolean chat_cleanup(gpointer data)
+{
+       GAtChat *chat = data;
+
+       g_at_chat_unref(chat);
+
+       return FALSE;
+}
+
+static void chat_callback(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       g_print("chat: callback [ok %d]\n", ok);
+
+       g_print("%s\n", g_at_result_final_response(result));
+
+       g_idle_add(chat_cleanup, user_data);
+}
+
+static void mux_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (char *) data, str);
+}
+
+static void mux_setup(GAtMux *m, gpointer data)
+{
+       GAtChat *chat = data;
+       GIOChannel *io;
+       GAtSyntax *syntax;
+
+       mux = m;
+
+       g_print("mux_setup: %p\n", mux);
+
+       if (mux == NULL) {
+               g_at_chat_unref(chat);
+               g_main_loop_quit(mainloop);
+               return;
+       }
+
+       g_at_mux_start(mux);
+
+       io = g_at_mux_create_channel(mux);
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       g_at_chat_set_debug(chat, mux_debug, "CHAT1");
+       g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000);
+       g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL);
+
+       io = g_at_mux_create_channel(mux);
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       g_at_chat_set_debug(chat, mux_debug, "CHAT2");
+       g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000);
+       g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL);
+
+       io = g_at_mux_create_channel(mux);
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       g_at_chat_set_debug(chat, mux_debug, "CHAT3");
+       g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000);
+       g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL);
+
+       io = g_at_mux_create_channel(mux);
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(io);
+
+       g_at_chat_set_debug(chat, mux_debug, "CHAT4");
+       g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000);
+       g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL);
+
+       g_timeout_add_seconds(7, cleanup_callback, NULL);
+}
+
+static void mux_init(gboolean ok, GAtResult *result, gpointer data)
+{
+       GAtChat *chat = data;
+
+       g_print("mux_init: %d\n", ok);
+
+       if (ok == FALSE) {
+               g_at_chat_unref(chat);
+               g_main_loop_quit(mainloop);
+               return;
+       }
+
+       g_at_mux_setup_gsm0710(chat, mux_setup, chat, NULL);
+}
+
+static void test_mux(void)
+{
+       GIOChannel *io;
+       GAtChat *chat;
+       GAtSyntax *syntax;
+       int sk;
+
+       sk = do_connect("192.168.0.202", 2000);
+       if (sk < 0) {
+               g_printerr("connect failed\n");
+               return;
+       }
+
+       mux = NULL;
+       io = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       syntax = g_at_syntax_new_gsm_permissive();
+       chat = g_at_chat_new(io, syntax);
+       g_at_syntax_unref(syntax);
+
+       g_io_channel_unref(io);
+
+       if (chat == NULL) {
+               g_printerr("Chat creation failed\n");
+               return;
+       }
+
+       g_at_chat_set_debug(chat, mux_debug, "MUX");
+       g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000);
+       g_at_chat_send(chat, "ATE0", NULL, mux_init, chat, NULL);
+
+       mainloop = g_main_loop_new(NULL, FALSE);
+
+       g_main_loop_run(mainloop);
+       g_main_loop_unref(mainloop);
+}
+
+static void test_basic(void)
+{
+       if (g_test_trap_fork(60 * 1000 * 1000, 0) == TRUE) {
+               test_mux();
+               exit(0);
+       }
+
+       g_test_trap_assert_passed();
+       //g_test_trap_assert_stderr("failed");
+}
+
+/* DLC 1, Open Channel */
+static const guint8 basic_open[] = { 0xF9, 0x07, 0x3F, 0x01, 0xDE, 0xF9 };
+
+/* DLC 1, Close Channel */
+static char const basic_close[] = { 0xF9, 0x07, 0x53, 0x01, 0x3F, 0xF9 };
+
+/* DLC 1, Data */
+static const guint8 basic_data[] = { 0x12, 0x34, 0x56 };
+static const guint8 basic_data_result[] =
+       { 0xF9, 0x07, 0xEF, 0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9 };
+
+/* DLC 1, Long Data */
+static const guint8 basic_long_frame[] =
+{      0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+       0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0,
+};
+
+static const guint8 basic_long_frame_result[] =
+{      0xF9, 0x07, 0xEF, 0x10, 0x01, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
+       0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x88, 0xF9
+};
+
+static void test_fill_basic(void)
+{
+       guint8 control_frame[6];
+       guint8 data_frame[128];
+       guint8 long_frame[256];
+       int s;
+
+       s = gsm0710_basic_fill_frame(control_frame, 1, GSM0710_OPEN_CHANNEL,
+                                       NULL, 0);
+       g_assert(s == sizeof(basic_open));
+       g_assert(memcmp(basic_open, control_frame, s) == 0);
+
+       s = gsm0710_basic_fill_frame(control_frame, 1, GSM0710_CLOSE_CHANNEL,
+                                       NULL, 0);
+       g_assert(s == sizeof(basic_close));
+       g_assert(memcmp(basic_close, control_frame, s) == 0);
+
+       s = gsm0710_basic_fill_frame(data_frame, 1, GSM0710_DATA,
+                                       basic_data, sizeof(basic_data));
+       g_assert(s == sizeof(basic_data_result));
+       g_assert(memcmp(basic_data_result, data_frame, s) == 0);
+
+       s = gsm0710_basic_fill_frame(long_frame, 1, GSM0710_DATA,
+                                       basic_long_frame,
+                                       sizeof(basic_long_frame));
+       g_assert(s == sizeof(basic_long_frame_result));
+       g_assert(memcmp(basic_long_frame_result, long_frame, s) == 0);
+}
+
+/* DLC 1, Open Channel */
+static const guint8 advanced_open[] = { 0x7E, 0x07, 0x3F, 0x89, 0x7E };
+
+/* DLC 1, Close Channel */
+static const guint8 advanced_close[] = { 0x7E, 0x07, 0x53, 0xC8, 0x7E };
+
+/* DLC 1, Data */
+static const guint8 advanced_data[] = { 0x12, 0x34, 0x56 };
+static const guint8 advanced_data_result[] =
+       { 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E };
+
+/* DLC 1, Quoted data */
+static const guint8 advanced_quoted_data[] =
+       { 0x12, 0x34, 0x56, 0x7E, 0x78, 0x7D };
+static const guint8 advanced_quoted_data_result[] =
+       { 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x7D, 0x5E, 0x78,
+               0x7D, 0x5D, 0x05, 0x7E };
+
+static void test_fill_advanced(void)
+{
+       guint8 control_frame[8];
+       guint8 data_frame[128];
+       int s;
+
+       s = gsm0710_advanced_fill_frame(control_frame, 1, GSM0710_OPEN_CHANNEL,
+                                       NULL, 0);
+       g_assert(s == sizeof(advanced_open));
+       g_assert(memcmp(advanced_open, control_frame, s) == 0);
+
+       s = gsm0710_advanced_fill_frame(control_frame, 1, GSM0710_CLOSE_CHANNEL,
+                                       NULL, 0);
+       g_assert(s == sizeof(advanced_close));
+       g_assert(memcmp(advanced_close, control_frame, s) == 0);
+
+       s = gsm0710_advanced_fill_frame(data_frame, 1, GSM0710_DATA,
+                                       advanced_data, sizeof(advanced_data));
+       g_assert(s == sizeof(advanced_data_result));
+       g_assert(memcmp(advanced_data_result, data_frame, s) == 0);
+
+       s = gsm0710_advanced_fill_frame(data_frame, 1, GSM0710_DATA,
+                                       advanced_quoted_data,
+                                       sizeof(advanced_quoted_data));
+       g_assert(s == sizeof(advanced_quoted_data_result));
+       g_assert(memcmp(advanced_quoted_data_result, data_frame, s) == 0);
+}
+
+static guint8 basic_input[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x07, 0xEF,
+       0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9, 0x07, 0xEF, 0x07, 0x12, 0x34, 0x56,
+       0xD3, 0xF9 };
+
+static guint8 basic_input2[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x07, 0xEF,
+       0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9, 0xF9, 0x07, 0xEF, 0x07, 0x12,
+       0x34, 0x56, 0xD3, 0xF9 };
+
+static int basic_garbage_size = 4;
+static int basic_frame_size = 7;
+
+static const guint8 basic_output[] = { 0x12, 0x34, 0x56 };
+
+static void test_extract_basic(void)
+{
+       int total = 0;
+       int nread;
+       guint8 dlc;
+       guint8 ctrl;
+       guint8 *frame;
+       int frame_size;
+
+       frame = NULL;
+       frame_size = 0;
+
+       nread = gsm0710_basic_extract_frame(basic_input + total,
+                                               basic_garbage_size, &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(frame == NULL);
+       g_assert(frame_size == 0);
+
+       total += nread;
+
+       /* Try to read with just the open flag */
+       nread = gsm0710_basic_extract_frame(basic_input + total,
+                                               basic_frame_size + 1,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(nread == 0);
+       g_assert(frame == NULL);
+
+       /* Now read with the close flag as well */
+       nread = gsm0710_basic_extract_frame(basic_input + total,
+                                               basic_frame_size + 2,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       /* Extracted the open flag + frame */
+       g_assert(nread == basic_frame_size + 1);
+       g_assert(frame_size == sizeof(basic_output));
+       g_assert(memcmp(basic_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       nread = gsm0710_basic_extract_frame(basic_input + total,
+                                               sizeof(basic_input) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(nread == (int)(sizeof(basic_input) - total - 1));
+       g_assert(frame_size == sizeof(basic_output));
+       g_assert(memcmp(basic_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       nread = gsm0710_basic_extract_frame(basic_input + total,
+                                               sizeof(basic_input) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+       g_assert(nread == 0);
+
+       total = 0;
+
+       nread = gsm0710_basic_extract_frame(basic_input2 + total,
+                                               sizeof(basic_input2) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(nread == basic_garbage_size + basic_frame_size + 1);
+       g_assert(frame_size == sizeof(basic_output));
+       g_assert(memcmp(basic_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       nread = gsm0710_basic_extract_frame(basic_input2 + total,
+                                               sizeof(basic_input2) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(frame_size == sizeof(basic_output));
+       g_assert(memcmp(basic_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       g_assert(total == sizeof(basic_input2) - 1);
+}
+
+static guint8 advanced_input[] =
+       { 0xFF, 0xFF, 0xFF, 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E,
+               0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E };
+
+static guint8 advanced_input2[] =
+       { 0xFF, 0xFF, 0xFF, 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E,
+               0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E };
+
+static int advanced_garbage_size = 3;
+static int advanced_frame_size = 6;
+
+static const guint8 advanced_output[] = { 0x12, 0x34, 0x56 };
+
+static void test_extract_advanced(void)
+{
+       int total = 0;
+       int nread;
+       guint8 dlc;
+       guint8 ctrl;
+       guint8 *frame;
+       int frame_size;
+
+       frame = NULL;
+       frame_size = 0;
+
+       nread = gsm0710_advanced_extract_frame(advanced_input + total,
+                                               advanced_garbage_size,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(frame == NULL);
+       g_assert(frame_size == 0);
+
+       total += nread;
+
+       /* Try to read with just the open flag */
+       nread = gsm0710_advanced_extract_frame(advanced_input + total,
+                                               advanced_frame_size + 1,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(nread == 0);
+       g_assert(frame == NULL);
+
+       /* Now read with the close flag as well */
+       nread = gsm0710_advanced_extract_frame(advanced_input + total,
+                                               advanced_frame_size + 2,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       /* Extracted the open flag + frame */
+       g_assert(nread == advanced_frame_size + 1);
+       g_assert(frame_size == sizeof(advanced_output));
+       g_assert(memcmp(advanced_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       nread = gsm0710_advanced_extract_frame(advanced_input + total,
+                                               sizeof(advanced_input) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(nread == (int)(sizeof(advanced_input) - total - 1));
+       g_assert(frame_size == sizeof(advanced_output));
+       g_assert(memcmp(advanced_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       nread = gsm0710_advanced_extract_frame(advanced_input + total,
+                                               sizeof(advanced_input) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+       g_assert(nread == 0);
+
+       total = 0;
+
+       nread = gsm0710_advanced_extract_frame(advanced_input2 + total,
+                                               sizeof(advanced_input2) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(nread == advanced_garbage_size + advanced_frame_size + 1);
+       g_assert(frame_size == sizeof(advanced_output));
+       g_assert(memcmp(advanced_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       nread = gsm0710_advanced_extract_frame(advanced_input2 + total,
+                                               sizeof(advanced_input2) - total,
+                                               &dlc, &ctrl,
+                                               &frame, &frame_size);
+
+       g_assert(frame_size == sizeof(advanced_output));
+       g_assert(memcmp(advanced_output, frame, frame_size) == 0);
+
+       total += nread;
+
+       g_assert(total == sizeof(advanced_input2) - 1);
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/testmux/fill_basic", test_fill_basic);
+       g_test_add_func("/testmux/fill_advanced", test_fill_advanced);
+       g_test_add_func("/testmux/extract_basic", test_extract_basic);
+       g_test_add_func("/testmux/extract_advanced", test_extract_advanced);
+       g_test_add_func("/testmux/basic", test_basic);
+
+       return g_test_run();
+}
diff --git a/unit/test-simutil.c b/unit/test-simutil.c
new file mode 100644 (file)
index 0000000..b61eba8
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <glib.h>
+
+#include <ofono/types.h>
+
+#include "simutil.h"
+#include "util.h"
+
+/* Taken from 51.011 Appendix K.2 */
+const unsigned char valid_mms_params[] = {
+       0xAB, 0x81, 0x88, 0x80, 0x01, 0x01, 0x81, 0x17, 0x68, 0x74, 0x74, 0x70,
+       0x3A, 0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x2D, 0x6F, 0x70, 0x65, 0x72, 0x61,
+       0x74, 0x6F, 0x72, 0x2E, 0x63, 0x6F, 0x6D, 0x82, 0x32, 0x10, 0xAA, 0x08,
+       0x2B, 0x34, 0x39, 0x35, 0x33, 0x34, 0x31, 0x39, 0x30, 0x36, 0x00, 0x09,
+       0x87, 0x25, 0xC5, 0x0A, 0x90, 0x0C, 0x9A, 0x0D, 0x64, 0x75, 0x6D, 0x6D,
+       0x79, 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x0E, 0x64, 0x75, 0x6D, 0x6D,
+       0x79, 0x5F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x00, 0x83,
+       0x36, 0x20, 0x31, 0x37, 0x30, 0x2E, 0x31, 0x38, 0x37, 0x2E, 0x35, 0x31,
+       0x2E, 0x33, 0x00, 0x21, 0x85, 0x23, 0x39, 0x32, 0x30, 0x33, 0x00, 0x24,
+       0xCB, 0x19, 0x9C, 0x1A, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x5F, 0x6E, 0x61,
+       0x6D, 0x65, 0x00, 0x1B, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x5F, 0x70, 0x61,
+       0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x00 };
+
+static void test_buffer(const unsigned char *buf, size_t size)
+{
+       struct ber_tlv_iter iter;
+       struct ber_tlv_iter cont;
+
+       ber_tlv_iter_init(&iter, buf, size);
+
+       g_assert(ber_tlv_iter_next(&iter) == TRUE);
+       g_assert(ber_tlv_iter_get_short_tag(&iter) == 0xAB);
+
+       ber_tlv_iter_recurse(&iter, &cont);
+
+       g_assert(ber_tlv_iter_next(&cont) == TRUE);
+       g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x80);
+       g_assert(ber_tlv_iter_get_length(&cont) == 1);
+
+       g_assert(ber_tlv_iter_next(&cont) == TRUE);
+       g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x81);
+       g_assert(ber_tlv_iter_get_length(&cont) == 23);
+
+       g_assert(ber_tlv_iter_next(&cont) == TRUE);
+       g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x82);
+       g_assert(ber_tlv_iter_get_length(&cont) == 50);
+
+       g_assert(ber_tlv_iter_next(&cont) == TRUE);
+       g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x83);
+       g_assert(ber_tlv_iter_get_length(&cont) == 54);
+
+       g_assert(ber_tlv_iter_next(&cont) == FALSE);
+       g_assert(ber_tlv_iter_next(&iter) == FALSE);
+}
+
+static void test_ber_tlv_iter(void)
+{
+       test_buffer(valid_mms_params, sizeof(valid_mms_params));
+}
+
+static void test_ber_tlv_builder_mms(void)
+{
+       struct ber_tlv_iter top_iter, nested_iter;
+       struct ber_tlv_builder top_builder, nested_builder;
+       unsigned char buf[512], *pdu;
+       unsigned int pdulen;
+
+       ber_tlv_iter_init(&top_iter, valid_mms_params,
+                               sizeof(valid_mms_params));
+       g_assert(ber_tlv_builder_init(&top_builder, buf, sizeof(buf)));
+
+       /* Copy the structure */
+       while (ber_tlv_iter_next(&top_iter) == TRUE) {
+               g_assert(ber_tlv_builder_next(&top_builder,
+                                       ber_tlv_iter_get_class(&top_iter),
+                                       ber_tlv_iter_get_encoding(&top_iter),
+                                       ber_tlv_iter_get_tag(&top_iter)));
+
+               ber_tlv_iter_recurse(&top_iter, &nested_iter);
+               g_assert(ber_tlv_builder_recurse(&top_builder,
+                                                       &nested_builder));
+
+               while (ber_tlv_iter_next(&nested_iter) == TRUE) {
+                       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       ber_tlv_iter_get_class(&nested_iter),
+                                       ber_tlv_iter_get_encoding(&nested_iter),
+                                       ber_tlv_iter_get_tag(&nested_iter)));
+
+                       g_assert(ber_tlv_builder_set_length(&nested_builder,
+                                       ber_tlv_iter_get_length(&nested_iter)));
+                       memcpy(ber_tlv_builder_get_data(&nested_builder),
+                                       ber_tlv_iter_get_data(&nested_iter),
+                                       ber_tlv_iter_get_length(&nested_iter));
+               }
+
+               ber_tlv_builder_optimize(&nested_builder, NULL, NULL);
+       }
+
+       ber_tlv_builder_optimize(&top_builder, &pdu, &pdulen);
+
+       test_buffer(pdu, pdulen);
+}
+
+static void test_ber_tlv_builder_efpnn(void)
+{
+       struct sim_eons *eons_info;
+       unsigned char efpnn0[64], efpnn1[64];
+       struct ber_tlv_builder builder;
+
+       g_assert(ber_tlv_builder_init(&builder, efpnn0, sizeof(efpnn0)));
+       g_assert(ber_tlv_builder_next(&builder,
+                                       BER_TLV_DATA_TYPE_APPLICATION,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x03));
+       g_assert(ber_tlv_builder_set_length(&builder, 10));
+       ber_tlv_builder_get_data(&builder)[0] = 0x00;
+       ber_tlv_builder_get_data(&builder)[1] = 0x54;
+       ber_tlv_builder_get_data(&builder)[2] = 0x75;
+       ber_tlv_builder_get_data(&builder)[3] = 0x78;
+       ber_tlv_builder_get_data(&builder)[4] = 0x20;
+       ber_tlv_builder_get_data(&builder)[5] = 0x43;
+       ber_tlv_builder_get_data(&builder)[6] = 0x6f;
+       ber_tlv_builder_get_data(&builder)[7] = 0x6d;
+       ber_tlv_builder_get_data(&builder)[8] = 0x6d;
+       ber_tlv_builder_get_data(&builder)[9] = 0xff;
+       ber_tlv_builder_get_data(&builder)[10] = 0xff;
+       ber_tlv_builder_optimize(&builder, NULL, NULL);
+
+       g_assert(ber_tlv_builder_init(&builder, efpnn1, sizeof(efpnn1)));
+       g_assert(ber_tlv_builder_next(&builder,
+                                       BER_TLV_DATA_TYPE_APPLICATION,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x03));
+       g_assert(ber_tlv_builder_set_length(&builder, 3));
+       ber_tlv_builder_get_data(&builder)[0] = 0x00;
+       ber_tlv_builder_get_data(&builder)[1] = 0x4c;
+       ber_tlv_builder_get_data(&builder)[2] = 0x6f;
+       ber_tlv_builder_get_data(&builder)[3] = 0x6e;
+       ber_tlv_builder_get_data(&builder)[4] = 0x67;
+       g_assert(ber_tlv_builder_next(&builder,
+                                       BER_TLV_DATA_TYPE_APPLICATION,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x05));
+       g_assert(ber_tlv_builder_set_length(&builder, 6));
+       ber_tlv_builder_get_data(&builder)[0] = 0x00;
+       ber_tlv_builder_get_data(&builder)[1] = 0x53;
+       ber_tlv_builder_get_data(&builder)[2] = 0x68;
+       ber_tlv_builder_get_data(&builder)[3] = 0x6f;
+       ber_tlv_builder_get_data(&builder)[4] = 0x72;
+       ber_tlv_builder_get_data(&builder)[5] = 0x74;
+       ber_tlv_builder_optimize(&builder, NULL, NULL);
+
+       eons_info = sim_eons_new(1);
+       sim_eons_add_pnn_record(eons_info, 1, efpnn0, sizeof(efpnn0));
+       g_assert(!sim_eons_pnn_is_empty(eons_info));
+       sim_eons_free(eons_info);
+
+       eons_info = sim_eons_new(1);
+       sim_eons_add_pnn_record(eons_info, 1, efpnn1, sizeof(efpnn1));
+       g_assert(!sim_eons_pnn_is_empty(eons_info));
+       sim_eons_free(eons_info);
+}
+
+static void test_ber_tlv_builder_3g_status(void)
+{
+       unsigned char buf[512];
+       struct ber_tlv_builder top_builder, nested_builder;
+       unsigned char *response;
+       unsigned int len;
+       int flen, rlen, str;
+       unsigned char access[3];
+       unsigned short efid;
+
+       /* Build a binary EF status response */
+       g_assert(ber_tlv_builder_init(&top_builder, buf, sizeof(buf)));
+
+       g_assert(ber_tlv_builder_next(&top_builder,
+                                       BER_TLV_DATA_TYPE_APPLICATION,
+                                       BER_TLV_DATA_ENCODING_TYPE_CONSTRUCTED,
+                                       0x02));
+       g_assert(ber_tlv_builder_recurse(&top_builder, &nested_builder));
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x02));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 2));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x41;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x21;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x03));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 2));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x2f;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x05;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x0a));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 1));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x05;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x0b));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 3));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x2f;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x06;
+       ber_tlv_builder_get_data(&nested_builder)[2] = 0x0f;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x00));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 2));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x00;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x0a;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x08));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 1));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x28;
+
+       ber_tlv_builder_optimize(&nested_builder, NULL, NULL);
+       ber_tlv_builder_optimize(&top_builder, &response, &len);
+
+       sim_parse_3g_get_response(response, len, &flen, &rlen, &str,
+                                       access, &efid);
+
+       g_assert(flen == 10);
+       g_assert(rlen == 0);
+       g_assert(str == 0);
+       g_assert(access[0] == 0x01);
+       g_assert(access[1] == 0xff);
+       g_assert(access[2] == 0x44);
+       g_assert(efid == 0x2F05);
+
+       /* Build a record-based EF status response */
+       g_assert(ber_tlv_builder_init(&top_builder, buf, sizeof(buf)));
+
+       g_assert(ber_tlv_builder_next(&top_builder,
+                                       BER_TLV_DATA_TYPE_APPLICATION,
+                                       BER_TLV_DATA_ENCODING_TYPE_CONSTRUCTED,
+                                       0x02));
+       g_assert(ber_tlv_builder_recurse(&top_builder, &nested_builder));
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x02));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 5));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x42;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x21;
+       ber_tlv_builder_get_data(&nested_builder)[2] = 0x00;
+       ber_tlv_builder_get_data(&nested_builder)[3] = 0x20;
+       ber_tlv_builder_get_data(&nested_builder)[4] = 0x04;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x03));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 2));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x6f;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x40;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x0a));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 1));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x05;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x0b));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 3));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x2f;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x06;
+       ber_tlv_builder_get_data(&nested_builder)[2] = 0x07;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x00));
+       g_assert(ber_tlv_builder_set_length(&nested_builder, 2));
+       ber_tlv_builder_get_data(&nested_builder)[0] = 0x00;
+       ber_tlv_builder_get_data(&nested_builder)[1] = 0x80;
+
+       g_assert(ber_tlv_builder_next(&nested_builder,
+                                       BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC,
+                                       BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE,
+                                       0x08));
+
+       ber_tlv_builder_optimize(&nested_builder, NULL, NULL);
+       ber_tlv_builder_optimize(&top_builder, &response, &len);
+
+       sim_parse_3g_get_response(response, len, &flen, &rlen, &str,
+                                       access, &efid);
+
+       g_assert(flen == 0x80);
+       g_assert(rlen == 0x20);
+       g_assert(str == 1);
+       g_assert(access[0] == 0x11);
+       g_assert(access[1] == 0xff);
+       g_assert(access[2] == 0x44);
+       g_assert(efid == 0x6F40);
+}
+
+const unsigned char valid_efopl[] = {
+       0x42, 0xf6, 0x1d, 0x00, 0x00, 0xff, 0xfe, 0x01,
+};
+
+const unsigned char valid_efpnn[][28] = {
+       { 0x43, 0x0a, 0x00, 0x54, 0x75, 0x78, 0x20, 0x43, 0x6f, 0x6d,
+         0x6d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, },
+       { 0x43, 0x05, 0x00, 0x4C, 0x6F, 0x6E, 0x67, 0x45, 0x06, 0x00,
+         0x53, 0x68, 0x6F, 0x72, 0x74, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }
+};
+
+static void test_eons(void)
+{
+       const struct sim_eons_operator_info *op_info;
+       struct sim_eons *eons_info;
+
+       eons_info = sim_eons_new(2);
+
+       g_assert(sim_eons_pnn_is_empty(eons_info));
+
+       sim_eons_add_pnn_record(eons_info, 1,
+                       valid_efpnn[0], sizeof(valid_efpnn[0]));
+       g_assert(!sim_eons_pnn_is_empty(eons_info));
+
+       sim_eons_add_pnn_record(eons_info, 2,
+                       valid_efpnn[1], sizeof(valid_efpnn[1]));
+       g_assert(!sim_eons_pnn_is_empty(eons_info));
+
+       sim_eons_add_opl_record(eons_info, valid_efopl, sizeof(valid_efopl));
+       sim_eons_optimize(eons_info);
+
+       op_info = sim_eons_lookup(eons_info, "246", "82");
+       g_assert(op_info == NULL);
+       op_info = sim_eons_lookup(eons_info, "246", "81");
+       g_assert(op_info);
+
+       g_assert(!strcmp(op_info->longname, "Tux Comm"));
+       g_assert(!op_info->shortname);
+       g_assert(!op_info->info);
+
+       sim_eons_free(eons_info);
+}
+
+static void test_ef_db(void)
+{
+       struct sim_ef_info *info;
+
+       info = sim_ef_db_lookup(0x6FAD);
+       g_assert(info);
+
+       info = sim_ef_db_lookup(0x6FB1);
+       g_assert(info == NULL);
+
+       info = sim_ef_db_lookup(0x2F05);
+       g_assert(info);
+
+       info = sim_ef_db_lookup(0x6FE3);
+       g_assert(info);
+}
+
+static const char *binary_ef = "62178202412183022F058A01058B032F060F8002000A"
+                               "880128";
+static const char *record_ef = "62198205422100200483026F408A01058B036F0607"
+                               "800200808800";
+
+static void test_3g_status_data(void)
+{
+       unsigned char *response;
+       long len;
+       int flen, rlen, str;
+       unsigned char access[3];
+       unsigned short efid;
+
+       response = decode_hex(binary_ef, -1, &len, 0);
+
+       sim_parse_3g_get_response(response, len, &flen, &rlen, &str,
+                                       access, &efid);
+
+       g_assert(flen == 10);
+       g_assert(rlen == 0);
+       g_assert(str == 0);
+       g_assert(access[0] == 0x01);
+       g_assert(access[1] == 0xff);
+       g_assert(access[2] == 0x44);
+       g_assert(efid == 0x2F05);
+
+       g_free(response);
+
+       response = decode_hex(record_ef, -1, &len, 0);
+
+       sim_parse_3g_get_response(response, len, &flen, &rlen, &str,
+                                       access, &efid);
+
+       g_assert(flen == 0x80);
+       g_assert(rlen == 0x20);
+       g_assert(str == 1);
+       g_assert(access[0] == 0x11);
+       g_assert(access[1] == 0xff);
+       g_assert(access[2] == 0x44);
+       g_assert(efid == 0x6F40);
+
+       g_free(response);
+}
+
+static char *at_cuad_response = "611B4F10A0000000871002FFFFFFFF8905080000"
+       "FFFFFFFFFFFFFFFFFFFFFFFFFF611F4F0CA000000063504B43532D"
+       "313550094D49445066696C657351043F007F80";
+
+static void test_application_entry_decode(void) {
+       unsigned char *ef_dir;
+       long len;
+       GSList *entries;
+       struct sim_app_record *app[2];
+
+       ef_dir = decode_hex(at_cuad_response, -1, &len, 0);
+       entries = sim_parse_app_template_entries(ef_dir, len);
+
+       g_assert(g_slist_length(entries) == 2);
+
+       app[0] = entries->next->data;
+       app[1] = entries->data;
+
+       g_assert(app[0]->aid_len == 0x10);
+       g_assert(!memcmp(app[0]->aid, &ef_dir[4], 0x10));
+       g_assert(app[0]->label == NULL);
+
+       g_assert(app[1]->aid_len == 0x0c);
+       g_assert(!memcmp(app[1]->aid, &ef_dir[37], 0x0c));
+       g_assert(app[1]->label != NULL);
+       g_assert(!strcmp(app[1]->label, "MIDPfiles"));
+
+       g_free(ef_dir);
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/testsimutil/ber tlv iter", test_ber_tlv_iter);
+       g_test_add_func("/testsimutil/ber tlv encode MMS",
+                       test_ber_tlv_builder_mms);
+       g_test_add_func("/testsimutil/ber tlv encode EFpnn",
+                       test_ber_tlv_builder_efpnn);
+       g_test_add_func("/testsimutil/ber tlv encode 3G Status response",
+                       test_ber_tlv_builder_3g_status);
+       g_test_add_func("/testsimutil/EONS Handling", test_eons);
+       g_test_add_func("/testsimutil/Elementary File DB", test_ef_db);
+       g_test_add_func("/testsimutil/3G Status response", test_3g_status_data);
+       g_test_add_func("/testsimutil/Application entries decoding",
+                       test_application_entry_decode);
+
+       return g_test_run();
+}
diff --git a/unit/test-sms.c b/unit/test-sms.c
new file mode 100644 (file)
index 0000000..aaaf971
--- /dev/null
@@ -0,0 +1,1690 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "util.h"
+#include "smsutil.h"
+
+static const char *simple_deliver = "07911326040000F0"
+               "040B911346610089F60000208062917314480CC8F71D14969741F977FD07";
+static const char *alnum_sender = "0791447758100650"
+               "040DD0F334FC1CA6970100008080312170224008D4F29CDE0EA7D9";
+static const char *simple_submit = "0011000B916407281553F80000AA"
+               "0AE8329BFD4697D9EC37";
+
+static void print_scts(struct sms_scts *scts, const char *prefix)
+{
+       time_t ts;
+       struct tm remote;
+       char buf[128];
+
+       g_print("%s: (YY-MM-DD) %02d-%02d-%02d\n", prefix,
+               (int)scts->year, (int)scts->month, (int)scts->day);
+
+       g_print("%s: (HH-MM-SS) %02d:%02d:%02d\n", prefix,
+               (int)scts->hour, (int)scts->minute, (int)scts->second);
+
+       g_print("%s: Timezone %d hours %d minutes\n", prefix,
+               (int)scts->timezone / 4,
+               (int)((abs(scts->timezone) % 4) * 15));
+
+       ts = sms_scts_to_time(scts, &remote);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&ts));
+       buf[127] = '\0';
+
+       g_print("local time: %s\n", buf);
+
+       strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", &remote);
+       buf[127] = '\0';
+
+       g_print("remote time: %s\n", buf);
+}
+
+static void print_vpf(enum sms_validity_period_format vpf,
+                       struct sms_validity_period *vp)
+{
+       g_print("Validity Period Format: %d\n", (int)vpf);
+
+       switch (vpf) {
+       case SMS_VALIDITY_PERIOD_FORMAT_ABSENT:
+               g_print("Validity-Period: Absent\n");
+               break;
+       case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE:
+               g_print("Validity-Period: %d\n",
+                       (int)vp->relative);
+               break;
+       case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE:
+               print_scts(&vp->absolute, "Validity-Period:");
+               break;
+       case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED:
+               g_print("Validity-Period: Enhanced");
+               break;
+       }
+}
+
+static void dump_details(struct sms *sms)
+{
+       if (sms->sc_addr.address[0] == '\0')
+               g_print("SMSC Address absent, default will be used\n");
+       else
+               g_print("SMSC Address number_type: %d, number_plan: %d, %s\n",
+                       (int)sms->sc_addr.number_type,
+                       (int)sms->sc_addr.numbering_plan, sms->sc_addr.address);
+
+       switch (sms->type) {
+       case SMS_TYPE_DELIVER:
+               g_print("Type: Deliver\n");
+
+               g_print("Originator-Address: %d, %d, %s\n",
+                       (int)sms->deliver.oaddr.number_type,
+                       (int)sms->deliver.oaddr.numbering_plan,
+                       sms->deliver.oaddr.address);
+
+               g_print("PID: %d\n", (int)sms->deliver.pid);
+               g_print("DCS: %d\n", (int)sms->deliver.dcs);
+
+               print_scts(&sms->deliver.scts, "Timestamp");
+
+               break;
+       case SMS_TYPE_SUBMIT:
+               g_print("Type: Submit\n");
+
+               g_print("Message Reference: %u\n", (int)sms->submit.mr);
+
+               g_print("Destination-Address: %d, %d, %s\n",
+                       (int)sms->submit.daddr.number_type,
+                       (int)sms->submit.daddr.numbering_plan,
+                       sms->submit.daddr.address);
+
+               g_print("PID: %d\n", (int)sms->submit.pid);
+               g_print("DCS: %d\n", (int)sms->submit.dcs);
+
+               print_vpf(sms->submit.vpf, &sms->submit.vp);
+
+               break;
+       case SMS_TYPE_STATUS_REPORT:
+               break;
+       case SMS_TYPE_COMMAND:
+       case SMS_TYPE_DELIVER_REPORT_ACK:
+       case SMS_TYPE_DELIVER_REPORT_ERROR:
+       case SMS_TYPE_SUBMIT_REPORT_ACK:
+       case SMS_TYPE_SUBMIT_REPORT_ERROR:
+               break;
+       }
+}
+
+static void test_simple_deliver(void)
+{
+       struct sms sms;
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       gboolean ret;
+       int data_len;
+       unsigned char *unpacked;
+       char *utf8;
+
+       decoded_pdu = decode_hex(simple_deliver, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(simple_deliver) / 2);
+
+       ret = sms_decode(decoded_pdu, pdu_len, FALSE, 30, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_DELIVER);
+
+       if (g_test_verbose())
+               dump_details(&sms);
+
+       g_assert(sms.sc_addr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL);
+       g_assert(sms.sc_addr.numbering_plan == SMS_NUMBERING_PLAN_ISDN);
+       g_assert(strcmp(sms.sc_addr.address, "31624000000") == 0);
+
+       g_assert(sms.deliver.oaddr.number_type ==
+                       SMS_NUMBER_TYPE_INTERNATIONAL);
+       g_assert(sms.deliver.oaddr.numbering_plan ==
+                       SMS_NUMBERING_PLAN_ISDN);
+       g_assert(strcmp(sms.deliver.oaddr.address, "31641600986") == 0);
+
+       g_assert(sms.deliver.pid == 0);
+       g_assert(sms.deliver.dcs == 0);
+
+       g_assert(sms.deliver.scts.year == 2);
+       g_assert(sms.deliver.scts.month == 8);
+       g_assert(sms.deliver.scts.day == 26);
+       g_assert(sms.deliver.scts.hour == 19);
+       g_assert(sms.deliver.scts.minute == 37);
+       g_assert(sms.deliver.scts.second == 41);
+       g_assert(sms.deliver.scts.timezone == -4);
+
+       g_assert(sms.deliver.udl == 12);
+
+       data_len = sms_udl_in_bytes(sms.deliver.udl, sms.deliver.dcs);
+
+       g_assert(data_len == 11);
+
+       unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE,
+                               sms.deliver.udl, NULL, 0xff);
+
+       g_assert(unpacked);
+
+       utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff);
+
+       g_free(unpacked);
+
+       g_assert(utf8);
+
+       if (g_test_verbose())
+               g_print("Decoded user data is: %s\n", utf8);
+
+       g_assert(strcmp(utf8, "How are you?") == 0);
+
+       g_free(utf8);
+}
+
+static void test_alnum_sender(void)
+{
+       struct sms sms;
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       gboolean ret;
+       int data_len;
+       unsigned char *unpacked;
+       char *utf8;
+
+       decoded_pdu = decode_hex(alnum_sender, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(alnum_sender) / 2);
+
+       ret = sms_decode(decoded_pdu, pdu_len, FALSE, 27, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_DELIVER);
+
+       if (g_test_verbose())
+               dump_details(&sms);
+
+       g_assert(sms.sc_addr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL);
+       g_assert(sms.sc_addr.numbering_plan == SMS_NUMBERING_PLAN_ISDN);
+       g_assert(strcmp(sms.sc_addr.address, "447785016005") == 0);
+
+       g_assert(sms.deliver.oaddr.number_type ==
+                       SMS_NUMBER_TYPE_ALPHANUMERIC);
+       g_assert(sms.deliver.oaddr.numbering_plan ==
+                       SMS_NUMBERING_PLAN_UNKNOWN);
+       g_assert(strcmp(sms.deliver.oaddr.address, "sipgate") == 0);
+
+       g_assert(sms.deliver.pid == 0);
+       g_assert(sms.deliver.dcs == 0);
+
+       g_assert(sms.deliver.scts.year == 8);
+       g_assert(sms.deliver.scts.month == 8);
+       g_assert(sms.deliver.scts.day == 13);
+       g_assert(sms.deliver.scts.hour == 12);
+       g_assert(sms.deliver.scts.minute == 07);
+       g_assert(sms.deliver.scts.second == 22);
+       g_assert(sms.deliver.scts.timezone == 4);
+
+       g_assert(sms.deliver.udl == 8);
+
+       data_len = sms_udl_in_bytes(sms.deliver.udl, sms.deliver.dcs);
+
+       g_assert(data_len == 7);
+
+       unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE,
+                               sms.deliver.udl, NULL, 0xff);
+
+       g_assert(unpacked);
+
+       utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff);
+
+       g_free(unpacked);
+
+       g_assert(utf8);
+
+       if (g_test_verbose())
+               g_print("Decoded user data is: %s\n", utf8);
+
+       g_assert(strcmp(utf8, "Testmail") == 0);
+
+       g_free(utf8);
+}
+
+static void test_deliver_encode(void)
+{
+       struct sms sms;
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       gboolean ret;
+       unsigned char pdu[176];
+       int encoded_pdu_len;
+       int encoded_tpdu_len;
+       char *encoded_pdu;
+
+       decoded_pdu = decode_hex(simple_deliver, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(simple_deliver) / 2);
+
+       ret = sms_decode(decoded_pdu, pdu_len, FALSE, 30, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_DELIVER);
+
+       ret = sms_encode(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu);
+
+       if (g_test_verbose()) {
+               int i;
+
+               for (i = 0; i < encoded_pdu_len; i++)
+                       g_print("%02X", pdu[i]);
+               g_print("\n");
+       }
+
+       g_assert(ret);
+       g_assert(encoded_tpdu_len == 30);
+       g_assert(encoded_pdu_len == pdu_len);
+
+       encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0);
+
+       g_assert(strcmp(simple_deliver, encoded_pdu) == 0);
+
+       g_free(encoded_pdu);
+
+       decoded_pdu = decode_hex(alnum_sender, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(alnum_sender) / 2);
+
+       ret = sms_decode(decoded_pdu, pdu_len, FALSE, 27, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_DELIVER);
+
+       ret = sms_encode(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu);
+
+       if (g_test_verbose()) {
+               int i;
+
+               for (i = 0; i < encoded_pdu_len; i++)
+                       g_print("%02X", pdu[i]);
+               g_print("\n");
+       }
+
+       g_assert(ret);
+       g_assert(encoded_tpdu_len == 27);
+       g_assert(encoded_pdu_len == pdu_len);
+
+       encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0);
+
+       g_assert(strcmp(alnum_sender, encoded_pdu) == 0);
+
+       g_free(encoded_pdu);
+}
+
+static void test_simple_submit(void)
+{
+       struct sms sms;
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       gboolean ret;
+       int data_len;
+       unsigned char *unpacked;
+       char *utf8;
+
+       decoded_pdu = decode_hex(simple_submit, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(simple_submit) / 2);
+
+       ret = sms_decode(decoded_pdu, pdu_len, TRUE, 23, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_SUBMIT);
+
+       if (g_test_verbose())
+               dump_details(&sms);
+
+       g_assert(strlen(sms.sc_addr.address) == 0);
+
+       g_assert(sms.submit.mr == 0);
+
+       g_assert(sms.submit.daddr.number_type ==
+                       SMS_NUMBER_TYPE_INTERNATIONAL);
+       g_assert(sms.submit.daddr.numbering_plan ==
+                       SMS_NUMBERING_PLAN_ISDN);
+       g_assert(strcmp(sms.submit.daddr.address, "46708251358") == 0);
+
+       g_assert(sms.submit.pid == 0);
+       g_assert(sms.submit.dcs == 0);
+
+       g_assert(sms.submit.vpf == SMS_VALIDITY_PERIOD_FORMAT_RELATIVE);
+       g_assert(sms.submit.vp.relative == 0xAA);
+
+       g_assert(sms.submit.udl == 10);
+
+       data_len = sms_udl_in_bytes(sms.submit.udl, sms.submit.dcs);
+
+       g_assert(data_len == 9);
+
+       unpacked = unpack_7bit(sms.submit.ud, data_len, 0, FALSE,
+                               sms.submit.udl, NULL, 0xff);
+
+       g_assert(unpacked);
+
+       utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff);
+
+       g_free(unpacked);
+
+       g_assert(utf8);
+
+       if (g_test_verbose())
+               g_print("Decoded user data is: %s\n", utf8);
+
+       g_assert(strcmp(utf8, "hellohello") == 0);
+
+       g_free(utf8);
+}
+
+static void test_submit_encode(void)
+{
+       struct sms sms;
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       gboolean ret;
+       unsigned char pdu[176];
+       int encoded_pdu_len;
+       int encoded_tpdu_len;
+       char *encoded_pdu;
+
+       decoded_pdu = decode_hex(simple_submit, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(simple_submit) / 2);
+
+       ret = sms_decode(decoded_pdu, pdu_len, TRUE, 23, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_SUBMIT);
+
+       ret = sms_encode(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu);
+
+       if (g_test_verbose()) {
+               int i;
+
+               for (i = 0; i < encoded_pdu_len; i++)
+                       g_print("%02X", pdu[i]);
+               g_print("\n");
+       }
+
+       g_assert(ret);
+       g_assert(encoded_tpdu_len == 23);
+       g_assert(encoded_pdu_len == pdu_len);
+
+       encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0);
+
+       g_assert(strcmp(simple_submit, encoded_pdu) == 0);
+
+       g_free(encoded_pdu);
+}
+
+struct sms_charset_data {
+       char *pdu;
+       int data_len;
+       enum gsm_dialect locking_lang;
+       enum gsm_dialect single_lang;
+       char expected_text[];
+};
+
+static struct sms_charset_data sms_charset_default = {
+       .pdu =
+               "0001000B91" "5310101010" "1000008080" "8060402818" "0E888462C1"
+               "68381E9088" "6442A9582E" "988C06C4E9" "783EA09068" "442A994EA8"
+               "946AC56AB9" "5EB0986C46" "ABD96EB89C" "6EC7EBF97E" "C0A070482C"
+               "1A8FC8A472" "C96C3A9FD0" "A8744AAD5A" "AFD8AC76CB" "ED7ABFE0B0"
+               "784C2E9BCF" "E8B47ACD6E" "BBDFF0B87C" "4EAFDBEFF8" "BC7ECFEFFB"
+               "FF",
+       .data_len = 112,
+       .expected_text = {
+               0x40, 0xc2, 0xa3, 0x24, 0xc2, 0xa5, 0xc3, 0xa8, 0xc3, 0xa9,
+               0xc3, 0xb9, 0xc3, 0xac, 0xc3, 0xb2, 0xc3, 0x87, 0x0a, 0xc3,
+               0x98, 0xc3, 0xb8, 0x0d, 0xc3, 0x85, 0xc3, 0xa5, 0xce, 0x94,
+               0x5f, 0xce, 0xa6, 0xce, 0x93, 0xce, 0x9b, 0xce, 0xa9, 0xce,
+               0xa0, 0xce, 0xa8, 0xce, 0xa3, 0xce, 0x98, 0xce, 0x9e, 0x20,
+               0xc3, 0x86, 0xc3, 0xa6, 0xc3, 0x9f, 0xc3, 0x89, 0x20, 0x21,
+               0x22, 0x23, 0xc2, 0xa4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+               0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
+               0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+               0x3f, 0xc2, 0xa1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+               0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
+               0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc3,
+               0x84, 0xc3, 0x96, 0xc3, 0x91, 0xc3, 0x9c, 0xc2, 0xa7, 0xc2,
+               0xbf, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+               0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+               0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xc3, 0xa4, 0xc3,
+               0xb6, 0xc3, 0xb1, 0xc3, 0xbc, 0xc3, 0xa0, 0x00
+       }
+};
+
+static struct sms_charset_data sms_charset_default_ext = {
+       .pdu =
+               "0001000B91" "5310101010" "100000151B" "C58602DAA0" "36A9CD6BC3"
+               "DBF436BE0D" "705306",
+       .data_len = 19,
+       .expected_text = {
+               0x0c, 0x5e, 0x20, 0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d, 0x7c,
+               0xe2, 0x82, 0xac, 0x00
+       }
+};
+
+static struct sms_charset_data sms_charset_turkey = {
+       .pdu =
+               "0001000B91" "5310101010" "1000008080" "8060402818" "0E888462C1"
+               "68381E9088" "6442A9582E" "988C06C4E9" "783EA09068" "442A994EA8"
+               "946AC56AB9" "5EB0986C46" "ABD96EB89C" "6EC7EBF97E" "C0A070482C"
+               "1A8FC8A472" "C96C3A9FD0" "A8744AAD5A" "AFD8AC76CB" "ED7ABFE0B0"
+               "784C2E9BCF" "E8B47ACD6E" "BBDFF0B87C" "4EAFDBEFF8" "BC7ECFEFFB"
+               "FF",
+       .data_len = 112,
+       .locking_lang = GSM_DIALECT_TURKISH,
+       .expected_text = {
+               0x40, 0xc2, 0xa3, 0x24, 0xc2, 0xa5, 0xe2, 0x82, 0xac, 0xc3,
+               0xa9, 0xc3, 0xb9, 0xc4, 0xb1, 0xc3, 0xb2, 0xc3, 0x87, 0x0a,
+               0xc4, 0x9e, 0xc4, 0x9f, 0x0d, 0xc3, 0x85, 0xc3, 0xa5, 0xce,
+               0x94, 0x5f, 0xce, 0xa6, 0xce, 0x93, 0xce, 0x9b, 0xce, 0xa9,
+               0xce, 0xa0, 0xce, 0xa8, 0xce, 0xa3, 0xce, 0x98, 0xce, 0x9e,
+               0x20, 0xc5, 0x9e, 0xc5, 0x9f, 0xc3, 0x9f, 0xc3, 0x89, 0x20,
+               0x21, 0x22, 0x23, 0xc2, 0xa4, 0x25, 0x26, 0x27, 0x28, 0x29,
+               0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+               0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
+               0x3e, 0x3f, 0xc4, 0xb0, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+               0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+               0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
+               0xc3, 0x84, 0xc3, 0x96, 0xc3, 0x91, 0xc3, 0x9c, 0xc2, 0xa7,
+               0xc3, 0xa7, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+               0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+               0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xc3, 0xa4,
+               0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xbc, 0xc3, 0xa0, 0x00
+       }
+};
+
+static struct sms_charset_data sms_charset_turkey_ext = {
+       .pdu =
+               "0001000B91" "5310101010" "1000001A1B" "C586B2416D" "529BD786B7"
+               "E96D7C1BE0" "02C8011318" "870E",
+       .data_len = 23,
+       .locking_lang = GSM_DIALECT_TURKISH,
+       .single_lang = GSM_DIALECT_TURKISH,
+       .expected_text = {
+               0x0c, 0x5e, 0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d, 0x7c, 0xc4,
+               0x9e, 0xc4, 0xb0, 0xc5, 0x9e, 0xc3, 0xa7, 0xe2, 0x82, 0xac,
+               0xc4, 0x9f, 0xc4, 0xb1, 0xc5, 0x9f, 0x00
+       }
+};
+
+static struct sms_charset_data sms_charset_portugal = {
+       .pdu =
+               "0001000B91" "5310101010" "1000008080" "8060402818" "0E888462C1"
+               "68381E9088" "6442A9582E" "988C06C4E9" "783EA09068" "442A994EA8"
+               "946AC56AB9" "5EB0986C46" "ABD96EB89C" "6EC7EBF97E" "C0A070482C"
+               "1A8FC8A472" "C96C3A9FD0" "A8744AAD5A" "AFD8AC76CB" "ED7ABFE0B0"
+               "784C2E9BCF" "E8B47ACD6E" "BBDFF0B87C" "4EAFDBEFF8" "BC7ECFEFFB"
+               "FF",
+       .data_len = 112,
+       .locking_lang = GSM_DIALECT_PORTUGUESE,
+       .expected_text = {
+               0x40, 0xc2, 0xa3, 0x24, 0xc2, 0xa5, 0xc3, 0xaa, 0xc3, 0xa9,
+               0xc3, 0xba, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xa7, 0x0a, 0xc3,
+               0x94, 0xc3, 0xb4, 0x0d, 0xc3, 0x81, 0xc3, 0xa1, 0xce, 0x94,
+               0x5f, 0xc2, 0xaa, 0xc3, 0x87, 0xc3, 0x80, 0xe2, 0x88, 0x9e,
+               0x5e, 0x5c, 0xe2, 0x82, 0xac, 0xc3, 0x93, 0x7c, 0x20, 0xc3,
+               0x82, 0xc3, 0xa2, 0xc3, 0x8a, 0xc3, 0x89, 0x20, 0x21, 0x22,
+               0x23, 0xc2, 0xba, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+               0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+               0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+               0xc3, 0x8d, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+               0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
+               0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc3, 0x83,
+               0xc3, 0x95, 0xc3, 0x9a, 0xc3, 0x9c, 0xc2, 0xa7, 0x7e, 0x61,
+               0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+               0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+               0x76, 0x77, 0x78, 0x79, 0x7a, 0xc3, 0xa3, 0xc3, 0xb5, 0x60,
+               0xc3, 0xbc, 0xc3, 0xa0, 0x00
+       }
+};
+
+static struct sms_charset_data sms_charset_portugal_ext = {
+       .pdu =
+               "0001000B91" "5310101010" "1000003184" "C446B16038" "1E1BC96662"
+               "D9543696CD" "6583D9643C" "1BD42675D9" "F0C01B9F86" "02CC74B75C"
+               "0EE68030EC" "F91D",
+       .data_len = 43,
+       .locking_lang = GSM_DIALECT_PORTUGUESE,
+       .single_lang = GSM_DIALECT_PORTUGUESE,
+       .expected_text = {
+               0xc3, 0xaa, 0xc3, 0xa7, 0x0c, 0xc3, 0x94, 0xc3, 0xb4, 0xc3,
+               0x81, 0xc3, 0xa1, 0xce, 0xa6, 0xce, 0x93, 0x5e, 0xce, 0xa9,
+               0xce, 0xa0, 0xce, 0xa8, 0xce, 0xa3, 0xce, 0x98, 0xc3, 0x8a,
+               0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d, 0x7c, 0xc3, 0x80, 0xc3,
+               0x8d, 0xc3, 0x93, 0xc3, 0x9a, 0xc3, 0x83, 0xc3, 0x95, 0xc3,
+               0x82, 0xe2, 0x82, 0xac, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba,
+               0xc3, 0xa3, 0xc3, 0xb5, 0xc3, 0xa2, 0x00
+       }
+};
+
+static struct sms_charset_data sms_charset_spain = {
+       .pdu =
+               "0001000B91" "5310101010" "100000269B" "C446B1A16C" "509BD4E6B5"
+               "E16D7A1BDF" "06B8096E92" "9BE7A6BA09" "6FCA9BF4E6" "BDA903",
+       .data_len = 34,
+       .locking_lang = GSM_DIALECT_SPANISH,
+       .single_lang = GSM_DIALECT_SPANISH,
+       .expected_text = {
+               0xc3, 0xa7, 0x0c, 0x5e, 0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d,
+               0x7c, 0xc3, 0x81, 0xc3, 0x8d, 0xc3, 0x93, 0xc3, 0x9a, 0xc3,
+               0xa1, 0xe2, 0x82, 0xac, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba,
+               0x00
+       }
+};
+
+static void test_sms_charset(gconstpointer param)
+{
+       gboolean ret;
+       struct sms sms;
+       unsigned char *pdu;
+       unsigned char *unpacked;
+       long pdu_len;
+       int data_len;
+       enum sms_charset sms_charset;
+       gboolean sms_compressed;
+       char *text;
+       struct sms_charset_data *data = (struct sms_charset_data *)param;
+
+       pdu = decode_hex(data->pdu, -1, &pdu_len, 0);
+
+       g_assert(pdu);
+       g_assert(pdu_len == (gint64)strlen(data->pdu) / 2);
+
+       ret = sms_decode(pdu, pdu_len, FALSE, pdu_len, &sms);
+
+       g_assert(ret);
+
+       g_free(pdu);
+
+       g_assert(sms.type == SMS_TYPE_DELIVER);
+
+       ret = sms_dcs_decode(sms.deliver.dcs, NULL, &sms_charset,
+                               &sms_compressed, NULL);
+
+       g_assert(ret);
+       g_assert(sms_charset == SMS_CHARSET_7BIT);
+       g_assert(sms_compressed == FALSE);
+
+       data_len = sms_udl_in_bytes(sms.deliver.udl, sms.deliver.dcs);
+
+       g_assert(data_len == data->data_len);
+
+       unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE,
+                               sms.deliver.udl, NULL, 0xff);
+
+       g_assert(unpacked);
+
+       text = convert_gsm_to_utf8_with_lang(unpacked, -1, NULL, NULL, 0xff,
+               data->locking_lang, data->single_lang);
+
+       g_assert(text);
+
+       g_free(unpacked);
+
+       g_assert(strcmp(data->expected_text, text) == 0);
+
+       g_free(text);
+}
+
+struct text_format_header {
+       unsigned char len;
+       unsigned char start;
+       unsigned char span;
+       unsigned char format;
+       unsigned char color;
+};
+
+struct ems_udh_test {
+       const char *pdu;
+       unsigned int len;
+       const char *expected;
+       unsigned int udl;
+       unsigned int udhl;
+       unsigned int data_len;
+       struct text_format_header formats[];
+};
+
+static struct ems_udh_test ems_udh_test_1 = {
+       .pdu = "0041000B915121551532F40000631A0A031906200A032104100A03270504"
+               "0A032E05080A043807002B8ACD29A85D9ECFC3E7F21C340EBB41E3B79B1"
+               "E4EBB41697A989D1EB340E2379BCC02B1C3F27399059AB7C36C3628EC26"
+               "83C66FF65B5E2683E8653C1D",
+       .len = 100,
+       .expected = "EMS messages can contain italic, bold, large, small and"
+               " colored text",
+       .formats = {
+               {
+                       .len = 3,
+                       .start = 0x19,
+                       .span = 0x06,
+                       .format = 0x20,
+               },
+               {
+                       .len = 3,
+                       .start = 0x21,
+                       .span = 0x04,
+                       .format = 0x10,
+               },
+               {
+                       .len = 3,
+                       .start = 0x27,
+                       .span = 0x05,
+                       .format = 0x04,
+               },
+               {
+                       .len = 3,
+                       .start = 0x2E,
+                       .span = 0x05,
+                       .format = 0x08,
+               },
+               {
+                       .len = 4,
+                       .start = 0x38,
+                       .span = 0x07,
+                       .format = 0x00,
+                       .color = 0x2B,
+               },
+               {
+                       .len = 0,
+               }
+       },
+       .udl = 99,
+       .udhl = 26,
+       .data_len = 87,
+};
+
+static struct ems_udh_test ems_udh_test_2 = {
+       .pdu = "079194712272303351030B915121340195F60000FF80230A030F07230A031"
+               "806130A031E0A430A032E0D830A033D14020A035104F60A0355010600159"
+               "D9E83D2735018442FCFE98A243DCC4E97C92C90F8CD26B3407537B92C67A"
+               "7DD65320B1476934173BA3CBD2ED3D1F277FD8C76299CEF3B280C92A7CF6"
+               "83A28CC4E9FDD6532E8FE96935D",
+       .len = 126,
+       .expected = "This is a test\nItalied, bold, underlined, and "
+               "strikethrough.\nNow a right aligned word.",
+       .formats = {
+               {
+                       .len = 3,
+                       .start = 0x0f,
+                       .span = 0x07,
+                       .format = 0x23,
+               },
+               {
+                       .len = 3,
+                       .start = 0x18,
+                       .span = 0x06,
+                       .format = 0x13,
+               },
+               {
+                       .len = 3,
+                       .start = 0x1e,
+                       .span = 0x0a,
+                       .format = 0x43,
+               },
+               {
+                       .len = 3,
+                       .start = 0x2e,
+                       .span = 0x0d,
+                       .format = 0x83,
+               },
+               {
+                       .len = 3,
+                       .start = 0x3d,
+                       .span = 0x14,
+                       .format = 0x02,
+               },
+               {
+                       .len = 3,
+                       .start = 0x51,
+                       .span = 0x04,
+                       .format = 0xf6,
+               },
+               {
+                       .len = 3,
+                       .start = 0x55,
+                       .span = 0x01,
+                       .format = 0x06,
+               },
+       },
+       .udl = 128,
+       .udhl = 35,
+       .data_len = 112,
+};
+
+static void test_ems_udh(gconstpointer data)
+{
+       const struct ems_udh_test *test = data;
+       struct sms sms;
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       gboolean ret;
+       unsigned int data_len;
+       unsigned int udhl;
+       struct sms_udh_iter iter;
+       int max_chars;
+       unsigned char *unpacked;
+       char *utf8;
+       int i;
+
+       decoded_pdu = decode_hex(test->pdu, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(test->pdu) / 2);
+
+       ret = sms_decode(decoded_pdu, pdu_len, TRUE, test->len, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_SUBMIT);
+
+       if (g_test_verbose())
+               dump_details(&sms);
+       udhl = sms.submit.ud[0];
+
+       g_assert(sms.submit.udl == test->udl);
+       g_assert(udhl == test->udhl);
+
+       ret = sms_udh_iter_init(&sms, &iter);
+
+       g_assert(ret);
+
+       for (i = 0; test->formats[i].len; i++) {
+               if (g_test_verbose()) {
+                       int j;
+                       unsigned char data[4];
+
+                       sms_udh_iter_get_ie_data(&iter, data);
+
+                       g_print("Header:\n");
+                       for (j = 0; j < sms_udh_iter_get_ie_length(&iter); j++)
+                               g_print("0x%02x ", data[j]);
+
+                       g_print("\n");
+               }
+
+               g_assert(sms_udh_iter_get_ie_type(&iter) ==
+                               SMS_IEI_TEXT_FORMAT);
+               g_assert(sms_udh_iter_get_ie_length(&iter) ==
+                               test->formats[i].len);
+
+               if (test->formats[i+1].len) {
+                       g_assert(sms_udh_iter_has_next(&iter) == TRUE);
+                       g_assert(sms_udh_iter_next(&iter) == TRUE);
+               } else {
+                       g_assert(sms_udh_iter_has_next(&iter) == FALSE);
+                       g_assert(sms_udh_iter_next(&iter) == FALSE);
+                       g_assert(sms_udh_iter_get_ie_type(&iter) ==
+                                       SMS_IEI_INVALID);
+               }
+       }
+
+       data_len = sms_udl_in_bytes(sms.submit.udl, sms.submit.dcs);
+
+       g_assert(data_len == test->data_len);
+
+       max_chars = (data_len - (udhl + 1)) * 8 / 7;
+
+       unpacked = unpack_7bit(sms.submit.ud + udhl + 1, data_len - (udhl + 1),
+                               udhl + 1, FALSE, max_chars, NULL, 0xff);
+
+       g_assert(unpacked);
+
+       utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff);
+
+       g_free(unpacked);
+
+       g_assert(utf8);
+
+       if (g_test_verbose())
+               g_print("Decoded user data is: %s\n", utf8);
+
+       g_assert(strcmp(utf8, test->expected) == 0);
+
+       g_free(utf8);
+}
+
+static const char *assembly_pdu1 = "038121F340048155550119906041001222048C0500"
+                                       "031E0301041804420430043A002C002004100"
+                                       "43B0435043A04410430043D04340440002000"
+                                       "200441043B044304480430043B00200437043"
+                                       "000200434043204350440044C044E00200020"
+                                       "04380020002004320441043500200431043E0"
+                                       "43B044C044804350020043F04400435043804"
+                                       "41043F043E043B043D044F043B0441044F002"
+                                       "000200433043D0435";
+static int assembly_pdu_len1 = 155;
+
+static const char *assembly_pdu2 = "038121F340048155550119906041001222048C0500"
+                                       "031E03020432043E043C002E000A041D04300"
+                                       "43A043E043D04350446002C0020043D043500"
+                                       "200432002004410438043B043004450020043"
+                                       "40430043B043504350020044204350440043F"
+                                       "04350442044C002C0020043E043D002004410"
+                                       "44204400435043C043804420435043B044C04"
+                                       "3D043E002004320431043504360430043B002"
+                                       "004320020043A043E";
+static int assembly_pdu_len2 = 155;
+
+static const char *assembly_pdu3 = "038121F340048155550119906041001222044A0500"
+                                       "031E0303043C043D043004420443002C00200"
+                                       "43F043E043704300431044B0432000A043404"
+                                       "3004360435002C002004470442043E0020002"
+                                       "00431044B043B0020043D04300433002E";
+static int assembly_pdu_len3 = 89;
+
+static void test_assembly(void)
+{
+       unsigned char pdu[176];
+       long pdu_len;
+       struct sms sms;
+       struct sms_assembly *assembly = sms_assembly_new(NULL);
+       guint16 ref;
+       guint8 max;
+       guint8 seq;
+       GSList *l;
+       char *utf8;
+       char *reencoded;
+
+       decode_hex_own_buf(assembly_pdu1, -1, &pdu_len, 0, pdu);
+       sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len1, &sms);
+
+       sms_extract_concatenation(&sms, &ref, &max, &seq);
+       l = sms_assembly_add_fragment(assembly, &sms, time(NULL),
+                                       &sms.deliver.oaddr, ref, max, seq);
+
+       if (g_test_verbose()) {
+               g_print("Ref: %u\n", ref);
+               g_print("Max: %u\n", max);
+               g_print("From: %s\n",
+                               sms_address_to_string(&sms.deliver.oaddr));
+       }
+
+       g_assert(g_slist_length(assembly->assembly_list) == 1);
+       g_assert(l == NULL);
+
+       sms_assembly_expire(assembly, time(NULL) + 40);
+
+       g_assert(g_slist_length(assembly->assembly_list) == 0);
+
+       sms_extract_concatenation(&sms, &ref, &max, &seq);
+       l = sms_assembly_add_fragment(assembly, &sms, time(NULL),
+                                       &sms.deliver.oaddr, ref, max, seq);
+       g_assert(g_slist_length(assembly->assembly_list) == 1);
+       g_assert(l == NULL);
+
+       decode_hex_own_buf(assembly_pdu2, -1, &pdu_len, 0, pdu);
+       sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len2, &sms);
+
+       sms_extract_concatenation(&sms, &ref, &max, &seq);
+       l = sms_assembly_add_fragment(assembly, &sms, time(NULL),
+                                       &sms.deliver.oaddr, ref, max, seq);
+       g_assert(l == NULL);
+
+       decode_hex_own_buf(assembly_pdu3, -1, &pdu_len, 0, pdu);
+       sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len3, &sms);
+
+       sms_extract_concatenation(&sms, &ref, &max, &seq);
+       l = sms_assembly_add_fragment(assembly, &sms, time(NULL),
+                                       &sms.deliver.oaddr, ref, max, seq);
+
+       g_assert(l != NULL);
+
+       utf8 = sms_decode_text(l);
+
+       g_slist_foreach(l, (GFunc)g_free, NULL);
+       g_slist_free(l);
+
+       sms_assembly_free(assembly);
+
+       if (g_test_verbose())
+               g_printf("Text:\n%s\n", utf8);
+
+       l = sms_text_prepare("555", utf8, ref, TRUE, FALSE);
+       g_assert(l);
+       g_assert(g_slist_length(l) == 3);
+
+       reencoded = sms_decode_text(l);
+
+       if (g_test_verbose())
+               g_printf("ReEncoded:\n%s\n", reencoded);
+
+       g_assert(strcmp(utf8, reencoded) == 0);
+
+       g_free(utf8);
+       g_free(reencoded);
+}
+
+static const char *test_no_fragmentation_7bit = "This is testing !";
+static const char *expected_no_fragmentation_7bit = "079153485002020911000C915"
+                       "348870420140000A71154747A0E4ACF41F4F29C9E769F4121";
+static const char *sc_addr = "+358405202090";
+static const char *da_addr = "+358478400241";
+static void test_prepare_7bit(void)
+{
+       GSList *r;
+       struct sms *sms;
+       gboolean ret;
+       unsigned char pdu[176];
+       int encoded_pdu_len;
+       int encoded_tpdu_len;
+       char *encoded_pdu;
+
+       r = sms_text_prepare("555", test_no_fragmentation_7bit, 0,
+                               FALSE, FALSE);
+
+       g_assert(r != NULL);
+
+       sms = r->data;
+
+       sms->sc_addr.number_type = SMS_NUMBER_TYPE_INTERNATIONAL;
+       sms->sc_addr.numbering_plan = SMS_NUMBERING_PLAN_ISDN;
+       strcpy(sms->sc_addr.address, sc_addr+1);
+
+       if (g_test_verbose())
+               g_print("sc_addr: %s\n", sms_address_to_string(&sms->sc_addr));
+
+       g_assert(!strcmp(sc_addr, sms_address_to_string(&sms->sc_addr)));
+
+       sms->submit.daddr.number_type = SMS_NUMBER_TYPE_INTERNATIONAL;
+       sms->submit.daddr.numbering_plan = SMS_NUMBERING_PLAN_ISDN;
+       strcpy(sms->submit.daddr.address, da_addr+1);
+
+       if (g_test_verbose())
+               g_print("da_addr: %s\n",
+                       sms_address_to_string(&sms->submit.daddr));
+
+       g_assert(!strcmp(da_addr,
+                               sms_address_to_string(&sms->submit.daddr)));
+
+       ret = sms_encode(sms, &encoded_pdu_len, &encoded_tpdu_len, pdu);
+
+       g_assert(ret);
+
+       if (g_test_verbose()) {
+               int i;
+
+               for (i = 0; i < encoded_pdu_len; i++)
+                       g_print("%02X", pdu[i]);
+               g_print("\n");
+       }
+
+       encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0);
+
+       g_assert(strcmp(expected_no_fragmentation_7bit, encoded_pdu) == 0);
+
+       g_free(encoded_pdu);
+       g_slist_foreach(r, (GFunc)g_free, NULL);
+       g_slist_free(r);
+}
+
+struct sms_concat_data {
+       const char *str;
+       unsigned int segments;
+};
+
+static struct sms_concat_data shakespeare_test = {
+       .str = "Shakespeare divided his time between London and Str"
+       "atford during his career. In 1596, the year before he bought New Plac"
+       "e as his family home in Stratford, Shakespeare was living in the pari"
+       "sh of St. Helen's, Bishopsgate, north of the River Thames.",
+       .segments = 2,
+};
+
+/* The string in this test should be padded at the end.  This confuses some
+ * decoders which do not use udl properly
+ */
+static void test_prepare_concat(gconstpointer data)
+{
+       const struct sms_concat_data *test = data;
+       GSList *r;
+       GSList *l;
+       char *decoded_str;
+       GSList *pdus = NULL;
+       unsigned char pdu[176];
+       struct sms *sms;
+       struct sms decoded;
+       int pdu_len, tpdu_len;
+       struct sms_assembly *assembly = sms_assembly_new(NULL);
+       guint16 ref;
+       guint8 max;
+       guint8 seq;
+
+       if (g_test_verbose())
+               g_print("strlen: %zd\n", strlen(test->str));
+
+       r = sms_text_prepare("+15554449999", test->str, 0, TRUE, FALSE);
+       g_assert(r);
+       g_assert(g_slist_length(r) == test->segments);
+
+       for (l = r; l; l = l->next) {
+               char *strpdu;
+
+               sms = l->data;
+
+               sms_encode(sms, &pdu_len, &tpdu_len, pdu);
+               g_assert(pdu_len == (tpdu_len + 1));
+
+               strpdu = encode_hex(pdu, pdu_len, 0);
+
+               if (g_test_verbose())
+                       g_printf("PDU: %s, len: %d, tlen: %d\n",
+                               strpdu, pdu_len, tpdu_len);
+               pdus = g_slist_append(pdus, strpdu);
+       }
+
+       g_slist_foreach(r, (GFunc)g_free, NULL);
+       g_slist_free(r);
+
+       for (l = pdus; l; l = l->next) {
+               long len;
+               gboolean ok;
+
+               decode_hex_own_buf((char *)l->data, -1, &len, 0, pdu);
+
+               if (g_test_verbose())
+                       g_print("PDU Len: %ld\n", len);
+
+               ok = sms_decode(pdu, len, TRUE, len - 1, &decoded);
+               g_assert(ok);
+
+               if (g_test_verbose())
+                       g_print("Pdu udl: %d\n", (int)decoded.submit.udl);
+
+               sms_extract_concatenation(&decoded, &ref, &max, &seq);
+               r = sms_assembly_add_fragment(assembly, &decoded, time(NULL),
+                                               &decoded.submit.daddr,
+                                               ref, max, seq);
+       }
+
+       g_assert(r);
+
+       decoded_str = sms_decode_text(r);
+
+       if (g_test_verbose())
+               g_printf("Decoded String: %s\n", decoded_str);
+
+       g_assert(decoded_str);
+       g_assert(strcmp(decoded_str, test->str) == 0);
+       g_free(decoded_str);
+       sms_assembly_free(assembly);
+}
+
+static void test_limit(gunichar uni, int target_size, gboolean use_16bit)
+{
+       char *utf8;
+       char *decoded;
+       GSList *l;
+       unsigned int i;
+       char utf8_char[6];
+       unsigned int stride;
+
+       stride = g_unichar_to_utf8(uni, utf8_char);
+
+       utf8 = g_new0(char, (target_size + 2) * stride);
+
+       for (i = 0; i < target_size * stride; i += stride)
+               memcpy(utf8 + i, utf8_char, stride);
+
+       utf8[i] = '\0';
+
+       l = sms_text_prepare("555", utf8, 0, use_16bit, FALSE);
+
+       g_assert(l);
+       g_assert(g_slist_length(l) == 255);
+
+       decoded = sms_decode_text(l);
+       g_assert(g_utf8_strlen(decoded, -1) == target_size);
+
+       g_free(decoded);
+
+       memcpy(utf8 + i, utf8_char, stride);
+       utf8[i+stride] = '\0';
+
+       l = sms_text_prepare("555", utf8, 0, use_16bit, FALSE);
+
+       g_assert(l == NULL);
+       g_free(utf8);
+}
+
+static void test_prepare_limits(void)
+{
+       gunichar ascii = 0x41;
+       gunichar ucs2 = 0x416;
+       unsigned int target_size;
+
+       /* The limit for 16 bit headers is 255 * 152 for GSM7 */
+       target_size = 255 * 152;
+       test_limit(ascii, target_size, TRUE);
+
+       /* The limit for 8 bit headers is 255 * 153 for GSM7 */
+       target_size = 255 * 153;
+       test_limit(ascii, target_size, FALSE);
+
+       /* The limit for 16 bit headers is 255 * 66 for UCS2 */
+       target_size = 255 * 66;
+       test_limit(ucs2, target_size, TRUE);
+
+       /* The limit for 8 bit headers is 255 * 67 for UCS2 */
+       target_size = 255 * 67;
+       test_limit(ucs2, target_size, FALSE);
+}
+
+static const char *cbs1 = "011000320111C2327BFC76BBCBEE46A3D168341A8D46A3D1683"
+       "41A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168"
+       "341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D100";
+
+static const char *cbs2 = "0110003201114679785E96371A8D46A3D168341A8D46A3D1683"
+       "41A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168"
+       "341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D100";
+
+static void test_cbs_encode_decode(void)
+{
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       gboolean ret;
+       struct cbs cbs;
+       unsigned char pdu[88];
+       int len;
+       char *encoded_pdu;
+       GSList *l;
+       char iso639_lang[3];
+       char *utf8;
+
+       decoded_pdu = decode_hex(cbs1, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+       g_assert(pdu_len == (long)strlen(cbs1) / 2);
+       g_assert(pdu_len == 88);
+
+       ret = cbs_decode(decoded_pdu, pdu_len, &cbs);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+
+       g_assert(cbs.gs == CBS_GEO_SCOPE_CELL_IMMEDIATE);
+       g_assert(cbs.message_code == 17);
+       g_assert(cbs.update_number == 0);
+       g_assert(cbs.message_identifier == 50);
+       g_assert(cbs.dcs == 1);
+       g_assert(cbs.max_pages == 1);
+       g_assert(cbs.page == 1);
+
+       l = g_slist_append(NULL, &cbs);
+
+       utf8 = cbs_decode_text(l, iso639_lang);
+
+       g_assert(utf8);
+
+       if (g_test_verbose()) {
+               g_printf("%s\n", utf8);
+               if (iso639_lang[0] == '\0')
+                       g_printf("Lang: Unspecified\n");
+               else
+                       g_printf("Lang: %s\n", iso639_lang);
+       }
+
+       g_assert(strcmp(utf8, "Belconnen") == 0);
+       g_assert(strcmp(iso639_lang, "en") == 0);
+
+       g_free(utf8);
+
+       g_slist_free(l);
+
+       ret = cbs_encode(&cbs, &len, pdu);
+
+       g_assert(ret);
+
+       encoded_pdu = encode_hex(pdu, len, 0);
+
+       g_assert(strcmp(cbs1, encoded_pdu) == 0);
+
+       g_free(encoded_pdu);
+}
+
+static void test_cbs_assembly(void)
+{
+       unsigned char *decoded_pdu;
+       long pdu_len;
+       struct cbs dec1;
+       struct cbs dec2;
+       struct cbs_assembly *assembly;
+       char iso639_lang[3];
+       GSList *l;
+       char *utf8;
+
+       assembly = cbs_assembly_new();
+
+       g_assert(assembly);
+
+       decoded_pdu = decode_hex(cbs1, -1, &pdu_len, 0);
+       cbs_decode(decoded_pdu, pdu_len, &dec1);
+       g_free(decoded_pdu);
+
+       decoded_pdu = decode_hex(cbs2, -1, &pdu_len, 0);
+       cbs_decode(decoded_pdu, pdu_len, &dec2);
+       g_free(decoded_pdu);
+
+       /* Add an initial page to the assembly */
+       l = cbs_assembly_add_page(assembly, &dec1);
+       g_assert(l);
+       g_assert(g_slist_length(assembly->recv_cell) == 1);
+       g_slist_foreach(l, (GFunc)g_free, NULL);
+       g_slist_free(l);
+
+       /* Can we receive new updates ? */
+       dec1.update_number = 8;
+       l = cbs_assembly_add_page(assembly, &dec1);
+       g_assert(l);
+       g_assert(g_slist_length(assembly->recv_cell) == 1);
+       g_slist_foreach(l, (GFunc)g_free, NULL);
+       g_slist_free(l);
+
+       /* Do we ignore old pages ? */
+       l = cbs_assembly_add_page(assembly, &dec1);
+       g_assert(l == NULL);
+
+       /* Do we ignore older pages ? */
+       dec1.update_number = 5;
+       l = cbs_assembly_add_page(assembly, &dec1);
+       g_assert(l == NULL);
+
+       cbs_assembly_location_changed(assembly, TRUE, TRUE, TRUE);
+       g_assert(assembly->recv_cell == NULL);
+
+       dec1.update_number = 9;
+       dec1.page = 3;
+       dec1.max_pages = 3;
+
+       dec2.update_number = 9;
+       dec2.page = 2;
+       dec2.max_pages = 3;
+
+       l = cbs_assembly_add_page(assembly, &dec2);
+       g_assert(l == NULL);
+       l = cbs_assembly_add_page(assembly, &dec1);
+       g_assert(l == NULL);
+
+       dec1.page = 1;
+       l = cbs_assembly_add_page(assembly, &dec1);
+       g_assert(l);
+
+       utf8 = cbs_decode_text(l, iso639_lang);
+
+       g_assert(utf8);
+
+       if (g_test_verbose()) {
+               g_printf("%s\n", utf8);
+               if (iso639_lang[0] == '\0')
+                       g_printf("Lang: Unspecified\n");
+               else
+                       g_printf("Lang: %s\n", iso639_lang);
+       }
+
+       g_assert(strcmp(utf8, "BelconnenFraserBelconnen") == 0);
+
+       g_free(utf8);
+       g_slist_foreach(l, (GFunc)g_free, NULL);
+       g_slist_free(l);
+
+       cbs_assembly_free(assembly);
+}
+
+static const char *ranges[] = { "1-5, 2, 3, 600, 569-900, 999",
+                               "0-20, 33, 44, 50-60, 20-50, 1-5, 5, 3, 5",
+                               NULL };
+static const char *inv_ranges[] = { "1-5, 3333", "1-5, afbcd", "1-5, 3-5,,",
+                                       "1-5, 3-5, c", NULL };
+
+static void test_range_minimizer(void)
+{
+       int i = 0;
+
+       while (inv_ranges[i]) {
+               GSList *l = cbs_extract_topic_ranges(inv_ranges[i]);
+
+               g_assert(l == NULL);
+               i++;
+       }
+
+       i = 0;
+
+       while (ranges[i]) {
+               GSList *r = cbs_extract_topic_ranges(ranges[i]);
+               char *rangestr;
+
+               g_assert(r != NULL);
+               i++;
+
+               rangestr = cbs_topic_ranges_to_string(r);
+
+               g_assert(rangestr);
+
+               if (g_test_verbose())
+                       g_print("range: %s\n", rangestr);
+
+               g_free(rangestr);
+               g_slist_foreach(r, (GFunc)g_free, NULL);
+               g_slist_free(r);
+       }
+}
+
+static void test_sr_assembly(void)
+{
+       const char *sr_pdu1 = "06040D91945152991136F00160124130340A0160124130"
+                               "940A00";
+       const char *sr_pdu2 = "06050D91945152991136F00160124130640A0160124130"
+                               "450A00";
+       const char *sr_pdu3 = "0606098121436587F9019012413064A0019012413045A0"
+                               "00";
+        struct sms sr1;
+       struct sms sr2;
+       struct sms sr3;
+       unsigned char pdu[176];
+       long pdu_len;
+       struct status_report_assembly *sra;
+       gboolean delivered;
+       struct sms_address addr;
+       unsigned char sha1[SMS_MSGID_LEN] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+                                               10, 11, 12, 13, 14, 15,
+                                               16, 17, 18, 19 };
+       unsigned char id[SMS_MSGID_LEN];
+
+       /* international address, mr 4 & mr 5 */
+
+        decode_hex_own_buf(sr_pdu1, -1, &pdu_len, 0, pdu);
+       g_assert(sms_decode(pdu, pdu_len, FALSE, 26, &sr1) == TRUE);
+
+       decode_hex_own_buf(sr_pdu2, -1, &pdu_len, 0, pdu);
+       g_assert(sms_decode(pdu, pdu_len, FALSE, 26, &sr2) == TRUE);
+
+       /* national address, mr 6 */
+
+       decode_hex_own_buf(sr_pdu3, -1, &pdu_len, 0, pdu);
+       g_assert(sms_decode(pdu, pdu_len, FALSE, 24, &sr3) == TRUE);
+
+       if (g_test_verbose()) {
+               g_print("sr1 address: %s, mr: %d\n",
+                       sms_address_to_string(&sr1.status_report.raddr),
+                       sr1.status_report.mr);
+
+               g_print("sr2 address: %s, mr: %d\n",
+                       sms_address_to_string(&sr2.status_report.raddr),
+                       sr2.status_report.mr);
+
+               g_print("sr3 address: %s, mr: %d\n",
+                       sms_address_to_string(&sr3.status_report.raddr),
+                       sr3.status_report.mr);
+       }
+
+       sms_address_from_string(&addr, "+4915259911630");
+
+       sra = status_report_assembly_new(NULL);
+
+       status_report_assembly_add_fragment(sra, sha1, &addr, 4, time(NULL), 2);
+       status_report_assembly_add_fragment(sra, sha1, &addr, 5, time(NULL), 2);
+
+       status_report_assembly_expire(sra, time(NULL) + 40);
+       g_assert(g_hash_table_size(sra->assembly_table) == 0);
+
+       status_report_assembly_add_fragment(sra, sha1, &addr, 4, time(NULL), 2);
+       status_report_assembly_add_fragment(sra, sha1, &addr, 5, time(NULL), 2);
+
+       g_assert(!status_report_assembly_report(sra, &sr1, id, &delivered));
+       g_assert(status_report_assembly_report(sra, &sr2, id, &delivered));
+
+       g_assert(memcmp(id, sha1, SMS_MSGID_LEN) == 0);
+       g_assert(delivered == TRUE);
+
+       /*
+        * Send sms-message in the national address-format,
+        * but receive in the international address-format.
+        */
+       sms_address_from_string(&addr, "9911630");
+       status_report_assembly_add_fragment(sra, sha1, &addr, 4, time(NULL), 2);
+       status_report_assembly_add_fragment(sra, sha1, &addr, 5, time(NULL), 2);
+
+       g_assert(!status_report_assembly_report(sra, &sr1, id, &delivered));
+       g_assert(status_report_assembly_report(sra, &sr2, id, &delivered));
+
+       g_assert(memcmp(id, sha1, SMS_MSGID_LEN) == 0);
+       g_assert(delivered == TRUE);
+       g_assert(g_hash_table_size(sra->assembly_table) == 0);
+
+       /*
+        * Send sms-message in the international address-format,
+        * but receive in the national address-format.
+        */
+       sms_address_from_string(&addr, "+358123456789");
+       status_report_assembly_add_fragment(sra, sha1, &addr, 6, time(NULL), 1);
+
+       g_assert(status_report_assembly_report(sra, &sr3, id, &delivered));
+
+       g_assert(memcmp(id, sha1, SMS_MSGID_LEN) == 0);
+       g_assert(delivered == TRUE);
+       g_assert(g_hash_table_size(sra->assembly_table) == 0);
+
+       status_report_assembly_free(sra);
+}
+
+struct wap_push_data {
+       const char *pdu;
+       int len;
+};
+
+static struct wap_push_data wap_push_1 = {
+       .pdu = "0791947122725014440185F039F501801140311480720605040B8423F00106"
+               "246170706C69636174696F6E2F766E642E7761702E6D6D732D6D657373616"
+               "76500AF84B4868C82984F67514B4B42008D9089088045726F74696B009650"
+               "696E2D557073008A808E0240008805810303F48083687474703A2F2F65707"
+               "3332E64652F4F2F5A39495A4F00",
+       .len = 128,
+};
+
+static void test_wap_push(gconstpointer data)
+{
+       const struct wap_push_data *test = data;
+       struct sms sms;
+       unsigned char *decoded_pdu;
+       gboolean ret;
+       long pdu_len;
+       long data_len;
+       enum sms_class cls;
+       enum sms_charset charset;
+       GSList *list;
+       unsigned char *wap_push;
+       int dst_port, src_port;
+       gboolean is_8bit;
+
+       decoded_pdu = decode_hex(test->pdu, -1, &pdu_len, 0);
+
+       g_assert(decoded_pdu);
+
+       ret = sms_decode(decoded_pdu, pdu_len, FALSE, test->len, &sms);
+
+       g_free(decoded_pdu);
+
+       g_assert(ret);
+       g_assert(sms.type == SMS_TYPE_DELIVER);
+
+       if (g_test_verbose())
+               dump_details(&sms);
+
+       ret = sms_dcs_decode(sms.deliver.dcs, &cls, &charset, NULL, NULL);
+
+       g_assert(ret == TRUE);
+       g_assert(charset == SMS_CHARSET_8BIT);
+
+       g_assert(sms_extract_app_port(&sms, &dst_port, &src_port, &is_8bit));
+
+       if (g_test_verbose()) {
+               g_print("8bit: %d\n", is_8bit);
+               g_print("src: %d, dst: %d\n", src_port, dst_port);
+       }
+
+       g_assert(is_8bit == FALSE);
+       g_assert(dst_port == 2948);
+
+       list = g_slist_append(NULL, &sms);
+
+       wap_push = sms_decode_datagram(list, &data_len);
+
+       if (g_test_verbose()) {
+               int i;
+
+               g_print("data_len: %ld\n", data_len);
+
+               for (i = 0; i < data_len; i++) {
+                       g_print("%02x", wap_push[i]);
+
+                       if ((i % 16) == 15)
+                               g_print("\n");
+               }
+
+               g_print("\n");
+       }
+
+       g_assert(wap_push);
+
+       g_free(wap_push);
+       g_slist_free(list);
+}
+
+int main(int argc, char **argv)
+{
+       char long_string[152*33 + 1];
+       struct sms_concat_data long_string_test;
+
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/testsms/Test Simple Deliver", test_simple_deliver);
+       g_test_add_func("/testsms/Test Alnum Deliver", test_alnum_sender);
+       g_test_add_func("/testsms/Test Deliver Encode", test_deliver_encode);
+       g_test_add_func("/testsms/Test Simple Submit", test_simple_submit);
+       g_test_add_func("/testsms/Test Submit Encode", test_submit_encode);
+
+       g_test_add_data_func("/testsms/Test "
+               "GSM 7 bit Default Alphabet Decode",
+               &sms_charset_default, test_sms_charset);
+
+       g_test_add_data_func("/testsms/Test "
+               "GSM 7 bit Default Alphabet Extension Table Decode",
+               &sms_charset_default_ext, test_sms_charset);
+
+       g_test_add_data_func("/testsms/Test "
+               "Turkish National Language Locking Shift Table Decode",
+               &sms_charset_turkey, test_sms_charset);
+
+       g_test_add_data_func("/testsms/Test "
+               "Turkish National Language Single Shift Table Decode",
+               &sms_charset_turkey_ext, test_sms_charset);
+
+       g_test_add_data_func("/testsms/Test "
+               "Portuguese National Language Locking Shift Table Decode",
+               &sms_charset_portugal, test_sms_charset);
+
+       g_test_add_data_func("/testsms/Test "
+               "Portuguese National Language Single Shift Table Decode",
+               &sms_charset_portugal_ext, test_sms_charset);
+
+       g_test_add_data_func("/testsms/Test "
+               "Spanish National Language Single Shift Table Decode",
+               &sms_charset_spain, test_sms_charset);
+
+       g_test_add_data_func("/testsms/Test EMS UDH 1",
+                       &ems_udh_test_1, test_ems_udh);
+       g_test_add_data_func("/testsms/Test EMS UDH 2",
+                       &ems_udh_test_2, test_ems_udh);
+
+       g_test_add_func("/testsms/Test Assembly", test_assembly);
+       g_test_add_func("/testsms/Test Prepare 7Bit", test_prepare_7bit);
+
+       g_test_add_data_func("/testsms/Test Prepare Concat",
+                       &shakespeare_test, test_prepare_concat);
+
+       memset(long_string, 'a', 152*30);
+       memset(long_string + 152*30, 'b', 152);
+       memset(long_string + 152*31, 'c', 152);
+       memset(long_string + 152*32, 'd', 152);
+       long_string[152*33] = '\0';
+
+       long_string_test.str = long_string;
+       long_string_test.segments = 33;
+
+       g_test_add_data_func("/testsms/Test Prepare Concat 30+ segments",
+                       &long_string_test, test_prepare_concat);
+
+       g_test_add_func("/testsms/Test Prepare Limits", test_prepare_limits);
+
+       g_test_add_func("/testsms/Test CBS Encode / Decode",
+                       test_cbs_encode_decode);
+       g_test_add_func("/testsms/Test CBS Assembly", test_cbs_assembly);
+
+       g_test_add_func("/testsms/Range minimizer", test_range_minimizer);
+
+       g_test_add_func("/testsms/Status Report Assembly", test_sr_assembly);
+
+       g_test_add_data_func("/testsms/Test WAP Push 1", &wap_push_1,
+                               test_wap_push);
+
+       return g_test_run();
+}
diff --git a/unit/test-stkutil.c b/unit/test-stkutil.c
new file mode 100644 (file)
index 0000000..b0bea5f
--- /dev/null
@@ -0,0 +1,26131 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include <ofono/types.h>
+#include "smsutil.h"
+#include "stkutil.h"
+#include "util.h"
+
+#define MAX_ITEM 100
+
+struct sms_submit_test {
+       gboolean rd;
+       enum sms_validity_period_format vpf;
+       gboolean rp;
+       gboolean udhi;
+       gboolean srr;
+       guint8 mr;
+       struct sms_address daddr;
+       guint8 pid;
+       guint8 dcs;
+       struct sms_validity_period vp;
+       guint8 udl;
+       guint8 ud[160];
+};
+
+struct sms_test {
+       struct sms_address sc_addr;
+       enum sms_type type;
+       union {
+               struct sms_deliver deliver;
+               struct sms_deliver_ack_report deliver_ack_report;
+               struct sms_deliver_err_report deliver_err_report;
+               struct sms_submit_test submit;
+               struct sms_submit_ack_report submit_ack_report;
+               struct sms_submit_err_report submit_err_report;
+               struct sms_command command;
+               struct sms_status_report status_report;
+       };
+};
+
+static gboolean g_mem_equal(const unsigned char *v1, const unsigned char *v2,
+                               unsigned int len)
+{
+       unsigned int i;
+
+       for (i = 0; i < len; i++)
+               if (v1[i] != v2[i])
+                       return FALSE;
+
+       return TRUE;
+}
+
+static inline void check_common_bool(const ofono_bool_t command,
+                                       const ofono_bool_t test)
+{
+       g_assert(command == test);
+}
+
+static inline void check_common_byte(const unsigned char command,
+                                       const unsigned char test)
+{
+       g_assert(command == test);
+}
+
+static inline void check_common_text(const char *command, const char *test)
+{
+       if (test == NULL) {
+               g_assert(command == NULL);
+               return;
+       }
+
+       g_assert(command != NULL);
+       g_assert(g_str_equal(command, test));
+}
+
+static inline void check_common_byte_array(
+                               const struct stk_common_byte_array *command,
+                               const struct stk_common_byte_array *test)
+{
+       if (test->len == 0) {
+               g_assert(command->len == 0);
+               return;
+       }
+
+       g_assert(command->len != 0);
+       g_assert(command->len == test->len);
+       g_assert(g_mem_equal(command->array, test->array, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.1 */
+static inline void check_address(const struct stk_address *command,
+                                       const struct stk_address *test)
+{
+       g_assert(command->ton_npi == test->ton_npi);
+       check_common_text(command->number, test->number);
+}
+
+/* Defined in TS 102.223 Section 8.2 */
+static inline void check_alpha_id(const char *command, const char *test)
+{
+       if (test != NULL && strlen(test) > 0)
+               check_common_text(command, test);
+       else
+               g_assert(command == NULL);
+}
+
+/* Defined in TS 102.223 Section 8.3 */
+static void check_subaddress(const struct stk_subaddress *command,
+                                       const struct stk_subaddress *test)
+{
+       if (test->len == 0) {
+               g_assert(command->len == 0);
+               return;
+       }
+
+       g_assert(command->len != 0);
+       g_assert(g_mem_equal(command->subaddr, test->subaddr, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.4 */
+static void check_ccp(const struct stk_ccp *command,
+                                       const struct stk_ccp *test)
+{
+       if (test->len == 0) {
+               g_assert(command->len == 0);
+               return;
+       }
+
+       g_assert(command->len != 0);
+       g_assert(g_mem_equal(command->ccp, test->ccp, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.8 */
+static void check_duration(const struct stk_duration *command,
+                                       const struct stk_duration *test)
+{
+       g_assert(command->unit == test->unit);
+       g_assert(command->interval == test->interval);
+}
+
+/* Defined in TS 102.223 Section 8.9 */
+static void check_item(const struct stk_item *command,
+                                       const struct stk_item *test)
+{
+       g_assert(command->id == test->id);
+       check_common_text(command->text, test->text);
+}
+
+/* Defined in TS 102.223 Section 8.10 */
+static inline void check_item_id(const unsigned char command,
+                                       const unsigned char test)
+{
+       check_common_byte(command, test);
+}
+
+static void check_items(GSList *command, const struct stk_item *test)
+{
+       struct stk_item *si;
+       GSList *l;
+       unsigned int i = 0;
+
+       for (l = command; l; l = l->next) {
+               si = l->data;
+               check_item(si, &test[i++]);
+       }
+
+       g_assert(test[i].id == 0);
+}
+
+/* Defined in TS 102.223 Section 8.11 */
+static void check_response_length(const struct stk_response_length *command,
+                                       const struct stk_response_length *test)
+{
+       g_assert(command->min == test->min);
+       g_assert(command->max == test->max);
+}
+
+/* Defined in TS 102.223 Section 8.13 */
+static void check_gsm_sms(const struct sms *command,
+                                       const struct sms_test *test)
+{
+       g_assert(command->sc_addr.number_type == test->sc_addr.number_type);
+       g_assert(command->sc_addr.numbering_plan ==
+                       test->sc_addr.numbering_plan);
+       g_assert(g_str_equal(command->sc_addr.address, test->sc_addr.address));
+
+       switch (test->type) {
+       case SMS_TYPE_SUBMIT: {
+               const struct sms_submit *cs = &command->submit;
+               const struct sms_submit_test *ts = &test->submit;
+               enum sms_charset charset;
+
+               g_assert(cs->rd == ts->rd);
+               g_assert(cs->vpf == ts->vpf);
+               g_assert(cs->rp == ts->rp);
+               g_assert(cs->udhi == ts->udhi);
+               g_assert(cs->srr == ts->srr);
+               g_assert(cs->mr == ts->mr);
+
+               g_assert(cs->daddr.number_type == ts->daddr.number_type);
+               g_assert(cs->daddr.numbering_plan == ts->daddr.numbering_plan);
+               g_assert(g_str_equal(cs->daddr.address, ts->daddr.address));
+
+               g_assert(cs->pid == ts->pid);
+               g_assert(cs->dcs == ts->dcs);
+
+               switch (ts->vpf) {
+               case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE:
+                       g_assert(cs->vp.relative == ts->vp.relative);
+                       break;
+               case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE: {
+                       const struct sms_scts *ca = &cs->vp.absolute;
+                       const struct sms_scts *ta = &ts->vp.absolute;
+                       g_assert(ca->year == ta->year);
+                       g_assert(ca->month == ta->month);
+                       g_assert(ca->day == ta->day);
+                       g_assert(ca->hour == ta->hour);
+                       g_assert(ca->minute == ta->minute);
+                       g_assert(ca->second == ta->second);
+                       g_assert(ca->has_timezone == ta->has_timezone);
+
+                       if (ta->has_timezone)
+                               g_assert(ca->timezone == ta->timezone);
+
+                       break;
+               }
+               case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED:
+                       g_assert(g_mem_equal(cs->vp.enhanced,
+                                                       ts->vp.enhanced, 7));
+                       break;
+               default:
+                       break;
+               }
+
+               g_assert(cs->udl == ts->udl);
+
+               sms_dcs_decode(ts->dcs, NULL, &charset, NULL, NULL);
+
+               if (charset == SMS_CHARSET_8BIT)
+                       g_assert(g_str_equal(cs->ud, ts->ud));
+               else {
+                       GSList *sms_list = NULL;
+                       char *message;
+                       sms_list = g_slist_prepend(sms_list, (void *)command);
+                       message = sms_decode_text(sms_list);
+                       g_assert(g_str_equal(message, ts->ud));
+                       g_free(message);
+               }
+
+               break;
+       }
+       default:
+               g_assert(FALSE);
+       }
+}
+
+/* Defined in TS 102.223 Section 8.14 */
+static inline void check_ss(const struct stk_ss *command,
+                                       const struct stk_ss *test)
+{
+       g_assert(command->ton_npi == test->ton_npi);
+       check_common_text(command->ss, test->ss);
+}
+
+/* Defined in TS 102.223 Section 8.15 */
+static inline void check_text(const char *command, const char *test)
+{
+       check_common_text(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.16 */
+static inline void check_tone(const ofono_bool_t command,
+                                       const ofono_bool_t test)
+{
+       check_common_bool(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.17 */
+static inline void check_ussd(const struct stk_ussd_string *command,
+                                                       const char *test)
+{
+       char *utf8 = ussd_decode(command->dcs, command->len, command->string);
+       check_common_text(utf8, test);
+       g_free(utf8);
+}
+
+/* Defined in TS 102.223 Section 8.18 */
+static void check_file_list(GSList *command, const struct stk_file *test)
+{
+       struct stk_file *sf;
+       GSList *l;
+       unsigned int i = 0;
+
+       for (l = command; l; l = l->next) {
+               sf = l->data;
+               g_assert(sf->len == test[i].len);
+               g_assert(g_mem_equal(sf->file, test[i++].file, sf->len));
+       }
+
+       g_assert(test[i].len == 0);
+}
+
+/* Defined in TS 102.223 Section 8.23 */
+static inline void check_default_text(const char *command, const char *test)
+{
+       check_common_text(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.24 */
+static void check_items_next_action_indicator(
+                       const struct stk_items_next_action_indicator *command,
+                       const struct stk_items_next_action_indicator *test)
+{
+       g_assert(command->len == test->len);
+       g_assert(g_mem_equal(command->list, test->list, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.25 */
+static void check_event_list(const struct stk_event_list *command,
+                               const struct stk_event_list *test)
+{
+       g_assert(command->len == test->len);
+       g_assert(g_mem_equal(command->list, test->list, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.31 */
+static void check_icon_id(const struct stk_icon_id *command,
+                                       const struct stk_icon_id *test)
+{
+       g_assert(command->id == test->id);
+       g_assert(command->qualifier == test->qualifier);
+}
+
+/* Defined in TS 102.223 Section 8.32 */
+static void check_item_icon_id_list(const struct stk_item_icon_id_list *command,
+                               const struct stk_item_icon_id_list *test)
+{
+       g_assert(command->qualifier == test->qualifier);
+       g_assert(command->len == test->len);
+       g_assert(g_mem_equal(command->list, test->list, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.35 */
+static void check_c_apdu(const struct stk_c_apdu *command,
+                               const struct stk_c_apdu *test)
+{
+       g_assert(command->cla == test->cla);
+       g_assert(command->ins == test->ins);
+       g_assert(command->p1 == test->p1);
+       g_assert(command->p2 == test->p2);
+       g_assert(command->lc == test->lc);
+       g_assert(g_mem_equal(command->data, test->data, test->lc));
+
+       if (test->has_le)
+               g_assert(command->le == test->le);
+}
+
+/* Defined in TS 102.223 Section 8.37 */
+static inline void check_timer_id(const unsigned char command,
+                                       const unsigned char test)
+{
+       check_common_byte(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.38 */
+static inline void check_timer_value(const struct stk_timer_value *command,
+                                       const struct stk_timer_value *test)
+{
+       g_assert(command->hour == test->hour);
+       g_assert(command->minute == test->minute);
+       g_assert(command->second == test->second);
+}
+
+/* Defined in TS 102.223 Section 8.40 */
+static inline void check_at_command(const char *command, const char *test)
+{
+       check_common_text(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.43 */
+static inline void check_imm_resp(const unsigned char command,
+                                       const unsigned char test)
+{
+       check_common_byte(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.44 */
+static inline void check_dtmf_string(const char *command, const char *test)
+{
+       check_common_text(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.45 */
+static inline void check_language(const char *command, const char *test)
+{
+       check_common_text(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.47 */
+static inline void check_browser_id(const unsigned char command,
+                                       const unsigned char test)
+{
+       check_common_byte(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.48 */
+static inline void check_url(const char *command, const char *test)
+{
+       check_common_text(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.49 */
+static inline void check_bearer(const struct stk_common_byte_array *command,
+                               const struct stk_common_byte_array *test)
+{
+       check_common_byte_array(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.50 */
+static void check_provisioning_file_reference(const struct stk_file *command,
+                                       const struct stk_file *test)
+{
+       g_assert(command->len == test->len);
+       g_assert(g_mem_equal(command->file, test->file, test->len));
+}
+
+static void check_provisioning_file_references(GSList *command,
+                                               const struct stk_file *test)
+{
+       struct stk_file *sf;
+       GSList *l;
+       unsigned int i = 0;
+
+       for (l = command; l; l = l->next) {
+               sf = l->data;
+               check_provisioning_file_reference(sf, &test[i++]);
+       }
+
+       g_assert(test[i].len == 0);
+}
+
+/* Defined in TS 102.223 Section 8.52 */
+static void check_bearer_desc(const struct stk_bearer_description *command,
+                               const struct stk_bearer_description *test)
+{
+       g_assert(command->type == test->type);
+
+       if (test->type == STK_BEARER_TYPE_GPRS_UTRAN) {
+               check_common_byte(command->gprs.precedence,
+                               test->gprs.precedence);
+               check_common_byte(command->gprs.delay,
+                               test->gprs.delay);
+               check_common_byte(command->gprs.reliability,
+                               test->gprs.reliability);
+               check_common_byte(command->gprs.peak,
+                               test->gprs.peak);
+               check_common_byte(command->gprs.mean,
+                               test->gprs.mean);
+               check_common_byte(command->gprs.pdp_type,
+                               test->gprs.pdp_type);
+
+               return;
+       }
+}
+
+/* Defined in TS 102.223 Section 8.53 */
+static inline void check_channel_data(
+                               const struct stk_common_byte_array *command,
+                               const struct stk_common_byte_array *test)
+{
+       check_common_byte_array(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.58 */
+static inline void check_other_address(
+                               const struct stk_other_address *command,
+                               const struct stk_other_address *test)
+{
+       check_common_byte(command->type, test->type);
+
+       if (test->type == STK_ADDRESS_IPV4)
+               g_assert(command->addr.ipv4 == test->addr.ipv4);
+       else
+               g_assert(g_mem_equal(command->addr.ipv6, test->addr.ipv6, 16));
+}
+
+/* Defined in TS 102.223 Section 8.59 */
+static void check_uicc_te_interface(const struct stk_uicc_te_interface *command,
+                               const struct stk_uicc_te_interface *test)
+{
+       check_common_byte(command->protocol, test->protocol);
+       g_assert(command->port == test->port);
+}
+
+/* Defined in TS 102.223 Section 8.60 */
+static inline void check_aid(const struct stk_aid *command,
+                                       const struct stk_aid *test)
+{
+       g_assert(g_mem_equal(command->aid, test->aid, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.70 */
+static inline void check_network_access_name(const char *command,
+                                               const char *test)
+{
+       check_common_text(command, test);
+}
+
+/* Defined in TS 102.223 Section 8.71 */
+static inline void check_cdma_sms_tpdu(
+                               const struct stk_common_byte_array *command,
+                               const struct stk_common_byte_array *test)
+{
+       check_common_byte_array(command, test);
+}
+
+static void check_text_attr_html(const struct stk_text_attribute *test,
+                               char *text, const char *expected_html)
+{
+       char *html;
+       unsigned short attrs[256];
+       int i;
+
+       if (expected_html == NULL)
+               return;
+
+       for (i = 0; i < test->len; i += 4) {
+               attrs[i] = test->attributes[i];
+               attrs[i + 1] = test->attributes[i + 1];
+               attrs[i + 2] = test->attributes[i + 2];
+               attrs[i + 3] = test->attributes[i + 3];
+       }
+       html = stk_text_to_html(text, attrs, test->len / 4);
+
+       g_assert(memcmp(html, expected_html, strlen(expected_html)) == 0);
+
+       g_free(html);
+}
+
+/* Defined in TS 102.223 Section 8.72 */
+static void check_text_attr(const struct stk_text_attribute *command,
+                                       const struct stk_text_attribute *test)
+{
+       g_assert(command->len == test->len);
+       g_assert(g_mem_equal(command->attributes, test->attributes, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.73 */
+static void check_item_text_attribute_list(
+                       const struct stk_item_text_attribute_list *command,
+                       const struct stk_item_text_attribute_list *test)
+{
+       g_assert(command->len == test->len);
+       g_assert(g_mem_equal(command->list, test->list, test->len));
+}
+
+/* Defined in TS 102.223 Section 8.80 */
+static void check_frame_id(const struct stk_frame_id *command,
+                                       const struct stk_frame_id *test)
+{
+       g_assert(command->has_id == test->has_id);
+       if (test->has_id)
+               g_assert(command->id == test->id);
+}
+
+struct display_text_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       const char *text;
+       struct stk_icon_id icon_id;
+       ofono_bool_t immediate_response;
+       struct stk_duration duration;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       const char *html;
+};
+
+unsigned char display_text_111[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F,
+                                       0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B,
+                                       0x69, 0x74, 0x20, 0x54, 0x65, 0x73,
+                                       0x74, 0x20, 0x31 };
+
+unsigned char display_text_131[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x81,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F,
+                                       0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B,
+                                       0x69, 0x74, 0x20, 0x54, 0x65, 0x73,
+                                       0x74, 0x20, 0x32 };
+
+unsigned char display_text_141[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0E,
+                                       0x00, 0xD4, 0xF7, 0x9B, 0xBD, 0x4E,
+                                       0xD3, 0x41, 0xD4, 0xF2, 0x9C, 0x0E,
+                                       0x9A, 0x01 };
+
+unsigned char display_text_151[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x00,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F,
+                                       0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B,
+                                       0x69, 0x74, 0x20, 0x54, 0x65, 0x73,
+                                       0x74, 0x20, 0x34 };
+
+unsigned char display_text_161[] = { 0xD0, 0x81, 0xAD, 0x81, 0x03, 0x01, 0x21,
+                                       0x80, 0x82, 0x02, 0x81, 0x02, 0x8D,
+                                       0x81, 0xA1, 0x04, 0x54, 0x68, 0x69,
+                                       0x73, 0x20, 0x63, 0x6F, 0x6D, 0x6D,
+                                       0x61, 0x6E, 0x64, 0x20, 0x69, 0x6E,
+                                       0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
+                                       0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
+                                       0x4D, 0x45, 0x20, 0x74, 0x6F, 0x20,
+                                       0x64, 0x69, 0x73, 0x70, 0x6C, 0x61,
+                                       0x79, 0x20, 0x61, 0x20, 0x74, 0x65,
+                                       0x78, 0x74, 0x20, 0x6D, 0x65, 0x73,
+                                       0x73, 0x61, 0x67, 0x65, 0x2E, 0x20,
+                                       0x49, 0x74, 0x20, 0x61, 0x6C, 0x6C,
+                                       0x6F, 0x77, 0x73, 0x20, 0x74, 0x68,
+                                       0x65, 0x20, 0x53, 0x49, 0x4D, 0x20,
+                                       0x74, 0x6F, 0x20, 0x64, 0x65, 0x66,
+                                       0x69, 0x6E, 0x65, 0x20, 0x74, 0x68,
+                                       0x65, 0x20, 0x70, 0x72, 0x69, 0x6F,
+                                       0x72, 0x69, 0x74, 0x79, 0x20, 0x6F,
+                                       0x66, 0x20, 0x74, 0x68, 0x61, 0x74,
+                                       0x20, 0x6D, 0x65, 0x73, 0x73, 0x61,
+                                       0x67, 0x65, 0x2C, 0x20, 0x61, 0x6E,
+                                       0x64, 0x20, 0x74, 0x68, 0x65, 0x20,
+                                       0x74, 0x65, 0x78, 0x74, 0x20, 0x73,
+                                       0x74, 0x72, 0x69, 0x6E, 0x67, 0x20,
+                                       0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74,
+                                       0x2E, 0x20, 0x54, 0x77, 0x6F, 0x20,
+                                       0x74, 0x79, 0x70, 0x65, 0x73, 0x20,
+                                       0x6F, 0x66, 0x20, 0x70, 0x72, 0x69,
+                                       0x6F };
+
+unsigned char display_text_171[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F,
+                                       0x04, 0x3C, 0x47, 0x4F, 0x2D, 0x42,
+                                       0x41, 0x43, 0x4B, 0x57, 0x41, 0x52,
+                                       0x44, 0x53, 0x3E };
+
+unsigned char display_text_511[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0B,
+                                       0x04, 0x42, 0x61, 0x73, 0x69, 0x63,
+                                       0x20, 0x49, 0x63, 0x6F, 0x6E, 0x9E,
+                                       0x02, 0x00, 0x01 };
+
+unsigned char display_text_521[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0C,
+                                       0x04, 0x43, 0x6F, 0x6C, 0x6F, 0x75,
+                                       0x72, 0x20, 0x49, 0x63, 0x6F, 0x6E,
+                                       0x9E, 0x02, 0x00, 0x02 };
+
+unsigned char display_text_531[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0B,
+                                       0x04, 0x42, 0x61, 0x73, 0x69, 0x63,
+                                       0x20, 0x49, 0x63, 0x6F, 0x6E, 0x9E,
+                                       0x02, 0x01, 0x01 };
+
+unsigned char display_text_611[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x19,
+                                       0x08, 0x04, 0x17, 0x04, 0x14, 0x04,
+                                       0x20, 0x04, 0x10, 0x04, 0x12, 0x04,
+                                       0x21, 0x04, 0x22, 0x04, 0x12, 0x04,
+                                       0x23, 0x04, 0x19, 0x04, 0x22, 0x04,
+                                       0x15 };
+
+unsigned char display_text_711[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x0A,
+                                       0x04, 0x31, 0x30, 0x20, 0x53, 0x65,
+                                       0x63, 0x6F, 0x6E, 0x64, 0x84, 0x02,
+                                       0x01, 0x0A };
+
+unsigned char display_text_811[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+unsigned char display_text_821[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x01, 0xB4 };
+
+unsigned char display_text_831[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x02, 0xB4 };
+
+unsigned char display_text_841[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x04, 0xB4 };
+
+unsigned char display_text_851[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x08, 0xB4 };
+
+unsigned char display_text_861[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x10, 0xB4 };
+
+unsigned char display_text_871[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x20, 0xB4 };
+
+unsigned char display_text_881[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x40, 0xB4 };
+
+unsigned char display_text_891[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x11,
+                                       0x04, 0x54, 0x65, 0x78, 0x74, 0x20,
+                                       0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
+                                       0x75, 0x74, 0x65, 0x20, 0x31, 0xD0,
+                                       0x04, 0x00, 0x10, 0x80, 0xB4 };
+
+unsigned char display_text_911[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x05,
+                                       0x08, 0x4F, 0x60, 0x59, 0x7D };
+
+unsigned char display_text_1011[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x21, 0x80,
+                                       0x82, 0x02, 0x81, 0x02, 0x8D, 0x07,
+                                       0x08, 0x00, 0x38, 0x00, 0x30, 0x30,
+                                       0xEB };
+
+static struct display_text_test display_text_data_111 = {
+       .pdu = display_text_111,
+       .pdu_len = sizeof(display_text_111),
+       .qualifier = 0x80,
+       .text = "Toolkit Test 1"
+};
+
+static struct display_text_test display_text_data_131 = {
+       .pdu = display_text_131,
+       .pdu_len = sizeof(display_text_131),
+       .qualifier = 0x81,
+       .text = "Toolkit Test 2"
+};
+
+static struct display_text_test display_text_data_141 = {
+       .pdu = display_text_141,
+       .pdu_len = sizeof(display_text_141),
+       .qualifier = 0x80,
+       .text = "Toolkit Test 3"
+};
+
+static struct display_text_test display_text_data_151 = {
+       .pdu = display_text_151,
+       .pdu_len = sizeof(display_text_151),
+       .qualifier = 0x00,
+       .text = "Toolkit Test 4"
+};
+
+static struct display_text_test display_text_data_161 = {
+       .pdu = display_text_161,
+       .pdu_len = sizeof(display_text_161),
+       .qualifier = 0x80,
+       .text = "This command instructs the ME to display a text message. "
+                       "It allows the SIM to define the priority of that "
+                       "message, and the text string format. Two types of "
+                       "prio"
+};
+
+static struct display_text_test display_text_data_171 = {
+       .pdu = display_text_171,
+       .pdu_len = sizeof(display_text_171),
+       .qualifier = 0x80,
+       .text = "<GO-BACKWARDS>"
+};
+
+static struct display_text_test display_text_data_511 = {
+       .pdu = display_text_511,
+       .pdu_len = sizeof(display_text_511),
+       .qualifier = 0x80,
+       .text = "Basic Icon",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct display_text_test display_text_data_521 = {
+       .pdu = display_text_521,
+       .pdu_len = sizeof(display_text_521),
+       .qualifier = 0x80,
+       .text = "Colour Icon",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct display_text_test display_text_data_531 = {
+       .pdu = display_text_531,
+       .pdu_len = sizeof(display_text_531),
+       .qualifier = 0x80,
+       .text = "Basic Icon",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct display_text_test display_text_data_611 = {
+       .pdu = display_text_611,
+       .pdu_len = sizeof(display_text_611),
+       .qualifier = 0x80,
+       .text = "ЗДРАВСТВУЙТЕ"
+};
+
+static struct display_text_test display_text_data_711 = {
+       .pdu = display_text_711,
+       .pdu_len = sizeof(display_text_711),
+       .qualifier = 0x80,
+       .text = "10 Second",
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 10,
+       }
+};
+
+static struct display_text_test display_text_data_811 = {
+       .pdu = display_text_811,
+       .pdu_len = sizeof(display_text_811),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 },
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 1</span>"
+               "</div>",
+};
+
+static struct display_text_test display_text_data_821 = {
+       .pdu = display_text_821,
+       .pdu_len = sizeof(display_text_821),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 },
+       },
+       .html = "<div style=\"text-align: center;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 1</span>"
+               "</div>",
+};
+
+static struct display_text_test display_text_data_831 = {
+       .pdu = display_text_831,
+       .pdu_len = sizeof(display_text_831),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 },
+       },
+       .html = "<div style=\"text-align: right;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 1</span>"
+               "</div>",
+};
+
+static struct display_text_test display_text_data_841 = {
+       .pdu = display_text_841,
+       .pdu_len = sizeof(display_text_841),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 },
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "big;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute 1</span></div>",
+};
+
+static struct display_text_test display_text_data_851 = {
+       .pdu = display_text_851,
+       .pdu_len = sizeof(display_text_851),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 },
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "small;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute 1</span></div>",
+};
+
+static struct display_text_test display_text_data_861 = {
+       .pdu = display_text_861,
+       .pdu_len = sizeof(display_text_861),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x10, 0xB4 },
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-weight: "
+               "bold;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute 1</span></div>",
+};
+
+static struct display_text_test display_text_data_871 = {
+       .pdu = display_text_871,
+       .pdu_len = sizeof(display_text_871),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x20, 0xB4 },
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-style: "
+               "italic;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute 1</span>",
+};
+
+static struct display_text_test display_text_data_881 = {
+       .pdu = display_text_881,
+       .pdu_len = sizeof(display_text_881),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 },
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\""
+               "text-decoration: underline;color: #347235;"
+               "background-color: #FFFF00;\">Text Attribute 1</span></div>",
+};
+
+static struct display_text_test display_text_data_891 = {
+       .pdu = display_text_891,
+       .pdu_len = sizeof(display_text_891),
+       .qualifier = 0x80,
+       .text = "Text Attribute 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 },
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\""
+               "text-decoration: line-through;color: #347235;"
+               "background-color: #FFFF00;\">Text Attribute 1</span>",
+};
+
+static struct display_text_test display_text_data_911 = {
+       .pdu = display_text_911,
+       .pdu_len = sizeof(display_text_911),
+       .qualifier = 0x80,
+       .text = "你好"
+};
+
+static struct display_text_test display_text_data_1011 = {
+       .pdu = display_text_1011,
+       .pdu_len = sizeof(display_text_1011),
+       .qualifier = 0x80,
+       .text = "80ル"
+};
+
+/* Defined in TS 102.384 Section 27.22.4.1 */
+static void test_display_text(gconstpointer data)
+{
+       const struct display_text_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_DISPLAY_TEXT);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_DISPLAY);
+
+       g_assert(command->display_text.text);
+       check_text(command->display_text.text, test->text);
+       check_icon_id(&command->display_text.icon_id, &test->icon_id);
+       check_imm_resp(command->display_text.immediate_response,
+                                               test->immediate_response);
+       check_duration(&command->display_text.duration, &test->duration);
+       check_text_attr(&command->display_text.text_attr,
+                                               &test->text_attr);
+       check_text_attr_html(&command->display_text.text_attr,
+                               command->display_text.text,
+                               test->html);
+       check_frame_id(&command->display_text.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct get_inkey_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *text;
+       struct stk_icon_id icon_id;
+       struct stk_duration duration;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       char *html;
+};
+
+static unsigned char get_inkey_111[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22 };
+
+static unsigned char get_inkey_121[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x09, 0x00, 0x45, 0x37,
+                                               0xBD, 0x2C, 0x07, 0x89, 0x60,
+                                               0x22 };
+
+static unsigned char get_inkey_131[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0F, 0x04, 0x3C, 0x47,
+                                               0x4F, 0x2D, 0x42, 0x41, 0x43,
+                                               0x4B, 0x57, 0x41, 0x52, 0x44,
+                                               0x53, 0x3E };
+
+static unsigned char get_inkey_141[] = { 0xD0, 0x13, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x08, 0x04, 0x3C, 0x41,
+                                               0x42, 0x4F, 0x52, 0x54, 0x3E };
+
+static unsigned char get_inkey_151[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x71, 0x22 };
+
+static unsigned char get_inkey_161[] = { 0xD0, 0x81, 0xAD, 0x81, 0x03, 0x01,
+                                               0x22, 0x01, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0xA1, 0x04,
+                                               0x45, 0x6E, 0x74, 0x65, 0x72,
+                                               0x20, 0x22, 0x78, 0x22, 0x2E,
+                                               0x20, 0x54, 0x68, 0x69, 0x73,
+                                               0x20, 0x63, 0x6F, 0x6D, 0x6D,
+                                               0x61, 0x6E, 0x64, 0x20, 0x69,
+                                               0x6E, 0x73, 0x74, 0x72, 0x75,
+                                               0x63, 0x74, 0x73, 0x20, 0x74,
+                                               0x68, 0x65, 0x20, 0x4D, 0x45,
+                                               0x20, 0x74, 0x6F, 0x20, 0x64,
+                                               0x69, 0x73, 0x70, 0x6C, 0x61,
+                                               0x79, 0x20, 0x74, 0x65, 0x78,
+                                               0x74, 0x2C, 0x20, 0x61, 0x6E,
+                                               0x64, 0x20, 0x74, 0x6F, 0x20,
+                                               0x65, 0x78, 0x70, 0x65, 0x63,
+                                               0x74, 0x20, 0x74, 0x68, 0x65,
+                                               0x20, 0x75, 0x73, 0x65, 0x72,
+                                               0x20, 0x74, 0x6F, 0x20, 0x65,
+                                               0x6E, 0x74, 0x65, 0x72, 0x20,
+                                               0x61, 0x20, 0x73, 0x69, 0x6E,
+                                               0x67, 0x6C, 0x65, 0x20, 0x63,
+                                               0x68, 0x61, 0x72, 0x61, 0x63,
+                                               0x74, 0x65, 0x72, 0x2E, 0x20,
+                                               0x41, 0x6E, 0x79, 0x20, 0x72,
+                                               0x65, 0x73, 0x70, 0x6F, 0x6E,
+                                               0x73, 0x65, 0x20, 0x65, 0x6E,
+                                               0x74, 0x65, 0x72, 0x65, 0x64,
+                                               0x20, 0x62, 0x79, 0x20, 0x74,
+                                               0x68, 0x65, 0x20, 0x75, 0x73,
+                                               0x65, 0x72, 0x20, 0x73, 0x68,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x62,
+                                               0x65, 0x20, 0x70, 0x61, 0x73,
+                                               0x73, 0x65, 0x64, 0x20, 0x74 };
+
+static unsigned char get_inkey_211[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0B, 0x04, 0x3C, 0x54,
+                                               0x49, 0x4D, 0x45, 0x2D, 0x4F,
+                                               0x55, 0x54, 0x3E };
+
+static unsigned char get_inkey_311[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x19, 0x08, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15 };
+
+static unsigned char get_inkey_321[] = { 0xD0, 0x81, 0x99, 0x81, 0x03, 0x01,
+                                               0x22, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0x8D, 0x08,
+                                               0x04, 0x17, 0x04, 0x14, 0x04,
+                                               0x20, 0x04, 0x10, 0x04, 0x12,
+                                               0x04, 0x21, 0x04, 0x22, 0x04,
+                                               0x12, 0x04, 0x23, 0x04, 0x19,
+                                               0x04, 0x22, 0x04, 0x15, 0x04,
+                                               0x17, 0x04, 0x14, 0x04, 0x20,
+                                               0x04, 0x10, 0x04, 0x12, 0x04,
+                                               0x21, 0x04, 0x22, 0x04, 0x12,
+                                               0x04, 0x23, 0x04, 0x19, 0x04,
+                                               0x22, 0x04, 0x15, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x04, 0x17, 0x04, 0x14, 0x04,
+                                               0x20, 0x04, 0x10, 0x04, 0x12,
+                                               0x04, 0x21, 0x04, 0x22, 0x04,
+                                               0x12, 0x04, 0x23, 0x04, 0x19 };
+
+static unsigned char get_inkey_411[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x22,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x06, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72 };
+
+static unsigned char get_inkey_511[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x04, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x59,
+                                               0x45, 0x53 };
+
+static unsigned char get_inkey_512[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x22,
+                                               0x04, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x09, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x4E,
+                                               0x4F };
+
+static unsigned char get_inkey_611[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x3C, 0x4E,
+                                               0x4F, 0x2D, 0x49, 0x43, 0x4F,
+                                               0x4E, 0x3E, 0x1E, 0x02, 0x00,
+                                               0x01 };
+
+static unsigned char get_inkey_621[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0D, 0x04, 0x3C, 0x42,
+                                               0x41, 0x53, 0x49, 0x43, 0x2D,
+                                               0x49, 0x43, 0x4F, 0x4E, 0x3E,
+                                               0x1E, 0x02, 0x01, 0x01 };
+
+static unsigned char get_inkey_631[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x3C, 0x4E,
+                                               0x4F, 0x2D, 0x49, 0x43, 0x4F,
+                                               0x4E, 0x3E, 0x1E, 0x02, 0x00,
+                                               0x02 };
+
+static unsigned char get_inkey_641[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0E, 0x04, 0x3C, 0x43,
+                                               0x4F, 0x4C, 0x4F, 0x55, 0x52,
+                                               0x2D, 0x49, 0x43, 0x4F, 0x4E,
+                                               0x3E, 0x1E, 0x02, 0x01, 0x02 };
+
+static unsigned char get_inkey_711[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x80, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22 };
+
+static unsigned char get_inkey_712[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x80, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22 };
+
+static unsigned char get_inkey_811[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0x84, 0x02, 0x01,
+                                               0x0A };
+
+static unsigned char get_inkey_911[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_912[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_921[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x01, 0xB4 };
+
+static unsigned char get_inkey_922[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_931[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x02, 0xB4 };
+
+static unsigned char get_inkey_932[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_941[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x04, 0xB4 };
+
+static unsigned char get_inkey_942[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_943[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_951[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x08, 0xB4 };
+
+static unsigned char get_inkey_952[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_953[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_961[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x10, 0xB4 };
+
+static unsigned char get_inkey_962[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_963[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_971[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x20, 0xB4 };
+
+static unsigned char get_inkey_972[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_973[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_981[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x40, 0xB4 };
+
+static unsigned char get_inkey_982[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_983[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_991[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x80, 0xB4 };
+
+static unsigned char get_inkey_992a[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_992b[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_993[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_9101[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x2B, 0x22, 0xD0, 0x04, 0x00,
+                                               0x09, 0x00, 0xB4 };
+
+static unsigned char get_inkey_9102[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x22,
+                                               0x23, 0x22 };
+
+static unsigned char get_inkey_1011[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x05, 0x08, 0x4F, 0x60,
+                                               0x59, 0x7D };
+
+static unsigned char get_inkey_1021[] = { 0xD0, 0x81, 0x99, 0x81, 0x03, 0x01,
+                                               0x22, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0x8D, 0x08,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D };
+
+static unsigned char get_inkey_1111[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x22,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x06, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72 };
+
+static unsigned char get_inkey_1211[] = { 0xD0, 0x0E, 0x81, 0x03, 0x01, 0x22,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x03, 0x08, 0x30, 0xEB };
+
+static unsigned char get_inkey_1221[] = { 0xD0, 0x81, 0x99, 0x81, 0x03, 0x01,
+                                               0x22, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0x8D, 0x08,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB };
+
+static unsigned char get_inkey_1311[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x22,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x06, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72 };
+
+static struct get_inkey_test get_inkey_data_111 = {
+       .pdu = get_inkey_111,
+       .pdu_len = sizeof(get_inkey_111),
+       .qualifier = 0x00,
+       .text = "Enter \"+\""
+};
+
+static struct get_inkey_test get_inkey_data_121 = {
+       .pdu = get_inkey_121,
+       .pdu_len = sizeof(get_inkey_121),
+       .qualifier = 0x00,
+       .text = "Enter \"0\""
+};
+
+static struct get_inkey_test get_inkey_data_131 = {
+       .pdu = get_inkey_131,
+       .pdu_len = sizeof(get_inkey_131),
+       .qualifier = 0x00,
+       .text = "<GO-BACKWARDS>"
+};
+
+static struct get_inkey_test get_inkey_data_141 = {
+       .pdu = get_inkey_141,
+       .pdu_len = sizeof(get_inkey_141),
+       .qualifier = 0x00,
+       .text = "<ABORT>"
+};
+
+static struct get_inkey_test get_inkey_data_151 = {
+       .pdu = get_inkey_151,
+       .pdu_len = sizeof(get_inkey_151),
+       .qualifier = 0x01,
+       .text = "Enter \"q\""
+};
+
+static struct get_inkey_test get_inkey_data_161 = {
+       .pdu = get_inkey_161,
+       .pdu_len = sizeof(get_inkey_161),
+       .qualifier = 0x01,
+       .text = "Enter \"x\". This command instructs the ME to display text, "
+               "and to expect the user to enter a single character. Any "
+               "response entered by the user shall be passed t"
+};
+
+static struct get_inkey_test get_inkey_data_211 = {
+       .pdu = get_inkey_211,
+       .pdu_len = sizeof(get_inkey_211),
+       .qualifier = 0x00,
+       .text = "<TIME-OUT>"
+};
+
+static struct get_inkey_test get_inkey_data_311 = {
+       .pdu = get_inkey_311,
+       .pdu_len = sizeof(get_inkey_311),
+       .qualifier = 0x00,
+       .text = "ЗДРАВСТВУЙТЕ"
+};
+
+static struct get_inkey_test get_inkey_data_321 = {
+       .pdu = get_inkey_321,
+       .pdu_len = sizeof(get_inkey_321),
+       .qualifier = 0x00,
+       .text = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ"
+               "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ"
+               "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ"
+};
+
+static struct get_inkey_test get_inkey_data_411 = {
+       .pdu = get_inkey_411,
+       .pdu_len = sizeof(get_inkey_411),
+       .qualifier = 0x03,
+       .text = "Enter"
+};
+
+static struct get_inkey_test get_inkey_data_511 = {
+       .pdu = get_inkey_511,
+       .pdu_len = sizeof(get_inkey_511),
+       .qualifier = 0x04,
+       .text = "Enter YES"
+};
+
+static struct get_inkey_test get_inkey_data_512 = {
+       .pdu = get_inkey_512,
+       .pdu_len = sizeof(get_inkey_512),
+       .qualifier = 0x04,
+       .text = "Enter NO"
+};
+
+static struct get_inkey_test get_inkey_data_611 = {
+       .pdu = get_inkey_611,
+       .pdu_len = sizeof(get_inkey_611),
+       .qualifier = 0x00,
+       .text = "<NO-ICON>",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct get_inkey_test get_inkey_data_621 = {
+       .pdu = get_inkey_621,
+       .pdu_len = sizeof(get_inkey_621),
+       .qualifier = 0x00,
+       .text = "<BASIC-ICON>",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct get_inkey_test get_inkey_data_631 = {
+       .pdu = get_inkey_631,
+       .pdu_len = sizeof(get_inkey_631),
+       .qualifier = 0x00,
+       .text = "<NO-ICON>",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct get_inkey_test get_inkey_data_641 = {
+       .pdu = get_inkey_641,
+       .pdu_len = sizeof(get_inkey_641),
+       .qualifier = 0x00,
+       .text = "<COLOUR-ICON>",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct get_inkey_test get_inkey_data_711 = {
+       .pdu = get_inkey_711,
+       .pdu_len = sizeof(get_inkey_711),
+       .qualifier = 0x80,
+       .text = "Enter \"+\""
+};
+
+static struct get_inkey_test get_inkey_data_712 = {
+       .pdu = get_inkey_712,
+       .pdu_len = sizeof(get_inkey_712),
+       .qualifier = 0x80,
+       .text = "Enter \"+\""
+};
+
+static struct get_inkey_test get_inkey_data_811 = {
+       .pdu = get_inkey_811,
+       .pdu_len = sizeof(get_inkey_811),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 10
+       }
+};
+
+static struct get_inkey_test get_inkey_data_911 = {
+       .pdu = get_inkey_911,
+       .pdu_len = sizeof(get_inkey_911),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"+\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_912 = {
+       .pdu = get_inkey_912,
+       .pdu_len = sizeof(get_inkey_912),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_921 = {
+       .pdu = get_inkey_921,
+       .pdu_len = sizeof(get_inkey_921),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x01, 0xB4 }
+       },
+       .html = "<div style=\"text-align: center;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"+\"</span>"
+               "</div>",
+};
+
+static struct get_inkey_test get_inkey_data_922 = {
+       .pdu = get_inkey_922,
+       .pdu_len = sizeof(get_inkey_922),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_931 = {
+       .pdu = get_inkey_931,
+       .pdu_len = sizeof(get_inkey_931),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x02, 0xB4 }
+       },
+       .html = "<div style=\"text-align: right;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"+\"</span>"
+               "</div>",
+};
+
+static struct get_inkey_test get_inkey_data_932 = {
+       .pdu = get_inkey_932,
+       .pdu_len = sizeof(get_inkey_932),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_941 = {
+       .pdu = get_inkey_941,
+       .pdu_len = sizeof(get_inkey_941),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x04, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "big;color: #347235;background-color: #FFFF00;\">Enter \"+\""
+               "</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_942 = {
+       .pdu = get_inkey_942,
+       .pdu_len = sizeof(get_inkey_942),
+       .qualifier = 0x00,
+       .text = "Enter \"#\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"#\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_943 = {
+       .pdu = get_inkey_943,
+       .pdu_len = sizeof(get_inkey_943),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_951 = {
+       .pdu = get_inkey_951,
+       .pdu_len = sizeof(get_inkey_951),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x08, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "small;color: #347235;background-color: #FFFF00;\">"
+               "Enter \"+\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_952 = {
+       .pdu = get_inkey_952,
+       .pdu_len = sizeof(get_inkey_952),
+       .qualifier = 0x00,
+       .text = "Enter \"#\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"#\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_953 = {
+       .pdu = get_inkey_953,
+       .pdu_len = sizeof(get_inkey_953),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_961 = {
+       .pdu = get_inkey_961,
+       .pdu_len = sizeof(get_inkey_961),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x10, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-weight: "
+               "bold;color: #347235;background-color: #FFFF00;\">Enter \"+\""
+               "</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_962 = {
+       .pdu = get_inkey_962,
+       .pdu_len = sizeof(get_inkey_962),
+       .qualifier = 0x00,
+       .text = "Enter \"#\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"#\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_963 = {
+       .pdu = get_inkey_963,
+       .pdu_len = sizeof(get_inkey_963),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_971 = {
+       .pdu = get_inkey_971,
+       .pdu_len = sizeof(get_inkey_971),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x20, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-style: "
+               "italic;color: #347235;background-color: #FFFF00;\">"
+               "Enter \"+\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_972 = {
+       .pdu = get_inkey_972,
+       .pdu_len = sizeof(get_inkey_972),
+       .qualifier = 0x00,
+       .text = "Enter \"#\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"#\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_973 = {
+       .pdu = get_inkey_973,
+       .pdu_len = sizeof(get_inkey_973),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_981 = {
+       .pdu = get_inkey_981,
+       .pdu_len = sizeof(get_inkey_981),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x40, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\""
+               "text-decoration: underline;color: #347235;"
+               "background-color: #FFFF00;\">Enter \"+\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_982 = {
+       .pdu = get_inkey_982,
+       .pdu_len = sizeof(get_inkey_982),
+       .qualifier = 0x00,
+       .text = "Enter \"#\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"#\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_983 = {
+       .pdu = get_inkey_983,
+       .pdu_len = sizeof(get_inkey_983),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_991 = {
+       .pdu = get_inkey_991,
+       .pdu_len = sizeof(get_inkey_991),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x80, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\""
+               "text-decoration: line-through;color: #347235;"
+               "background-color: #FFFF00;\">Enter \"+\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_992a = {
+       .pdu = get_inkey_992a,
+       .pdu_len = sizeof(get_inkey_992a),
+       .qualifier = 0x00,
+       .text = "Enter \"#\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"#\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_992b = {
+       .pdu = get_inkey_992b,
+       .pdu_len = sizeof(get_inkey_992b),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_993 = {
+       .pdu = get_inkey_993,
+       .pdu_len = sizeof(get_inkey_993),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_9101 = {
+       .pdu = get_inkey_9101,
+       .pdu_len = sizeof(get_inkey_9101),
+       .qualifier = 0x00,
+       .text = "Enter \"+\"",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter \"+\"</span></div>",
+};
+
+static struct get_inkey_test get_inkey_data_9102 = {
+       .pdu = get_inkey_9102,
+       .pdu_len = sizeof(get_inkey_9102),
+       .qualifier = 0x00,
+       .text = "Enter \"#\""
+};
+
+static struct get_inkey_test get_inkey_data_1011 = {
+       .pdu = get_inkey_1011,
+       .pdu_len = sizeof(get_inkey_1011),
+       .qualifier = 0x00,
+       .text = "你好"
+};
+
+static struct get_inkey_test get_inkey_data_1021 = {
+       .pdu = get_inkey_1021,
+       .pdu_len = sizeof(get_inkey_1021),
+       .qualifier = 0x00,
+       .text = "你好你好你好你好你好你好你好你好你好你好"
+               "你好你好你好你好你好你好你好你好你好你好"
+               "你好你好你好你好你好你好你好你好你好你好"
+               "你好你好你好你好你好"
+};
+
+static struct get_inkey_test get_inkey_data_1111 = {
+       .pdu = get_inkey_1111,
+       .pdu_len = sizeof(get_inkey_1111),
+       .qualifier = 0x03,
+       .text = "Enter"
+};
+
+static struct get_inkey_test get_inkey_data_1211 = {
+       .pdu = get_inkey_1211,
+       .pdu_len = sizeof(get_inkey_1211),
+       .qualifier = 0x00,
+       .text = "ル"
+};
+
+static struct get_inkey_test get_inkey_data_1221 = {
+       .pdu = get_inkey_1221,
+       .pdu_len = sizeof(get_inkey_1221),
+       .qualifier = 0x00,
+       .text = "ルルルルルルルルルルルルルルルルルルルル"
+               "ルルルルルルルルルルルルルルルルルルルル"
+               "ルルルルルルルルルルルルルルルルルルルル"
+               "ルルルルルルルルルル"
+};
+
+static struct get_inkey_test get_inkey_data_1311 = {
+       .pdu = get_inkey_1311,
+       .pdu_len = sizeof(get_inkey_1311),
+       .qualifier = 0x03,
+       .text = "Enter"
+};
+
+/* Defined in TS 102.384 Section 27.22.4.2 */
+static void test_get_inkey(gconstpointer data)
+{
+       const struct get_inkey_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_GET_INKEY);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       g_assert(command->get_inkey.text);
+       check_text(command->get_inkey.text, test->text);
+       check_icon_id(&command->get_inkey.icon_id, &test->icon_id);
+       check_duration(&command->get_inkey.duration, &test->duration);
+       check_text_attr(&command->get_inkey.text_attr,
+                                               &test->text_attr);
+       check_text_attr_html(&command->get_inkey.text_attr,
+                               command->get_inkey.text, test->html);
+       check_frame_id(&command->get_inkey.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct get_input_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *text;
+       struct stk_response_length resp_len;
+       char *default_text;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       char *html;
+};
+
+static unsigned char get_input_111[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_121[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x23,
+                                               0x08, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0B, 0x00, 0x45, 0x37,
+                                               0xBD, 0x2C, 0x07, 0xD9, 0x6E,
+                                               0xAA, 0xD1, 0x0A, 0x91, 0x02,
+                                               0x05, 0x05 };
+
+static unsigned char get_input_131[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x41,
+                                               0x62, 0x43, 0x64, 0x45, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_141[] = { 0xD0, 0x27, 0x81, 0x03, 0x01, 0x23,
+                                               0x04, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x18, 0x04, 0x50, 0x61,
+                                               0x73, 0x73, 0x77, 0x6F, 0x72,
+                                               0x64, 0x20, 0x31, 0x3C, 0x53,
+                                               0x45, 0x4E, 0x44, 0x3E, 0x32,
+                                               0x33, 0x34, 0x35, 0x36, 0x37,
+                                               0x38, 0x91, 0x02, 0x04, 0x08 };
+
+static unsigned char get_input_151[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x15, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x2E, 0x2E, 0x39, 0x2C, 0x30,
+                                               0x2E, 0x2E, 0x39, 0x2C, 0x30,
+                                               0x28, 0x31, 0x29, 0x91, 0x02,
+                                               0x01, 0x14 };
+
+static unsigned char get_input_161[] = { 0xD0, 0x1E, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0F, 0x04, 0x3C, 0x47,
+                                               0x4F, 0x2D, 0x42, 0x41, 0x43,
+                                               0x4B, 0x57, 0x41, 0x52, 0x44,
+                                               0x53, 0x3E, 0x91, 0x02, 0x00,
+                                               0x08 };
+
+static unsigned char get_input_171[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x08, 0x04, 0x3C, 0x41,
+                                               0x42, 0x4F, 0x52, 0x54, 0x3E,
+                                               0x91, 0x02, 0x00, 0x08 };
+
+static unsigned char get_input_181[] = { 0xD0, 0x81, 0xB1, 0x81, 0x03, 0x01,
+                                               0x23, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0xA1, 0x04,
+                                               0x2A, 0x2A, 0x2A, 0x31, 0x31,
+                                               0x31, 0x31, 0x31, 0x31, 0x31,
+                                               0x31, 0x31, 0x31, 0x23, 0x23,
+                                               0x23, 0x2A, 0x2A, 0x2A, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x23,
+                                               0x23, 0x23, 0x2A, 0x2A, 0x2A,
+                                               0x33, 0x33, 0x33, 0x33, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x33,
+                                               0x23, 0x23, 0x23, 0x2A, 0x2A,
+                                               0x2A, 0x34, 0x34, 0x34, 0x34,
+                                               0x34, 0x34, 0x34, 0x34, 0x34,
+                                               0x34, 0x23, 0x23, 0x23, 0x2A,
+                                               0x2A, 0x2A, 0x35, 0x35, 0x35,
+                                               0x35, 0x35, 0x35, 0x35, 0x35,
+                                               0x35, 0x35, 0x23, 0x23, 0x23,
+                                               0x2A, 0x2A, 0x2A, 0x36, 0x36,
+                                               0x36, 0x36, 0x36, 0x36, 0x36,
+                                               0x36, 0x36, 0x36, 0x23, 0x23,
+                                               0x23, 0x2A, 0x2A, 0x2A, 0x37,
+                                               0x37, 0x37, 0x37, 0x37, 0x37,
+                                               0x37, 0x37, 0x37, 0x37, 0x23,
+                                               0x23, 0x23, 0x2A, 0x2A, 0x2A,
+                                               0x38, 0x38, 0x38, 0x38, 0x38,
+                                               0x38, 0x38, 0x38, 0x38, 0x38,
+                                               0x23, 0x23, 0x23, 0x2A, 0x2A,
+                                               0x2A, 0x39, 0x39, 0x39, 0x39,
+                                               0x39, 0x39, 0x39, 0x39, 0x39,
+                                               0x39, 0x23, 0x23, 0x23, 0x2A,
+                                               0x2A, 0x2A, 0x30, 0x30, 0x30,
+                                               0x30, 0x30, 0x30, 0x30, 0x30,
+                                               0x30, 0x30, 0x23, 0x23, 0x23,
+                                               0x91, 0x02, 0xA0, 0xA0 };
+
+static unsigned char get_input_191[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x07, 0x04, 0x3C, 0x53,
+                                               0x45, 0x4E, 0x44, 0x3E, 0x91,
+                                               0x02, 0x00, 0x01 };
+
+static unsigned char get_input_1101[] = { 0xD0, 0x0F, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x00, 0x91, 0x02, 0x01,
+                                               0x05 };
+
+static unsigned char get_input_211[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0B, 0x04, 0x3C, 0x54,
+                                               0x49, 0x4D, 0x45, 0x2D, 0x4F,
+                                               0x55, 0x54, 0x3E, 0x91, 0x02,
+                                               0x00, 0x0A };
+
+static unsigned char get_input_311[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x23,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x19, 0x08, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x91, 0x02, 0x05,
+                                               0x05 };
+
+static unsigned char get_input_321[] = { 0xD0, 0x81, 0x9D, 0x81, 0x03, 0x01,
+                                               0x23, 0x01, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0x8D, 0x08,
+                                               0x04, 0x17, 0x04, 0x14, 0x04,
+                                               0x20, 0x04, 0x10, 0x04, 0x12,
+                                               0x04, 0x21, 0x04, 0x22, 0x04,
+                                               0x12, 0x04, 0x23, 0x04, 0x19,
+                                               0x04, 0x22, 0x04, 0x15, 0x04,
+                                               0x17, 0x04, 0x14, 0x04, 0x20,
+                                               0x04, 0x10, 0x04, 0x12, 0x04,
+                                               0x21, 0x04, 0x22, 0x04, 0x12,
+                                               0x04, 0x23, 0x04, 0x19, 0x04,
+                                               0x22, 0x04, 0x15, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x04, 0x17, 0x04, 0x14, 0x04,
+                                               0x20, 0x04, 0x10, 0x04, 0x12,
+                                               0x04, 0x21, 0x04, 0x22, 0x04,
+                                               0x12, 0x04, 0x23, 0x04, 0x19,
+                                               0x91, 0x02, 0x05, 0x05 };
+
+static unsigned char get_input_411[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x48,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x91,
+                                               0x02, 0x0C, 0x0C };
+
+static unsigned char get_input_421[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x48,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x91,
+                                               0x02, 0x05, 0xFF };
+
+static unsigned char get_input_511[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0x17, 0x06,
+                                               0x04, 0x31, 0x32, 0x33, 0x34,
+                                               0x35 };
+
+static unsigned char get_input_521[] = { 0xD0, 0x81, 0xBA, 0x81, 0x03, 0x01,
+                                               0x23, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x07, 0x04, 0x45,
+                                               0x6E, 0x74, 0x65, 0x72, 0x3A,
+                                               0x91, 0x02, 0xA0, 0xA0, 0x17,
+                                               0x81, 0xA1, 0x04, 0x2A, 0x2A,
+                                               0x2A, 0x31, 0x31, 0x31, 0x31,
+                                               0x31, 0x31, 0x31, 0x31, 0x31,
+                                               0x31, 0x23, 0x23, 0x23, 0x2A,
+                                               0x2A, 0x2A, 0x32, 0x32, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x32,
+                                               0x32, 0x32, 0x23, 0x23, 0x23,
+                                               0x2A, 0x2A, 0x2A, 0x33, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x33,
+                                               0x33, 0x33, 0x33, 0x23, 0x23,
+                                               0x23, 0x2A, 0x2A, 0x2A, 0x34,
+                                               0x34, 0x34, 0x34, 0x34, 0x34,
+                                               0x34, 0x34, 0x34, 0x34, 0x23,
+                                               0x23, 0x23, 0x2A, 0x2A, 0x2A,
+                                               0x35, 0x35, 0x35, 0x35, 0x35,
+                                               0x35, 0x35, 0x35, 0x35, 0x35,
+                                               0x23, 0x23, 0x23, 0x2A, 0x2A,
+                                               0x2A, 0x36, 0x36, 0x36, 0x36,
+                                               0x36, 0x36, 0x36, 0x36, 0x36,
+                                               0x36, 0x23, 0x23, 0x23, 0x2A,
+                                               0x2A, 0x2A, 0x37, 0x37, 0x37,
+                                               0x37, 0x37, 0x37, 0x37, 0x37,
+                                               0x37, 0x37, 0x23, 0x23, 0x23,
+                                               0x2A, 0x2A, 0x2A, 0x38, 0x38,
+                                               0x38, 0x38, 0x38, 0x38, 0x38,
+                                               0x38, 0x38, 0x38, 0x23, 0x23,
+                                               0x23, 0x2A, 0x2A, 0x2A, 0x39,
+                                               0x39, 0x39, 0x39, 0x39, 0x39,
+                                               0x39, 0x39, 0x39, 0x39, 0x23,
+                                               0x23, 0x23, 0x2A, 0x2A, 0x2A,
+                                               0x30, 0x30, 0x30, 0x30, 0x30,
+                                               0x30, 0x30, 0x30, 0x30, 0x30,
+                                               0x23, 0x23, 0x23 };
+
+static unsigned char get_input_611[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x3C, 0x4E,
+                                               0x4F, 0x2D, 0x49, 0x43, 0x4F,
+                                               0x4E, 0x3E, 0x91, 0x02, 0x00,
+                                               0x0A, 0x1E, 0x02, 0x00, 0x01 };
+
+static unsigned char get_input_621[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0D, 0x04, 0x3C, 0x42,
+                                               0x41, 0x53, 0x49, 0x43, 0x2D,
+                                               0x49, 0x43, 0x4F, 0x4E, 0x3E,
+                                               0x91, 0x02, 0x00, 0x0A, 0x1E,
+                                               0x02, 0x01, 0x01 };
+
+static unsigned char get_input_631[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0A, 0x04, 0x3C, 0x4E,
+                                               0x4F, 0x2D, 0x49, 0x43, 0x4F,
+                                               0x4E, 0x3E, 0x91, 0x02, 0x00,
+                                               0x0A, 0x1E, 0x02, 0x00, 0x02 };
+
+static unsigned char get_input_641[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0E, 0x04, 0x3C, 0x43,
+                                               0x4F, 0x4C, 0x4F, 0x55, 0x52,
+                                               0x2D, 0x49, 0x43, 0x4F, 0x4E,
+                                               0x3E, 0x91, 0x02, 0x00, 0x0A,
+                                               0x1E, 0x02, 0x01, 0x02 };
+
+static unsigned char get_input_711[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x80, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_811[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_812[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_821[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x01, 0xB4 };
+
+static unsigned char get_input_822[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_831[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x02, 0xB4 };
+
+static unsigned char get_input_832[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_841[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x04, 0xB4 };
+
+static unsigned char get_input_842[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_843[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_851[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x08, 0xB4 };
+
+static unsigned char get_input_852[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_853[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_861[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x10, 0xB4 };
+
+static unsigned char get_input_862[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_863[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_871[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x20, 0xB4 };
+
+static unsigned char get_input_872[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_873[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_881[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x40, 0xB4 };
+
+static unsigned char get_input_882[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_883[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_891[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x80, 0xB4 };
+
+static unsigned char get_input_892[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_893[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x33,
+                                               0x33, 0x33, 0x33, 0x33, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_8101[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x91,
+                                               0x02, 0x05, 0x05, 0xD0, 0x04,
+                                               0x00, 0x0B, 0x00, 0xB4 };
+
+static unsigned char get_input_8102[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x32,
+                                               0x32, 0x32, 0x32, 0x32, 0x91,
+                                               0x02, 0x05, 0x05 };
+
+static unsigned char get_input_911[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x23,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x05, 0x08, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x91, 0x02, 0x05,
+                                               0x05 };
+
+static unsigned char get_input_921[] = { 0xD0, 0x81, 0x9D, 0x81, 0x03, 0x01,
+                                               0x23, 0x01, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0x8D, 0x08,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x4F, 0x60, 0x59, 0x7D, 0x4F,
+                                               0x60, 0x59, 0x7D, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x4F, 0x60, 0x59,
+                                               0x7D, 0x4F, 0x60, 0x59, 0x7D,
+                                               0x91, 0x02, 0x05, 0x05 };
+
+static unsigned char get_input_1011[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x48,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x91,
+                                               0x02, 0x02, 0x02 };
+
+static unsigned char get_input_1021[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x48,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x91,
+                                               0x02, 0x05, 0xFF };
+
+static unsigned char get_input_1111[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x23,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x03, 0x08, 0x30, 0xEB,
+                                               0x91, 0x02, 0x05, 0x05 };
+
+static unsigned char get_input_1121[] = { 0xD0, 0x81, 0x9D, 0x81, 0x03, 0x01,
+                                               0x23, 0x01, 0x82, 0x02, 0x81,
+                                               0x82, 0x8D, 0x81, 0x8D, 0x08,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x30, 0xEB, 0x30, 0xEB, 0x30,
+                                               0xEB, 0x30, 0xEB, 0x30, 0xEB,
+                                               0x91, 0x02, 0x05, 0x05 };
+
+static unsigned char get_input_1211[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x48,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x91,
+                                               0x02, 0x02, 0x02 };
+
+static unsigned char get_input_1221[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x8D, 0x0C, 0x04, 0x45, 0x6E,
+                                               0x74, 0x65, 0x72, 0x20, 0x48,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x91,
+                                               0x02, 0x05, 0xFF };
+
+static struct get_input_test get_input_data_111 = {
+       .pdu = get_input_111,
+       .pdu_len = sizeof(get_input_111),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_121 = {
+       .pdu = get_input_121,
+       .pdu_len = sizeof(get_input_121),
+       .qualifier = 0x08,
+       .text = "Enter 67*#+",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_131 = {
+       .pdu = get_input_131,
+       .pdu_len = sizeof(get_input_131),
+       .qualifier = 0x01,
+       .text = "Enter AbCdE",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_141 = {
+       .pdu = get_input_141,
+       .pdu_len = sizeof(get_input_141),
+       .qualifier = 0x04,
+       .text = "Password 1<SEND>2345678",
+       .resp_len = {
+               .min = 4,
+               .max = 8
+       }
+};
+
+static struct get_input_test get_input_data_151 = {
+       .pdu = get_input_151,
+       .pdu_len = sizeof(get_input_151),
+       .qualifier = 0x00,
+       .text = "Enter 1..9,0..9,0(1)",
+       .resp_len = {
+               .min = 1,
+               .max = 20
+       }
+};
+
+static struct get_input_test get_input_data_161 = {
+       .pdu = get_input_161,
+       .pdu_len = sizeof(get_input_161),
+       .qualifier = 0x00,
+       .text = "<GO-BACKWARDS>",
+       .resp_len = {
+               .min = 0,
+               .max = 8
+       }
+};
+
+static struct get_input_test get_input_data_171 = {
+       .pdu = get_input_171,
+       .pdu_len = sizeof(get_input_171),
+       .qualifier = 0x00,
+       .text = "<ABORT>",
+       .resp_len = {
+               .min = 0,
+               .max = 8
+       }
+};
+
+static struct get_input_test get_input_data_181 = {
+       .pdu = get_input_181,
+       .pdu_len = sizeof(get_input_181),
+       .qualifier = 0x00,
+       .text = "***1111111111###***2222222222###***3333333333###"
+               "***4444444444###***5555555555###***6666666666###"
+               "***7777777777###***8888888888###***9999999999###"
+               "***0000000000###",
+       .resp_len = {
+               .min = 160,
+               .max = 160
+       }
+};
+
+static struct get_input_test get_input_data_191 = {
+       .pdu = get_input_191,
+       .pdu_len = sizeof(get_input_191),
+       .qualifier = 0x00,
+       .text = "<SEND>",
+       .resp_len = {
+               .min = 0,
+               .max = 1
+       }
+};
+
+static struct get_input_test get_input_data_1101 = {
+       .pdu = get_input_1101,
+       .pdu_len = sizeof(get_input_1101),
+       .qualifier = 0x00,
+       .text = "",
+       .resp_len = {
+               .min = 1,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_211 = {
+       .pdu = get_input_211,
+       .pdu_len = sizeof(get_input_211),
+       .qualifier = 0x00,
+       .text = "<TIME-OUT>",
+       .resp_len = {
+               .min = 0,
+               .max = 10
+       }
+};
+
+static struct get_input_test get_input_data_311 = {
+       .pdu = get_input_311,
+       .pdu_len = sizeof(get_input_311),
+       .qualifier = 0x01,
+       .text = "ЗДРАВСТВУЙТЕ",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_321 = {
+       .pdu = get_input_321,
+       .pdu_len = sizeof(get_input_321),
+       .qualifier = 0x01,
+       .text = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ"
+               "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ"
+               "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_411 = {
+       .pdu = get_input_411,
+       .pdu_len = sizeof(get_input_411),
+       .qualifier = 0x03,
+       .text = "Enter Hello",
+       .resp_len = {
+               .min = 12,
+               .max = 12
+       }
+};
+
+static struct get_input_test get_input_data_421 = {
+       .pdu = get_input_421,
+       .pdu_len = sizeof(get_input_421),
+       .qualifier = 0x03,
+       .text = "Enter Hello",
+       .resp_len = {
+               .min = 5,
+               .max = 0xFF
+       }
+};
+
+static struct get_input_test get_input_data_511 = {
+       .pdu = get_input_511,
+       .pdu_len = sizeof(get_input_511),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .default_text = "12345"
+};
+
+static struct get_input_test get_input_data_521 = {
+       .pdu = get_input_521,
+       .pdu_len = sizeof(get_input_521),
+       .qualifier = 0x00,
+       .text = "Enter:",
+       .resp_len = {
+               .min = 160,
+               .max = 160
+       },
+       .default_text = "***1111111111###***2222222222###***3333333333###"
+                       "***4444444444###***5555555555###***6666666666###"
+                       "***7777777777###***8888888888###***9999999999###"
+                       "***0000000000###"
+};
+
+static struct get_input_test get_input_data_611 = {
+       .pdu = get_input_611,
+       .pdu_len = sizeof(get_input_611),
+       .qualifier = 0x00,
+       .text = "<NO-ICON>",
+       .resp_len = {
+               .min = 0,
+               .max = 10
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct get_input_test get_input_data_621 = {
+       .pdu = get_input_621,
+       .pdu_len = sizeof(get_input_621),
+       .qualifier = 0x00,
+       .text = "<BASIC-ICON>",
+       .resp_len = {
+               .min = 0,
+               .max = 10
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct get_input_test get_input_data_631 = {
+       .pdu = get_input_631,
+       .pdu_len = sizeof(get_input_631),
+       .qualifier = 0x00,
+       .text = "<NO-ICON>",
+       .resp_len = {
+               .min = 0,
+               .max = 10
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct get_input_test get_input_data_641 = {
+       .pdu = get_input_641,
+       .pdu_len = sizeof(get_input_641),
+       .qualifier = 0x00,
+       .text = "<COLOUR-ICON>",
+       .resp_len = {
+               .min = 0,
+               .max = 10
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct get_input_test get_input_data_711 = {
+       .pdu = get_input_711,
+       .pdu_len = sizeof(get_input_711),
+       .qualifier = 0x80,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_811 = {
+       .pdu = get_input_811,
+       .pdu_len = sizeof(get_input_811),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 12345</span></div>"
+};
+
+static struct get_input_test get_input_data_812 = {
+       .pdu = get_input_812,
+       .pdu_len = sizeof(get_input_812),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_821 = {
+       .pdu = get_input_821,
+       .pdu_len = sizeof(get_input_821),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x01, 0xB4 }
+       },
+       .html = "<div style=\"text-align: center;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 12345</span>"
+               "</div>",
+};
+
+static struct get_input_test get_input_data_822 = {
+       .pdu = get_input_822,
+       .pdu_len = sizeof(get_input_822),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_831 = {
+       .pdu = get_input_831,
+       .pdu_len = sizeof(get_input_831),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x02, 0xB4 }
+       },
+       .html = "<div style=\"text-align: right;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 12345</span>"
+               "</div>",
+};
+
+static struct get_input_test get_input_data_832 = {
+       .pdu = get_input_832,
+       .pdu_len = sizeof(get_input_832),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_841 = {
+       .pdu = get_input_841,
+       .pdu_len = sizeof(get_input_841),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x04, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "big;color: #347235;background-color: #FFFF00;\">Enter 12345"
+               "</span></div>",
+};
+
+static struct get_input_test get_input_data_842 = {
+       .pdu = get_input_842,
+       .pdu_len = sizeof(get_input_842),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 22222</span></div>"
+};
+
+static struct get_input_test get_input_data_843 = {
+       .pdu = get_input_843,
+       .pdu_len = sizeof(get_input_843),
+       .qualifier = 0x00,
+       .text = "Enter 33333",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_851 = {
+       .pdu = get_input_851,
+       .pdu_len = sizeof(get_input_851),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x08, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "small;color: #347235;background-color: #FFFF00;\">Enter "
+               "12345</span></div>",
+};
+
+static struct get_input_test get_input_data_852 = {
+       .pdu = get_input_852,
+       .pdu_len = sizeof(get_input_852),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 22222</span></div>",
+};
+
+static struct get_input_test get_input_data_853 = {
+       .pdu = get_input_853,
+       .pdu_len = sizeof(get_input_853),
+       .qualifier = 0x00,
+       .text = "Enter 33333",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_861 = {
+       .pdu = get_input_861,
+       .pdu_len = sizeof(get_input_861),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x10, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-weight: "
+               "bold;color: #347235;background-color: #FFFF00;\">Enter "
+               "12345</span></div>"
+};
+
+static struct get_input_test get_input_data_862 = {
+       .pdu = get_input_862,
+       .pdu_len = sizeof(get_input_862),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 22222</span></div>",
+};
+
+static struct get_input_test get_input_data_863 = {
+       .pdu = get_input_863,
+       .pdu_len = sizeof(get_input_863),
+       .qualifier = 0x00,
+       .text = "Enter 33333",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_871 = {
+       .pdu = get_input_871,
+       .pdu_len = sizeof(get_input_871),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x20, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-style: "
+               "italic;color: #347235;background-color: #FFFF00;\">Enter "
+               "12345</span></div>",
+};
+
+static struct get_input_test get_input_data_872 = {
+       .pdu = get_input_872,
+       .pdu_len = sizeof(get_input_872),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 22222</span></div>",
+};
+
+static struct get_input_test get_input_data_873 = {
+       .pdu = get_input_873,
+       .pdu_len = sizeof(get_input_873),
+       .qualifier = 0x00,
+       .text = "Enter 33333",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_881 = {
+       .pdu = get_input_881,
+       .pdu_len = sizeof(get_input_881),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x40, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: underline;color: #347235;"
+               "background-color: #FFFF00;\">Enter 12345</span></div>",
+};
+
+static struct get_input_test get_input_data_882 = {
+       .pdu = get_input_882,
+       .pdu_len = sizeof(get_input_882),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 22222</span></div>",
+};
+
+static struct get_input_test get_input_data_883 = {
+       .pdu = get_input_883,
+       .pdu_len = sizeof(get_input_883),
+       .qualifier = 0x00,
+       .text = "Enter 33333",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_891 = {
+       .pdu = get_input_891,
+       .pdu_len = sizeof(get_input_891),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x80, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: line-through;color: #347235;"
+               "background-color: #FFFF00;\">Enter 12345</span></div>",
+};
+
+static struct get_input_test get_input_data_892 = {
+       .pdu = get_input_892,
+       .pdu_len = sizeof(get_input_892),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 22222</span></div>",
+};
+
+static struct get_input_test get_input_data_893 = {
+       .pdu = get_input_893,
+       .pdu_len = sizeof(get_input_893),
+       .qualifier = 0x00,
+       .text = "Enter 33333",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_8101 = {
+       .pdu = get_input_8101,
+       .pdu_len = sizeof(get_input_8101),
+       .qualifier = 0x00,
+       .text = "Enter 12345",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Enter 12345</span></div>",
+};
+
+static struct get_input_test get_input_data_8102 = {
+       .pdu = get_input_8102,
+       .pdu_len = sizeof(get_input_8102),
+       .qualifier = 0x00,
+       .text = "Enter 22222",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_911 = {
+       .pdu = get_input_911,
+       .pdu_len = sizeof(get_input_911),
+       .qualifier = 0x01,
+       .text = "你好",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_921 = {
+       .pdu = get_input_921,
+       .pdu_len = sizeof(get_input_921),
+       .qualifier = 0x01,
+       .text = "你好你好你好你好你好你好你好你好你好你好"
+               "你好你好你好你好你好你好你好你好你好你好"
+               "你好你好你好你好你好你好你好你好你好你好"
+               "你好你好你好你好你好",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_1011 = {
+       .pdu = get_input_1011,
+       .pdu_len = sizeof(get_input_1011),
+       .qualifier = 0x03,
+       .text = "Enter Hello",
+       .resp_len = {
+               .min = 2,
+               .max = 2
+       }
+};
+
+static struct get_input_test get_input_data_1021 = {
+       .pdu = get_input_1021,
+       .pdu_len = sizeof(get_input_1021),
+       .qualifier = 0x03,
+       .text = "Enter Hello",
+       .resp_len = {
+               .min = 5,
+               .max = 0xFF
+       }
+};
+
+static struct get_input_test get_input_data_1111 = {
+       .pdu = get_input_1111,
+       .pdu_len = sizeof(get_input_1111),
+       .qualifier = 0x01,
+       .text = "ル",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_1121 = {
+       .pdu = get_input_1121,
+       .pdu_len = sizeof(get_input_1121),
+       .qualifier = 0x01,
+       .text = "ルルルルルルルルルルルルルルルルルルルル"
+               "ルルルルルルルルルルルルルルルルルルルル"
+               "ルルルルルルルルルルルルルルルルルルルル"
+               "ルルルルルルルルルル",
+       .resp_len = {
+               .min = 5,
+               .max = 5
+       }
+};
+
+static struct get_input_test get_input_data_1211 = {
+       .pdu = get_input_1211,
+       .pdu_len = sizeof(get_input_1211),
+       .qualifier = 0x03,
+       .text = "Enter Hello",
+       .resp_len = {
+               .min = 2,
+               .max = 2
+       }
+};
+
+static struct get_input_test get_input_data_1221 = {
+       .pdu = get_input_1221,
+       .pdu_len = sizeof(get_input_1221),
+       .qualifier = 0x03,
+       .text = "Enter Hello",
+       .resp_len = {
+               .min = 5,
+               .max = 0xFF
+       }
+};
+
+/* Defined in TS 102.384 Section 27.22.4.3 */
+static void test_get_input(gconstpointer data)
+{
+       const struct get_input_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_GET_INPUT);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       if (test->text)
+               g_assert(command->get_input.text);
+       check_text(command->get_input.text, test->text);
+       check_response_length(&command->get_input.resp_len, &test->resp_len);
+       check_default_text(command->get_input.default_text, test->default_text);
+       check_icon_id(&command->get_input.icon_id, &test->icon_id);
+       check_text_attr(&command->get_input.text_attr,
+                                               &test->text_attr);
+       check_text_attr_html(&command->get_input.text_attr,
+                               command->get_input.text, test->html);
+       check_frame_id(&command->get_input.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct more_time_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+};
+
+static unsigned char more_time_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x02,
+                                               0x00, 0x82, 0x02, 0x81, 0x82 };
+
+static struct more_time_test more_time_data_111 = {
+       .pdu = more_time_111,
+       .pdu_len = sizeof(more_time_111),
+       .qualifier = 0x00,
+};
+
+/* Defined in TS 102.384 Section 27.22.4.4 */
+static void test_more_time(gconstpointer data)
+{
+       const struct get_input_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_MORE_TIME);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       stk_command_free(command);
+}
+
+struct play_tone_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       unsigned char tone;
+       struct stk_duration duration;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       char *html;
+};
+
+static unsigned char play_tone_111[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x09, 0x44, 0x69, 0x61,
+                                               0x6C, 0x20, 0x54, 0x6F, 0x6E,
+                                               0x65, 0x8E, 0x01, 0x01, 0x84,
+                                               0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_112[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x09, 0x53, 0x75, 0x62,
+                                               0x2E, 0x20, 0x42, 0x75, 0x73,
+                                               0x79, 0x8E, 0x01, 0x02, 0x84,
+                                               0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_113[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x0A, 0x43, 0x6F, 0x6E,
+                                               0x67, 0x65, 0x73, 0x74, 0x69,
+                                               0x6F, 0x6E, 0x8E, 0x01, 0x03,
+                                               0x84, 0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_114[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x06, 0x52, 0x50, 0x20,
+                                               0x41, 0x63, 0x6B, 0x8E, 0x01,
+                                               0x04, 0x84, 0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_115[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x05, 0x4E, 0x6F, 0x20,
+                                               0x52, 0x50, 0x8E, 0x01, 0x05,
+                                               0x84, 0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_116[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x09, 0x53, 0x70, 0x65,
+                                               0x63, 0x20, 0x49, 0x6E, 0x66,
+                                               0x6F, 0x8E, 0x01, 0x06, 0x84,
+                                               0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_117[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x09, 0x43, 0x61, 0x6C,
+                                               0x6C, 0x20, 0x57, 0x61, 0x69,
+                                               0x74, 0x8E, 0x01, 0x07, 0x84,
+                                               0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_118[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x09, 0x52, 0x69, 0x6E,
+                                               0x67, 0x20, 0x54, 0x6F, 0x6E,
+                                               0x65, 0x8E, 0x01, 0x08, 0x84,
+                                               0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_119[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01,
+                                               0x20, 0x00, 0x82, 0x02, 0x81,
+                                               0x03, 0x85, 0x81, 0xF1, 0x54,
+                                               0x68, 0x69, 0x73, 0x20, 0x63,
+                                               0x6F, 0x6D, 0x6D, 0x61, 0x6E,
+                                               0x64, 0x20, 0x69, 0x6E, 0x73,
+                                               0x74, 0x72, 0x75, 0x63, 0x74,
+                                               0x73, 0x20, 0x74, 0x68, 0x65,
+                                               0x20, 0x4D, 0x45, 0x20, 0x74,
+                                               0x6F, 0x20, 0x70, 0x6C, 0x61,
+                                               0x79, 0x20, 0x61, 0x6E, 0x20,
+                                               0x61, 0x75, 0x64, 0x69, 0x6F,
+                                               0x20, 0x74, 0x6F, 0x6E, 0x65,
+                                               0x2E, 0x20, 0x55, 0x70, 0x6F,
+                                               0x6E, 0x20, 0x72, 0x65, 0x63,
+                                               0x65, 0x69, 0x76, 0x69, 0x6E,
+                                               0x67, 0x20, 0x74, 0x68, 0x69,
+                                               0x73, 0x20, 0x63, 0x6F, 0x6D,
+                                               0x6D, 0x61, 0x6E, 0x64, 0x2C,
+                                               0x20, 0x74, 0x68, 0x65, 0x20,
+                                               0x4D, 0x45, 0x20, 0x73, 0x68,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x63,
+                                               0x68, 0x65, 0x63, 0x6B, 0x20,
+                                               0x69, 0x66, 0x20, 0x69, 0x74,
+                                               0x20, 0x69, 0x73, 0x20, 0x63,
+                                               0x75, 0x72, 0x72, 0x65, 0x6E,
+                                               0x74, 0x6C, 0x79, 0x20, 0x69,
+                                               0x6E, 0x2C, 0x20, 0x6F, 0x72,
+                                               0x20, 0x69, 0x6E, 0x20, 0x74,
+                                               0x68, 0x65, 0x20, 0x70, 0x72,
+                                               0x6F, 0x63, 0x65, 0x73, 0x73,
+                                               0x20, 0x6F, 0x66, 0x20, 0x73,
+                                               0x65, 0x74, 0x74, 0x69, 0x6E,
+                                               0x67, 0x20, 0x75, 0x70, 0x20,
+                                               0x28, 0x53, 0x45, 0x54, 0x2D,
+                                               0x55, 0x50, 0x20, 0x6D, 0x65,
+                                               0x73, 0x73, 0x61, 0x67, 0x65,
+                                               0x20, 0x73, 0x65, 0x6E, 0x74,
+                                               0x20, 0x74, 0x6F, 0x20, 0x74,
+                                               0x68, 0x65, 0x20, 0x6E, 0x65,
+                                               0x74, 0x77, 0x6F, 0x72, 0x6B,
+                                               0x2C, 0x20, 0x73, 0x65, 0x65,
+                                               0x20, 0x47, 0x53, 0x4D, 0x22,
+                                               0x30, 0x34, 0x2E, 0x30, 0x38,
+                                               0x22, 0x28, 0x38, 0x29, 0x29,
+                                               0x2C, 0x20, 0x61, 0x20, 0x73,
+                                               0x70, 0x65, 0x65, 0x63, 0x68,
+                                               0x20, 0x63, 0x61, 0x6C, 0x6C,
+                                               0x2E, 0x20, 0x2D, 0x20, 0x49,
+                                               0x66, 0x20, 0x74, 0x68, 0x65,
+                                               0x20, 0x4D, 0x45, 0x20, 0x49 };
+
+static unsigned char play_tone_1110[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x04, 0x42, 0x65, 0x65,
+                                               0x70, 0x8E, 0x01, 0x10, 0x84,
+                                               0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_1111[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x08, 0x50, 0x6F, 0x73,
+                                               0x69, 0x74, 0x69, 0x76, 0x65,
+                                               0x8E, 0x01, 0x11, 0x84, 0x02,
+                                               0x01, 0x01 };
+
+static unsigned char play_tone_1112[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x08, 0x4E, 0x65, 0x67,
+                                               0x61, 0x74, 0x69, 0x76, 0x65,
+                                               0x8E, 0x01, 0x12, 0x84, 0x02,
+                                               0x01, 0x01 };
+
+static unsigned char play_tone_1113[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x05, 0x51, 0x75, 0x69,
+                                               0x63, 0x6B, 0x8E, 0x01, 0x10,
+                                               0x84, 0x02, 0x02, 0x02 };
+
+static unsigned char play_tone_1114[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x07, 0x3C, 0x41, 0x42,
+                                               0x4F, 0x52, 0x54, 0x3E, 0x8E,
+                                               0x01, 0x06, 0x84, 0x02, 0x00,
+                                               0x01 };
+
+static unsigned char play_tone_1115[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03 };
+
+static unsigned char play_tone_211[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x8E, 0x01, 0x11,
+                                               0x84, 0x02, 0x01, 0x01 };
+
+
+static unsigned char play_tone_212[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x0F, 0x81, 0x0C, 0x08,
+                                               0x97, 0x94, 0xA0, 0x90, 0x92,
+                                               0xA1, 0xA2, 0x92, 0xA3, 0x99,
+                                               0xA2, 0x95, 0x8E, 0x01, 0x11,
+                                               0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_213[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x82, 0x0C, 0x04,
+                                               0x10, 0x87, 0x84, 0x90, 0x80,
+                                               0x82, 0x91, 0x92, 0x82, 0x93,
+                                               0x89, 0x92, 0x85, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_311[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x0C, 0x3C, 0x42, 0x41,
+                                               0x53, 0x49, 0x43, 0x2D, 0x49,
+                                               0x43, 0x4F, 0x4E, 0x3E, 0x8E,
+                                               0x01, 0x11, 0x84, 0x02, 0x01,
+                                               0x01, 0x1E, 0x02, 0x00, 0x01 };
+
+static unsigned char play_tone_321[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x0C, 0x3C, 0x42, 0x41,
+                                               0x53, 0x49, 0x43, 0x2D, 0x49,
+                                               0x43, 0x4F, 0x4E, 0x3E, 0x8E,
+                                               0x01, 0x11, 0x84, 0x02, 0x01,
+                                               0x01, 0x1E, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_331[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x0D, 0x3C, 0x43, 0x4F,
+                                               0x4C, 0x4F, 0x55, 0x52, 0x2D,
+                                               0x49, 0x43, 0x4F, 0x4E, 0x3E,
+                                               0x8E, 0x01, 0x11, 0x84, 0x02,
+                                               0x01, 0x01, 0x1E, 0x02, 0x00,
+                                               0x02 };
+
+static unsigned char play_tone_341[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x0D, 0x3C, 0x43, 0x4F,
+                                               0x4C, 0x4F, 0x55, 0x52, 0x2D,
+                                               0x49, 0x43, 0x4F, 0x4E, 0x3E,
+                                               0x8E, 0x01, 0x11, 0x84, 0x02,
+                                               0x01, 0x01, 0x1E, 0x02, 0x01,
+                                               0x02 };
+
+static unsigned char play_tone_411[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_412[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_421[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x01,
+                                               0xB4 };
+
+static unsigned char play_tone_422[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_431[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x02,
+                                               0xB4 };
+
+static unsigned char play_tone_432[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_441[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x04,
+                                               0xB4 };
+
+static unsigned char play_tone_442[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_443[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_451[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x08,
+                                               0xB4 };
+
+static unsigned char play_tone_452[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_453[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_461[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x10,
+                                               0xB4 };
+
+static unsigned char play_tone_462[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_463[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_471[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x20,
+                                               0xB4 };
+
+static unsigned char play_tone_472[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_473[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_481[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x40,
+                                               0xB4 };
+
+static unsigned char play_tone_482[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_483[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_491[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x80,
+                                               0xB4 };
+
+static unsigned char play_tone_492[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_493[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_4101[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01,
+                                               0xD0, 0x04, 0x00, 0x10, 0x00,
+                                               0xB4 };
+
+static unsigned char play_tone_4102[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_511[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x05, 0x80, 0x4E, 0x2D,
+                                               0x4E, 0x00, 0x8E, 0x01, 0x11,
+                                               0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_512[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x05, 0x81, 0x02, 0x9C,
+                                               0xAD, 0x80, 0x8E, 0x01, 0x11,
+                                               0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_513[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x06, 0x82, 0x02, 0x4E,
+                                               0x00, 0xAD, 0x80, 0x8E, 0x01,
+                                               0x11, 0x84, 0x02, 0x01, 0x01 };
+
+static unsigned char play_tone_611[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x09, 0x80, 0x00, 0x38,
+                                               0x00, 0x30, 0x30, 0xEB, 0x00,
+                                               0x30, 0x8E, 0x01, 0x01, 0x84,
+                                               0x02, 0x01, 0x05 };
+
+static unsigned char play_tone_612[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x07, 0x81, 0x04, 0x61,
+                                               0x38, 0x31, 0xEB, 0x31, 0x8E,
+                                               0x01, 0x01, 0x84, 0x02, 0x01,
+                                               0x05 };
+
+static unsigned char play_tone_613[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x20,
+                                               0x00, 0x82, 0x02, 0x81, 0x03,
+                                               0x85, 0x08, 0x82, 0x04, 0x30,
+                                               0xA0, 0x38, 0x32, 0xCB, 0x32,
+                                               0x8E, 0x01, 0x01, 0x84, 0x02,
+                                               0x01, 0x05 };
+
+static struct play_tone_test play_tone_data_111 = {
+       .pdu = play_tone_111,
+       .pdu_len = sizeof(play_tone_111),
+       .qualifier = 0x00,
+       .alpha_id = "Dial Tone",
+       .tone = STK_TONE_TYPE_DIAL_TONE,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_112 = {
+       .pdu = play_tone_112,
+       .pdu_len = sizeof(play_tone_112),
+       .qualifier = 0x00,
+       .alpha_id = "Sub. Busy",
+       .tone = STK_TONE_TYPE_BUSY_TONE,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_113 = {
+       .pdu = play_tone_113,
+       .pdu_len = sizeof(play_tone_113),
+       .qualifier = 0x00,
+       .alpha_id = "Congestion",
+       .tone = STK_TONE_TYPE_CONGESTION,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_114 = {
+       .pdu = play_tone_114,
+       .pdu_len = sizeof(play_tone_114),
+       .qualifier = 0x00,
+       .alpha_id = "RP Ack",
+       .tone = STK_TONE_TYPE_RP_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_115 = {
+       .pdu = play_tone_115,
+       .pdu_len = sizeof(play_tone_115),
+       .qualifier = 0x00,
+       .alpha_id = "No RP",
+       .tone = STK_TONE_TYPE_CALL_DROPPED,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_116 = {
+       .pdu = play_tone_116,
+       .pdu_len = sizeof(play_tone_116),
+       .qualifier = 0x00,
+       .alpha_id = "Spec Info",
+       .tone = STK_TONE_TYPE_ERROR,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_117 = {
+       .pdu = play_tone_117,
+       .pdu_len = sizeof(play_tone_117),
+       .qualifier = 0x00,
+       .alpha_id = "Call Wait",
+       .tone = STK_TONE_TYPE_CALL_WAITING,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_118 = {
+       .pdu = play_tone_118,
+       .pdu_len = sizeof(play_tone_118),
+       .qualifier = 0x00,
+       .alpha_id = "Ring Tone",
+       .tone = STK_TONE_TYPE_RINGING,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_119 = {
+       .pdu = play_tone_119,
+       .pdu_len = sizeof(play_tone_119),
+       .qualifier = 0x00,
+       .alpha_id = "This command instructs the ME to play an audio tone. "
+                       "Upon receiving this command, the ME shall check "
+                       "if it is currently in, or in the process of setting "
+                       "up (SET-UP message sent to the network, see "
+                       "GSM\"04.08\"(8)), a speech call. - If the ME I"
+};
+
+static struct play_tone_test play_tone_data_1110 = {
+       .pdu = play_tone_1110,
+       .pdu_len = sizeof(play_tone_1110),
+       .qualifier = 0x00,
+       .alpha_id = "Beep",
+       .tone = STK_TONE_TYPE_GENERAL_BEEP,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_1111 = {
+       .pdu = play_tone_1111,
+       .pdu_len = sizeof(play_tone_1111),
+       .qualifier = 0x00,
+       .alpha_id = "Positive",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_1112 = {
+       .pdu = play_tone_1112,
+       .pdu_len = sizeof(play_tone_1112),
+       .qualifier = 0x00,
+       .alpha_id = "Negative",
+       .tone = STK_TONE_TYPE_NEGATIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_1113 = {
+       .pdu = play_tone_1113,
+       .pdu_len = sizeof(play_tone_1113),
+       .qualifier = 0x00,
+       .alpha_id = "Quick",
+       .tone = STK_TONE_TYPE_GENERAL_BEEP,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECOND_TENTHS,
+               .interval = 2
+       }
+};
+
+static struct play_tone_test play_tone_data_1114 = {
+       .pdu = play_tone_1114,
+       .pdu_len = sizeof(play_tone_1114),
+       .qualifier = 0x00,
+       .alpha_id = "<ABORT>",
+       .tone = STK_TONE_TYPE_ERROR,
+       .duration = {
+               .unit = STK_DURATION_TYPE_MINUTES,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_1115 = {
+       .pdu = play_tone_1115,
+       .pdu_len = sizeof(play_tone_1115),
+       .qualifier = 0x00
+};
+
+static struct play_tone_test play_tone_data_211 = {
+       .pdu = play_tone_211,
+       .pdu_len = sizeof(play_tone_211),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_212 = {
+       .pdu = play_tone_212,
+       .pdu_len = sizeof(play_tone_212),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_213 = {
+       .pdu = play_tone_213,
+       .pdu_len = sizeof(play_tone_213),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_311 = {
+       .pdu = play_tone_311,
+       .pdu_len = sizeof(play_tone_311),
+       .qualifier = 0x00,
+       .alpha_id = "<BASIC-ICON>",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .icon_id = {
+           .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+           .id = 0x01
+       }
+};
+
+static struct play_tone_test play_tone_data_321 = {
+       .pdu = play_tone_321,
+       .pdu_len = sizeof(play_tone_321),
+       .qualifier = 0x00,
+       .alpha_id = "<BASIC-ICON>",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .icon_id = {
+           .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+           .id = 0x01
+       }
+};
+
+static struct play_tone_test play_tone_data_331 = {
+       .pdu = play_tone_331,
+       .pdu_len = sizeof(play_tone_331),
+       .qualifier = 0x00,
+       .alpha_id = "<COLOUR-ICON>",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .icon_id = {
+           .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+           .id = 0x02
+       }
+};
+
+static struct play_tone_test play_tone_data_341 = {
+       .pdu = play_tone_341,
+       .pdu_len = sizeof(play_tone_341),
+       .qualifier = 0x00,
+       .alpha_id = "<COLOUR-ICON>",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .icon_id = {
+           .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+           .id = 0x02
+       }
+};
+
+static struct play_tone_test play_tone_data_411 = {
+       .pdu = play_tone_411,
+       .pdu_len = sizeof(play_tone_411),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 1</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_412 = {
+       .pdu = play_tone_412,
+       .pdu_len = sizeof(play_tone_412),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_421 = {
+       .pdu = play_tone_421,
+       .pdu_len = sizeof(play_tone_421),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 }
+       },
+       .html = "<div style=\"text-align: center;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 1</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_422 = {
+       .pdu = play_tone_422,
+       .pdu_len = sizeof(play_tone_422),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_431 = {
+       .pdu = play_tone_431,
+       .pdu_len = sizeof(play_tone_431),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 }
+       },
+       .html = "<div style=\"text-align: right;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 1</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_432 = {
+       .pdu = play_tone_432,
+       .pdu_len = sizeof(play_tone_432),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_441 = {
+       .pdu = play_tone_441,
+       .pdu_len = sizeof(play_tone_441),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "big;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute 1</span></div>",
+};
+
+static struct play_tone_test play_tone_data_442 = {
+       .pdu = play_tone_442,
+       .pdu_len = sizeof(play_tone_442),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 2</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_443 = {
+       .pdu = play_tone_443,
+       .pdu_len = sizeof(play_tone_443),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_451 = {
+       .pdu = play_tone_451,
+       .pdu_len = sizeof(play_tone_451),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "small;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute 1</span></div>",
+};
+
+static struct play_tone_test play_tone_data_452 = {
+       .pdu = play_tone_452,
+       .pdu_len = sizeof(play_tone_452),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 2</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_453 = {
+       .pdu = play_tone_453,
+       .pdu_len = sizeof(play_tone_453),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_461 = {
+       .pdu = play_tone_461,
+       .pdu_len = sizeof(play_tone_461),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x10, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-weight: "
+               "bold;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute</span></div> 1"
+};
+
+static struct play_tone_test play_tone_data_462 = {
+       .pdu = play_tone_462,
+       .pdu_len = sizeof(play_tone_462),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 2</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_463 = {
+       .pdu = play_tone_463,
+       .pdu_len = sizeof(play_tone_463),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_471 = {
+       .pdu = play_tone_471,
+       .pdu_len = sizeof(play_tone_471),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x20, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-style: "
+               "italic;color: #347235;background-color: #FFFF00;\">"
+               "Text Attribute</span></div> 1",
+};
+
+static struct play_tone_test play_tone_data_472 = {
+       .pdu = play_tone_472,
+       .pdu_len = sizeof(play_tone_472),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 2</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_473 = {
+       .pdu = play_tone_473,
+       .pdu_len = sizeof(play_tone_473),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_481 = {
+       .pdu = play_tone_481,
+       .pdu_len = sizeof(play_tone_481),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: underline;color: #347235;"
+               "background-color: #FFFF00;\">Text Attribute 1</span></div>",
+};
+
+static struct play_tone_test play_tone_data_482 = {
+       .pdu = play_tone_482,
+       .pdu_len = sizeof(play_tone_482),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 2</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_483 = {
+       .pdu = play_tone_483,
+       .pdu_len = sizeof(play_tone_483),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_491 = {
+       .pdu = play_tone_491,
+       .pdu_len = sizeof(play_tone_491),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: line-through;color: #347235;"
+               "background-color: #FFFF00;\">Text Attribute 1</span></div>",
+};
+
+static struct play_tone_test play_tone_data_492 = {
+       .pdu = play_tone_492,
+       .pdu_len = sizeof(play_tone_492),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 2</span>"
+               "</div>",
+};
+
+static struct play_tone_test play_tone_data_493 = {
+       .pdu = play_tone_493,
+       .pdu_len = sizeof(play_tone_493),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_4101 = {
+       .pdu = play_tone_4101,
+       .pdu_len = sizeof(play_tone_4101),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Text Attribute 1</span>",
+};
+
+static struct play_tone_test play_tone_data_4102 = {
+       .pdu = play_tone_4102,
+       .pdu_len = sizeof(play_tone_4102),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_511 = {
+       .pdu = play_tone_511,
+       .pdu_len = sizeof(play_tone_511),
+       .qualifier = 0x00,
+       .alpha_id = "中一",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_512 = {
+       .pdu = play_tone_512,
+       .pdu_len = sizeof(play_tone_512),
+       .qualifier = 0x00,
+       .alpha_id = "中一",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_513 = {
+       .pdu = play_tone_513,
+       .pdu_len = sizeof(play_tone_513),
+       .qualifier = 0x00,
+       .alpha_id = "中一",
+       .tone = STK_TONE_TYPE_POSITIVE_ACK,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 1
+       }
+};
+
+static struct play_tone_test play_tone_data_611 = {
+       .pdu = play_tone_611,
+       .pdu_len = sizeof(play_tone_611),
+       .qualifier = 0x00,
+       .alpha_id = "80ル0",
+       .tone = STK_TONE_TYPE_DIAL_TONE,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_612 = {
+       .pdu = play_tone_612,
+       .pdu_len = sizeof(play_tone_612),
+       .qualifier = 0x00,
+       .alpha_id = "81ル1",
+       .tone = STK_TONE_TYPE_DIAL_TONE,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+static struct play_tone_test play_tone_data_613 = {
+       .pdu = play_tone_613,
+       .pdu_len = sizeof(play_tone_613),
+       .qualifier = 0x00,
+       .alpha_id = "82ル2",
+       .tone = STK_TONE_TYPE_DIAL_TONE,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 5
+       }
+};
+
+/* Defined in TS 102.384 Section 27.22.4.5 */
+static void test_play_tone(gconstpointer data)
+{
+       const struct play_tone_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_PLAY_TONE);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_EARPIECE);
+
+       check_alpha_id(command->play_tone.alpha_id, test->alpha_id);
+       check_tone(command->play_tone.tone, test->tone);
+       check_duration(&command->play_tone.duration, &test->duration);
+       check_icon_id(&command->play_tone.icon_id, &test->icon_id);
+       check_text_attr(&command->play_tone.text_attr, &test->text_attr);
+       check_text_attr_html(&command->play_tone.text_attr,
+                               command->play_tone.alpha_id, test->html);
+       check_frame_id(&command->play_tone.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct poll_interval_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       struct stk_duration duration;
+};
+
+static unsigned char poll_interval_111[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x03,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x84, 0x02, 0x01, 0x14 };
+
+static struct poll_interval_test poll_interval_data_111 = {
+       .pdu = poll_interval_111,
+       .pdu_len = sizeof(poll_interval_111),
+       .qualifier = 0x00,
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 20
+       }
+};
+
+/* Defined in TS 102.384 Section 27.22.4.6 */
+static void test_poll_interval(gconstpointer data)
+{
+       const struct poll_interval_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_POLL_INTERVAL);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_duration(&command->poll_interval.duration, &test->duration);
+
+       stk_command_free(command);
+}
+
+struct setup_menu_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       struct stk_item items[MAX_ITEM];
+       struct stk_items_next_action_indicator next_act;
+       struct stk_icon_id icon_id;
+       struct stk_item_icon_id_list item_icon_id_list;
+       struct stk_text_attribute text_attr;
+       struct stk_item_text_attribute_list item_text_attr_list;
+       char *html;
+};
+
+static unsigned char setup_menu_111[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31, 0x8F, 0x07,
+                                               0x02, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x32, 0x8F, 0x07, 0x03,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x33, 0x8F, 0x07, 0x04, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x34 };
+
+static unsigned char setup_menu_112[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x04, 0x11, 0x4F, 0x6E, 0x65,
+                                               0x8F, 0x04, 0x12, 0x54, 0x77,
+                                               0x6F };
+
+static unsigned char setup_menu_113[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00, 0x8F, 0x00 };
+
+static unsigned char setup_menu_121[] = { 0xD0, 0x81, 0xFC, 0x81, 0x03, 0x01,
+                                               0x25, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0A, 0x4C, 0x61,
+                                               0x72, 0x67, 0x65, 0x4D, 0x65,
+                                               0x6E, 0x75, 0x31, 0x8F, 0x05,
+                                               0x50, 0x5A, 0x65, 0x72, 0x6F,
+                                               0x8F, 0x04, 0x4F, 0x4F, 0x6E,
+                                               0x65, 0x8F, 0x04, 0x4E, 0x54,
+                                               0x77, 0x6F, 0x8F, 0x06, 0x4D,
+                                               0x54, 0x68, 0x72, 0x65, 0x65,
+                                               0x8F, 0x05, 0x4C, 0x46, 0x6F,
+                                               0x75, 0x72, 0x8F, 0x05, 0x4B,
+                                               0x46, 0x69, 0x76, 0x65, 0x8F,
+                                               0x04, 0x4A, 0x53, 0x69, 0x78,
+                                               0x8F, 0x06, 0x49, 0x53, 0x65,
+                                               0x76, 0x65, 0x6E, 0x8F, 0x06,
+                                               0x48, 0x45, 0x69, 0x67, 0x68,
+                                               0x74, 0x8F, 0x05, 0x47, 0x4E,
+                                               0x69, 0x6E, 0x65, 0x8F, 0x06,
+                                               0x46, 0x41, 0x6C, 0x70, 0x68,
+                                               0x61, 0x8F, 0x06, 0x45, 0x42,
+                                               0x72, 0x61, 0x76, 0x6F, 0x8F,
+                                               0x08, 0x44, 0x43, 0x68, 0x61,
+                                               0x72, 0x6C, 0x69, 0x65, 0x8F,
+                                               0x06, 0x43, 0x44, 0x65, 0x6C,
+                                               0x74, 0x61, 0x8F, 0x05, 0x42,
+                                               0x45, 0x63, 0x68, 0x6F, 0x8F,
+                                               0x09, 0x41, 0x46, 0x6F, 0x78,
+                                               0x2D, 0x74, 0x72, 0x6F, 0x74,
+                                               0x8F, 0x06, 0x40, 0x42, 0x6C,
+                                               0x61, 0x63, 0x6B, 0x8F, 0x06,
+                                               0x3F, 0x42, 0x72, 0x6F, 0x77,
+                                               0x6E, 0x8F, 0x04, 0x3E, 0x52,
+                                               0x65, 0x64, 0x8F, 0x07, 0x3D,
+                                               0x4F, 0x72, 0x61, 0x6E, 0x67,
+                                               0x65, 0x8F, 0x07, 0x3C, 0x59,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x77,
+                                               0x8F, 0x06, 0x3B, 0x47, 0x72,
+                                               0x65, 0x65, 0x6E, 0x8F, 0x05,
+                                               0x3A, 0x42, 0x6C, 0x75, 0x65,
+                                               0x8F, 0x07, 0x39, 0x56, 0x69,
+                                               0x6F, 0x6C, 0x65, 0x74, 0x8F,
+                                               0x05, 0x38, 0x47, 0x72, 0x65,
+                                               0x79, 0x8F, 0x06, 0x37, 0x57,
+                                               0x68, 0x69, 0x74, 0x65, 0x8F,
+                                               0x06, 0x36, 0x6D, 0x69, 0x6C,
+                                               0x6C, 0x69, 0x8F, 0x06, 0x35,
+                                               0x6D, 0x69, 0x63, 0x72, 0x6F,
+                                               0x8F, 0x05, 0x34, 0x6E, 0x61,
+                                               0x6E, 0x6F, 0x8F, 0x05, 0x33,
+                                               0x70, 0x69, 0x63, 0x6F };
+
+static unsigned char setup_menu_122[] = { 0xD0, 0x81, 0xF3, 0x81, 0x03, 0x01,
+                                               0x25, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0A, 0x4C, 0x61,
+                                               0x72, 0x67, 0x65, 0x4D, 0x65,
+                                               0x6E, 0x75, 0x32, 0x8F, 0x1D,
+                                               0xFF, 0x31, 0x20, 0x43, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x46, 0x6F,
+                                               0x72, 0x77, 0x61, 0x72, 0x64,
+                                               0x20, 0x55, 0x6E, 0x63, 0x6F,
+                                               0x6E, 0x64, 0x69, 0x74, 0x69,
+                                               0x6F, 0x6E, 0x61, 0x6C, 0x8F,
+                                               0x1C, 0xFE, 0x32, 0x20, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x46,
+                                               0x6F, 0x72, 0x77, 0x61, 0x72,
+                                               0x64, 0x20, 0x4F, 0x6E, 0x20,
+                                               0x55, 0x73, 0x65, 0x72, 0x20,
+                                               0x42, 0x75, 0x73, 0x79, 0x8F,
+                                               0x1B, 0xFD, 0x33, 0x20, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x46,
+                                               0x6F, 0x72, 0x77, 0x61, 0x72,
+                                               0x64, 0x20, 0x4F, 0x6E, 0x20,
+                                               0x4E, 0x6F, 0x20, 0x52, 0x65,
+                                               0x70, 0x6C, 0x79, 0x8F, 0x25,
+                                               0xFC, 0x34, 0x20, 0x43, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x46, 0x6F,
+                                               0x72, 0x77, 0x61, 0x72, 0x64,
+                                               0x20, 0x4F, 0x6E, 0x20, 0x55,
+                                               0x73, 0x65, 0x72, 0x20, 0x4E,
+                                               0x6F, 0x74, 0x20, 0x52, 0x65,
+                                               0x61, 0x63, 0x68, 0x61, 0x62,
+                                               0x6C, 0x65, 0x8F, 0x20, 0xFB,
+                                               0x35, 0x20, 0x42, 0x61, 0x72,
+                                               0x72, 0x69, 0x6E, 0x67, 0x20,
+                                               0x4F, 0x66, 0x20, 0x41, 0x6C,
+                                               0x6C, 0x20, 0x4F, 0x75, 0x74,
+                                               0x67, 0x6F, 0x69, 0x6E, 0x67,
+                                               0x20, 0x43, 0x61, 0x6C, 0x6C,
+                                               0x73, 0x8F, 0x24, 0xFA, 0x36,
+                                               0x20, 0x42, 0x61, 0x72, 0x72,
+                                               0x69, 0x6E, 0x67, 0x20, 0x4F,
+                                               0x66, 0x20, 0x41, 0x6C, 0x6C,
+                                               0x20, 0x4F, 0x75, 0x74, 0x67,
+                                               0x6F, 0x69, 0x6E, 0x67, 0x20,
+                                               0x49, 0x6E, 0x74, 0x20, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x73, 0x8F,
+                                               0x13, 0xF9, 0x37, 0x20, 0x43,
+                                               0x4C, 0x49, 0x20, 0x50, 0x72,
+                                               0x65, 0x73, 0x65, 0x6E, 0x74,
+                                               0x61, 0x74, 0x69, 0x6F, 0x6E };
+
+static unsigned char setup_menu_123[] = { 0xD0, 0x81, 0xFC, 0x81, 0x03, 0x01,
+                                               0x25, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x81, 0xEC, 0x54,
+                                               0x68, 0x65, 0x20, 0x53, 0x49,
+                                               0x4D, 0x20, 0x73, 0x68, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x73, 0x75,
+                                               0x70, 0x70, 0x6C, 0x79, 0x20,
+                                               0x61, 0x20, 0x73, 0x65, 0x74,
+                                               0x20, 0x6F, 0x66, 0x20, 0x6D,
+                                               0x65, 0x6E, 0x75, 0x20, 0x69,
+                                               0x74, 0x65, 0x6D, 0x73, 0x2C,
+                                               0x20, 0x77, 0x68, 0x69, 0x63,
+                                               0x68, 0x20, 0x73, 0x68, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x62, 0x65,
+                                               0x20, 0x69, 0x6E, 0x74, 0x65,
+                                               0x67, 0x72, 0x61, 0x74, 0x65,
+                                               0x64, 0x20, 0x77, 0x69, 0x74,
+                                               0x68, 0x20, 0x74, 0x68, 0x65,
+                                               0x20, 0x6D, 0x65, 0x6E, 0x75,
+                                               0x20, 0x73, 0x79, 0x73, 0x74,
+                                               0x65, 0x6D, 0x20, 0x28, 0x6F,
+                                               0x72, 0x20, 0x6F, 0x74, 0x68,
+                                               0x65, 0x72, 0x20, 0x4D, 0x4D,
+                                               0x49, 0x20, 0x66, 0x61, 0x63,
+                                               0x69, 0x6C, 0x69, 0x74, 0x79,
+                                               0x29, 0x20, 0x69, 0x6E, 0x20,
+                                               0x6F, 0x72, 0x64, 0x65, 0x72,
+                                               0x20, 0x74, 0x6F, 0x20, 0x67,
+                                               0x69, 0x76, 0x65, 0x20, 0x74,
+                                               0x68, 0x65, 0x20, 0x75, 0x73,
+                                               0x65, 0x72, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x6F, 0x70, 0x70,
+                                               0x6F, 0x72, 0x74, 0x75, 0x6E,
+                                               0x69, 0x74, 0x79, 0x20, 0x74,
+                                               0x6F, 0x20, 0x63, 0x68, 0x6F,
+                                               0x6F, 0x73, 0x65, 0x20, 0x6F,
+                                               0x6E, 0x65, 0x20, 0x6F, 0x66,
+                                               0x20, 0x74, 0x68, 0x65, 0x73,
+                                               0x65, 0x20, 0x6D, 0x65, 0x6E,
+                                               0x75, 0x20, 0x69, 0x74, 0x65,
+                                               0x6D, 0x73, 0x20, 0x61, 0x74,
+                                               0x20, 0x68, 0x69, 0x73, 0x20,
+                                               0x6F, 0x77, 0x6E, 0x20, 0x64,
+                                               0x69, 0x73, 0x63, 0x72, 0x65,
+                                               0x74, 0x69, 0x6F, 0x6E, 0x2E,
+                                               0x20, 0x45, 0x61, 0x63, 0x68,
+                                               0x20, 0x69, 0x74, 0x65, 0x6D,
+                                               0x20, 0x63, 0x6F, 0x6D, 0x70,
+                                               0x72, 0x69, 0x73, 0x65, 0x73,
+                                               0x20, 0x61, 0x20, 0x73, 0x68,
+                                               0x8F, 0x02, 0x01, 0x59 };
+
+static unsigned char setup_menu_211[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x25,
+                                               0x80, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31, 0x8F, 0x07,
+                                               0x02, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x32, 0x8F, 0x07, 0x03,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x33, 0x8F, 0x07, 0x04, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x34 };
+
+static unsigned char setup_menu_311[] = { 0xD0, 0x41, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31, 0x8F, 0x07,
+                                               0x02, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x32, 0x8F, 0x07, 0x03,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x33, 0x8F, 0x07, 0x04, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x34,
+                                               0x18, 0x04, 0x13, 0x10, 0x15,
+                                               0x26 };
+
+static unsigned char setup_menu_411[] = { 0xD0, 0x3C, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31, 0x8F, 0x07,
+                                               0x02, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x32, 0x8F, 0x07, 0x03,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x33, 0x9E, 0x02, 0x01, 0x01,
+                                               0x9F, 0x04, 0x01, 0x05, 0x05,
+                                               0x05 };
+
+static unsigned char setup_menu_421[] = { 0xD0, 0x3C, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31, 0x8F, 0x07,
+                                               0x02, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x32, 0x8F, 0x07, 0x03,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x33, 0x9E, 0x02, 0x00, 0x01,
+                                               0x9F, 0x04, 0x00, 0x05, 0x05,
+                                               0x05 };
+
+static unsigned char setup_menu_511[] = { 0xD0, 0x29, 0x81, 0x03, 0x01, 0x25,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31, 0x8F, 0x07,
+                                               0x02, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x32 };
+
+static unsigned char setup_menu_611[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x00, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x00, 0xB4,
+                                               0x00, 0x06, 0x00, 0xB4, 0x00,
+                                               0x06, 0x00, 0xB4 };
+
+static unsigned char setup_menu_612[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x32, 0x8F, 0x07, 0x04, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x34,
+                                               0x8F, 0x07, 0x05, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x35, 0x8F,
+                                               0x07, 0x06, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x36 };
+
+static unsigned char setup_menu_621[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x01, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x01, 0xB4,
+                                               0x00, 0x06, 0x01, 0xB4, 0x00,
+                                               0x06, 0x01, 0xB4 };
+
+static unsigned char setup_menu_622[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x32, 0x8F, 0x07, 0x04, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x34,
+                                               0x8F, 0x07, 0x05, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x35, 0x8F,
+                                               0x07, 0x06, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x36 };
+
+static unsigned char setup_menu_631[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x02, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x02, 0xB4,
+                                               0x00, 0x06, 0x02, 0xB4, 0x00,
+                                               0x06, 0x02, 0xB4 };
+
+static unsigned char setup_menu_632[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x32, 0x8F, 0x07, 0x04, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x34,
+                                               0x8F, 0x07, 0x05, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x35, 0x8F,
+                                               0x07, 0x06, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x36 };
+
+static unsigned char setup_menu_641[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x04, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x04, 0xB4,
+                                               0x00, 0x06, 0x04, 0xB4, 0x00,
+                                               0x06, 0x04, 0xB4 };
+
+static unsigned char setup_menu_642[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x32, 0x8F, 0x07, 0x04, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x34,
+                                               0x8F, 0x07, 0x05, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x35, 0x8F,
+                                               0x07, 0x06, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x36, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x00, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x00, 0xB4,
+                                               0x00, 0x06, 0x00, 0xB4, 0x00,
+                                               0x06, 0x00, 0xB4 };
+
+static unsigned char setup_menu_643[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x33, 0x8F, 0x07, 0x07, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x37,
+                                               0x8F, 0x07, 0x08, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x38, 0x8F,
+                                               0x07, 0x09, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x39 };
+
+static unsigned char setup_menu_651[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x08, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x08, 0xB4,
+                                               0x00, 0x06, 0x08, 0xB4, 0x00,
+                                               0x06, 0x08, 0xB4 };
+
+static unsigned char setup_menu_661[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x10, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x10, 0xB4,
+                                               0x00, 0x06, 0x10, 0xB4, 0x00,
+                                               0x06, 0x10, 0xB4 };
+
+static unsigned char setup_menu_671[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x20, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x20, 0xB4,
+                                               0x00, 0x06, 0x20, 0xB4, 0x00,
+                                               0x06, 0x20, 0xB4 };
+
+static unsigned char setup_menu_681[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x40, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x40, 0xB4,
+                                               0x00, 0x06, 0x40, 0xB4, 0x00,
+                                               0x06, 0x40, 0xB4 };
+
+static unsigned char setup_menu_691[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x20,
+                                               0x31, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0xD0, 0x04,
+                                               0x00, 0x0E, 0x80, 0xB4, 0xD1,
+                                               0x0C, 0x00, 0x06, 0x80, 0xB4,
+                                               0x00, 0x06, 0x80, 0xB4, 0x00,
+                                               0x06, 0x80, 0xB4 };
+
+static unsigned char setup_menu_6101[] = { 0xD0, 0x46, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0C, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x4D, 0x65, 0x6E, 0x75, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31, 0x8F, 0x07,
+                                               0x02, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x32, 0x8F, 0x07, 0x03,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x33, 0xD0, 0x04, 0x00, 0x0C,
+                                               0x00, 0xB4, 0xD1, 0x0C, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4, 0x00, 0x06, 0x00,
+                                               0xB4 };
+
+static unsigned char setup_menu_711[] = { 0xD0, 0x81, 0x9C, 0x81, 0x03, 0x01,
+                                               0x25, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x19, 0x80, 0x04,
+                                               0x17, 0x04, 0x14, 0x04, 0x20,
+                                               0x04, 0x10, 0x04, 0x12, 0x04,
+                                               0x21, 0x04, 0x22, 0x04, 0x12,
+                                               0x04, 0x23, 0x04, 0x19, 0x04,
+                                               0x22, 0x04, 0x15, 0x8F, 0x1C,
+                                               0x01, 0x80, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15, 0x00, 0x31, 0x8F, 0x1C,
+                                               0x02, 0x80, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15, 0x00, 0x32, 0x8F, 0x1C,
+                                               0x03, 0x80, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15, 0x00, 0x33, 0x8F, 0x1C,
+                                               0x04, 0x80, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15, 0x00, 0x34 };
+
+static unsigned char setup_menu_712[] = { 0xD0, 0x60, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x8F, 0x1C, 0x11,
+                                               0x80, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x00, 0x35, 0x8F, 0x1C, 0x12,
+                                               0x80, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x00, 0x36 };
+
+static unsigned char setup_menu_713[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00, 0x8F, 0x00 };
+
+static unsigned char setup_menu_811[] = { 0xD0, 0x3C, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x09, 0x80, 0x5D, 0xE5,
+                                               0x51, 0x77, 0x7B, 0xB1, 0x53,
+                                               0x55, 0x8F, 0x08, 0x01, 0x80,
+                                               0x98, 0x79, 0x76, 0xEE, 0x4E,
+                                               0x00, 0x8F, 0x08, 0x02, 0x80,
+                                               0x98, 0x79, 0x76, 0xEE, 0x4E,
+                                               0x8C, 0x8F, 0x08, 0x03, 0x80,
+                                               0x98, 0x79, 0x76, 0xEE, 0x4E,
+                                               0x09, 0x8F, 0x08, 0x04, 0x80,
+                                               0x98, 0x79, 0x76, 0xEE, 0x56,
+                                               0xDB };
+
+static unsigned char setup_menu_812[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x09, 0x80, 0x5D, 0xE5,
+                                               0x51, 0x77, 0x7B, 0xB1, 0x53,
+                                               0x55, 0x8F, 0x04, 0x11, 0x80,
+                                               0x4E, 0x00, 0x8F, 0x04, 0x12,
+                                               0x80, 0x4E, 0x8C };
+
+static unsigned char setup_menu_813[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00, 0x8F, 0x00 };
+
+static unsigned char setup_menu_911[] = { 0xD0, 0x44, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x09, 0x80, 0x00, 0x38,
+                                               0x00, 0x30, 0x30, 0xEB, 0x00,
+                                               0x30, 0x8F, 0x0A, 0x01, 0x80,
+                                               0x00, 0x38, 0x00, 0x30, 0x30,
+                                               0xEB, 0x00, 0x31, 0x8F, 0x0A,
+                                               0x02, 0x80, 0x00, 0x38, 0x00,
+                                               0x30, 0x30, 0xEB, 0x00, 0x32,
+                                               0x8F, 0x0A, 0x03, 0x80, 0x00,
+                                               0x38, 0x00, 0x30, 0x30, 0xEB,
+                                               0x00, 0x33, 0x8F, 0x0A, 0x04,
+                                               0x80, 0x00, 0x38, 0x00, 0x30,
+                                               0x30, 0xEB, 0x00, 0x34 };
+
+static unsigned char setup_menu_912[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x09, 0x80, 0x00, 0x38,
+                                               0x00, 0x30, 0x30, 0xEB, 0x00,
+                                               0x30, 0x8F, 0x0A, 0x11, 0x80,
+                                               0x00, 0x38, 0x00, 0x30, 0x30,
+                                               0xEB, 0x00, 0x35, 0x8F, 0x0A,
+                                               0x12, 0x80, 0x00, 0x38, 0x00,
+                                               0x30, 0x30, 0xEB, 0x00, 0x36 };
+
+static unsigned char setup_menu_913[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00, 0x8F, 0x00 };
+
+/* Negative case: No item is present */
+static unsigned char setup_menu_neg_1[] = { 0xD0, 0x0B, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00 };
+
+/* Negative case: Two empty items*/
+static unsigned char setup_menu_neg_2[] = { 0xD0, 0x0F, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00, 0x8F, 0x00, 0x8F,
+                                               0x00 };
+
+/* Negative case: valid item + empty item */
+static unsigned char setup_menu_neg_3[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00, 0x8F, 0x07, 0x01,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x31, 0x8F, 0x00 };
+
+/* Negative case: empty item + valid item */
+static unsigned char setup_menu_neg_4[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x25,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x00, 0x8F, 0x00, 0x8F,
+                                               0x07, 0x01, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x31 };
+
+static struct setup_menu_test setup_menu_data_111 = {
+       .pdu = setup_menu_111,
+       .pdu_len = sizeof(setup_menu_111),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+               { .id = 4, .text = "Item 4" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_112 = {
+       .pdu = setup_menu_112,
+       .pdu_len = sizeof(setup_menu_112),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 0x11, .text = "One" },
+               { .id = 0x12, .text = "Two" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_113 = {
+       .pdu = setup_menu_113,
+       .pdu_len = sizeof(setup_menu_113),
+       .qualifier = 0x00,
+       .alpha_id = ""
+};
+
+static struct setup_menu_test setup_menu_data_121 = {
+       .pdu = setup_menu_121,
+       .pdu_len = sizeof(setup_menu_121),
+       .qualifier = 0x00,
+       .alpha_id = "LargeMenu1",
+       .items = {
+               { .id = 0x50, .text = "Zero" },
+               { .id = 0x4F, .text = "One" },
+               { .id = 0x4E, .text = "Two" },
+               { .id = 0x4D, .text = "Three" },
+               { .id = 0x4C, .text = "Four" },
+               { .id = 0x4B, .text = "Five" },
+               { .id = 0x4A, .text = "Six" },
+               { .id = 0x49, .text = "Seven" },
+               { .id = 0x48, .text = "Eight" },
+               { .id = 0x47, .text = "Nine" },
+               { .id = 0x46, .text = "Alpha" },
+               { .id = 0x45, .text = "Bravo" },
+               { .id = 0x44, .text = "Charlie" },
+               { .id = 0x43, .text = "Delta" },
+               { .id = 0x42, .text = "Echo" },
+               { .id = 0x41, .text = "Fox-trot" },
+               { .id = 0x40, .text = "Black" },
+               { .id = 0x3F, .text = "Brown" },
+               { .id = 0x3E, .text = "Red" },
+               { .id = 0x3D, .text = "Orange" },
+               { .id = 0x3C, .text = "Yellow" },
+               { .id = 0x3B, .text = "Green" },
+               { .id = 0x3A, .text = "Blue" },
+               { .id = 0x39, .text = "Violet" },
+               { .id = 0x38, .text = "Grey" },
+               { .id = 0x37, .text = "White" },
+               { .id = 0x36, .text = "milli" },
+               { .id = 0x35, .text = "micro" },
+               { .id = 0x34, .text = "nano" },
+               { .id = 0x33, .text = "pico" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_122 = {
+       .pdu = setup_menu_122,
+       .pdu_len = sizeof(setup_menu_122),
+       .qualifier = 0x00,
+       .alpha_id = "LargeMenu2",
+       .items = {
+               { .id = 0xFF, .text = "1 Call Forward Unconditional" },
+               { .id = 0xFE, .text = "2 Call Forward On User Busy" },
+               { .id = 0xFD, .text = "3 Call Forward On No Reply" },
+               { .id = 0xFC, .text = "4 Call Forward On User Not Reachable" },
+               { .id = 0xFB, .text = "5 Barring Of All Outgoing Calls" },
+               { .id = 0xFA, .text = "6 Barring Of All Outgoing Int Calls" },
+               { .id = 0xF9, .text = "7 CLI Presentation" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_123 = {
+       .pdu = setup_menu_123,
+       .pdu_len = sizeof(setup_menu_123),
+       .qualifier = 0x00,
+       .alpha_id = "The SIM shall supply a set of menu items, which shall "
+                       "be integrated with the menu system (or other MMI "
+                       "facility) in order to give the user the opportunity "
+                       "to choose one of these menu items at his own "
+                       "discretion. Each item comprises a sh",
+       .items = {
+               { .id = 0x01, .text = "Y" }
+       }
+};
+
+static struct setup_menu_test setup_menu_data_211 = {
+       .pdu = setup_menu_211,
+       .pdu_len = sizeof(setup_menu_211),
+       .qualifier = 0x80,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+               { .id = 4, .text = "Item 4" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_311 = {
+       .pdu = setup_menu_311,
+       .pdu_len = sizeof(setup_menu_311),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+               { .id = 4, .text = "Item 4" },
+       },
+       .next_act = {
+               .list = { STK_COMMAND_TYPE_SEND_SMS,
+                               STK_COMMAND_TYPE_SETUP_CALL,
+                               STK_COMMAND_TYPE_LAUNCH_BROWSER,
+                               STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO },
+               .len = 4
+       }
+};
+
+static struct setup_menu_test setup_menu_data_411 = {
+       .pdu = setup_menu_411,
+       .pdu_len = sizeof(setup_menu_411),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 1
+       },
+       .item_icon_id_list = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .list = { 5, 5, 5 },
+               .len = 3
+       }
+};
+
+static struct setup_menu_test setup_menu_data_421 = {
+       .pdu = setup_menu_421,
+       .pdu_len = sizeof(setup_menu_421),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 1
+       },
+       .item_icon_id_list = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .list = { 5, 5, 5 },
+               .len = 3
+       }
+};
+
+static struct setup_menu_test setup_menu_data_511 = {
+       .pdu = setup_menu_511,
+       .pdu_len = sizeof(setup_menu_511),
+       .qualifier = 0x01,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_611 = {
+       .pdu = setup_menu_611,
+       .pdu_len = sizeof(setup_menu_611),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4,
+                               0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Menu 1</span>"
+               "</div>",
+};
+
+static struct setup_menu_test setup_menu_data_612 = {
+       .pdu = setup_menu_612,
+       .pdu_len = sizeof(setup_menu_612),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 2",
+       .items = {
+               { .id = 4, .text = "Item 4" },
+               { .id = 5, .text = "Item 5" },
+               { .id = 6, .text = "Item 6" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_621 = {
+       .pdu = setup_menu_621,
+       .pdu_len = sizeof(setup_menu_621),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x01, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4,
+                               0x00, 0x06, 0x01, 0xB4 }
+       },
+       .html = "<div style=\"text-align: center;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Menu 1</span>"
+               "</div>"
+};
+
+static struct setup_menu_test setup_menu_data_622 = {
+       .pdu = setup_menu_622,
+       .pdu_len = sizeof(setup_menu_622),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 2",
+       .items = {
+               { .id = 4, .text = "Item 4" },
+               { .id = 5, .text = "Item 5" },
+               { .id = 6, .text = "Item 6" },
+       }
+};
+
+/*
+ * Some problem with data of item #3 in item_text_attr_list
+ * and the explanation
+ */
+static struct setup_menu_test setup_menu_data_631 = {
+       .pdu = setup_menu_631,
+       .pdu_len = sizeof(setup_menu_631),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x02, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4,
+                               0x00, 0x06, 0x02, 0xB4 }
+       },
+       .html = "<div style=\"text-align: right;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Menu 1</span>"
+               "</div>"
+};
+
+static struct setup_menu_test setup_menu_data_632 = {
+       .pdu = setup_menu_632,
+       .pdu_len = sizeof(setup_menu_632),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 2",
+       .items = {
+               { .id = 4, .text = "Item 4" },
+               { .id = 5, .text = "Item 5" },
+               { .id = 6, .text = "Item 6" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_641 = {
+       .pdu = setup_menu_641,
+       .pdu_len = sizeof(setup_menu_641),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x04, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4,
+                               0x00, 0x06, 0x04, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "big;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Menu 1</span></div>",
+};
+
+static struct setup_menu_test setup_menu_data_642 = {
+       .pdu = setup_menu_642,
+       .pdu_len = sizeof(setup_menu_642),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 2",
+       .items = {
+               { .id = 4, .text = "Item 4" },
+               { .id = 5, .text = "Item 5" },
+               { .id = 6, .text = "Item 6" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4,
+                               0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Menu 2</span>"
+               "</div>",
+};
+
+static struct setup_menu_test setup_menu_data_643 = {
+       .pdu = setup_menu_643,
+       .pdu_len = sizeof(setup_menu_643),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 3",
+       .items = {
+               { .id = 7, .text = "Item 7" },
+               { .id = 8, .text = "Item 8" },
+               { .id = 9, .text = "Item 9" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_651 = {
+       .pdu = setup_menu_651,
+       .pdu_len = sizeof(setup_menu_651),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x08, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4,
+                               0x00, 0x06, 0x08, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "small;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Menu 1</span></div>",
+};
+
+static struct setup_menu_test setup_menu_data_661 = {
+       .pdu = setup_menu_661,
+       .pdu_len = sizeof(setup_menu_661),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x10, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4,
+                               0x00, 0x06, 0x10, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-weight: "
+               "bold;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Menu 1</span></div>",
+};
+
+static struct setup_menu_test setup_menu_data_671 = {
+       .pdu = setup_menu_671,
+       .pdu_len = sizeof(setup_menu_671),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x20, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4,
+                               0x00, 0x06, 0x20, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-style: "
+               "italic;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Menu 1</span></div>"
+};
+
+static struct setup_menu_test setup_menu_data_681 = {
+       .pdu = setup_menu_681,
+       .pdu_len = sizeof(setup_menu_681),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x40, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4,
+                               0x00, 0x06, 0x40, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: underline;color: #347235;"
+               "background-color: #FFFF00;\">Toolkit Menu 1</span></div>",
+};
+
+static struct setup_menu_test setup_menu_data_691 = {
+       .pdu = setup_menu_691,
+       .pdu_len = sizeof(setup_menu_691),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x80, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4,
+                               0x00, 0x06, 0x80, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: line-through;color: #347235;"
+               "background-color: #FFFF00;\">Toolkit Menu 1</span></div>",
+};
+
+static struct setup_menu_test setup_menu_data_6101 = {
+       .pdu = setup_menu_6101,
+       .pdu_len = sizeof(setup_menu_6101),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Menu",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0C, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 12,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4,
+                               0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Menu</span>"
+               "</div>",
+};
+
+static struct setup_menu_test setup_menu_data_711 = {
+       .pdu = setup_menu_711,
+       .pdu_len = sizeof(setup_menu_711),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .items = {
+               { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" },
+               { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" },
+               { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" },
+               { .id = 4, .text = "ЗДРАВСТВУЙТЕ4" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_712 = {
+       .pdu = setup_menu_712,
+       .pdu_len = sizeof(setup_menu_712),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .items = {
+               { .id = 0x11, .text = "ЗДРАВСТВУЙТЕ5" },
+               { .id = 0x12, .text = "ЗДРАВСТВУЙТЕ6" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_713 = {
+       .pdu = setup_menu_713,
+       .pdu_len = sizeof(setup_menu_713),
+       .qualifier = 0x00,
+       .alpha_id = ""
+};
+
+static struct setup_menu_test setup_menu_data_811 = {
+       .pdu = setup_menu_811,
+       .pdu_len = sizeof(setup_menu_811),
+       .qualifier = 0x00,
+       .alpha_id = "工具箱单",
+       .items = {
+               { .id = 1, .text = "项目一" },
+               { .id = 2, .text = "项目二" },
+               { .id = 3, .text = "项目三" },
+               { .id = 4, .text = "项目四" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_812 = {
+       .pdu = setup_menu_812,
+       .pdu_len = sizeof(setup_menu_812),
+       .qualifier = 0x00,
+       .alpha_id = "工具箱单",
+       .items = {
+               { .id = 0x11, .text = "一" },
+               { .id = 0x12, .text = "二" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_813 = {
+       .pdu = setup_menu_813,
+       .pdu_len = sizeof(setup_menu_813),
+       .qualifier = 0x00,
+       .alpha_id = ""
+};
+
+static struct setup_menu_test setup_menu_data_911 = {
+       .pdu = setup_menu_911,
+       .pdu_len = sizeof(setup_menu_911),
+       .qualifier = 0x00,
+       .alpha_id = "80ル0",
+       .items = {
+               { .id = 1, .text = "80ル1" },
+               { .id = 2, .text = "80ル2" },
+               { .id = 3, .text = "80ル3" },
+               { .id = 4, .text = "80ル4" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_912 = {
+       .pdu = setup_menu_912,
+       .pdu_len = sizeof(setup_menu_912),
+       .qualifier = 0x00,
+       .alpha_id = "80ル0",
+       .items = {
+               { .id = 0x11, .text = "80ル5" },
+               { .id = 0x12, .text = "80ル6" },
+       }
+};
+
+static struct setup_menu_test setup_menu_data_913 = {
+       .pdu = setup_menu_913,
+       .pdu_len = sizeof(setup_menu_913),
+       .qualifier = 0x00,
+       .alpha_id = ""
+};
+
+static struct setup_menu_test setup_menu_data_neg_1 = {
+       .pdu = setup_menu_neg_1,
+       .pdu_len = sizeof(setup_menu_neg_1)
+};
+
+static struct setup_menu_test setup_menu_data_neg_2 = {
+       .pdu = setup_menu_neg_2,
+       .pdu_len = sizeof(setup_menu_neg_2)
+};
+
+static struct setup_menu_test setup_menu_data_neg_3 = {
+       .pdu = setup_menu_neg_3,
+       .pdu_len = sizeof(setup_menu_neg_3)
+};
+
+static struct setup_menu_test setup_menu_data_neg_4 = {
+       .pdu = setup_menu_neg_4,
+       .pdu_len = sizeof(setup_menu_neg_4)
+};
+
+/* Defined in TS 102.384 Section 27.22.4.7 */
+static void test_setup_menu(gconstpointer data)
+{
+       const struct setup_menu_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SETUP_MENU);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_alpha_id(command->setup_menu.alpha_id, test->alpha_id);
+       check_items(command->setup_menu.items, test->items);
+       check_items_next_action_indicator(&command->setup_menu.next_act,
+                                               &test->next_act);
+       check_icon_id(&command->setup_menu.icon_id, &test->icon_id);
+       check_item_icon_id_list(&command->setup_menu.item_icon_id_list,
+                                       &test->item_icon_id_list);
+       check_text_attr(&command->setup_menu.text_attr, &test->text_attr);
+       check_item_text_attribute_list(&command->setup_menu.item_text_attr_list,
+                                       &test->item_text_attr_list);
+       check_text_attr_html(&command->setup_menu.text_attr,
+                               command->setup_menu.alpha_id, test->html);
+       stk_command_free(command);
+}
+
+static void test_setup_menu_missing_val(gconstpointer data)
+{
+       const struct setup_menu_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_MISSING_VALUE);
+
+       stk_command_free(command);
+}
+
+static void test_setup_menu_neg(gconstpointer data)
+{
+       const struct setup_menu_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD);
+
+       stk_command_free(command);
+}
+
+struct select_item_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       struct stk_item items[MAX_ITEM];
+       struct stk_items_next_action_indicator next_act;
+       unsigned char item_id;
+       struct stk_icon_id icon_id;
+       struct stk_item_icon_id_list item_icon_id_list;
+       struct stk_text_attribute text_attr;
+       struct stk_item_text_attribute_list item_text_attr_list;
+       struct stk_frame_id frame_id;
+       char *html;
+};
+
+static unsigned char select_item_111[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0x8F, 0x07,
+                                               0x04, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x34 };
+
+static unsigned char select_item_121[] = { 0xD0, 0x81, 0xFC, 0x81, 0x03, 0x01,
+                                               0x24, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0A, 0x4C, 0x61,
+                                               0x72, 0x67, 0x65, 0x4D, 0x65,
+                                               0x6E, 0x75, 0x31, 0x8F, 0x05,
+                                               0x50, 0x5A, 0x65, 0x72, 0x6F,
+                                               0x8F, 0x04, 0x4F, 0x4F, 0x6E,
+                                               0x65, 0x8F, 0x04, 0x4E, 0x54,
+                                               0x77, 0x6F, 0x8F, 0x06, 0x4D,
+                                               0x54, 0x68, 0x72, 0x65, 0x65,
+                                               0x8F, 0x05, 0x4C, 0x46, 0x6F,
+                                               0x75, 0x72, 0x8F, 0x05, 0x4B,
+                                               0x46, 0x69, 0x76, 0x65, 0x8F,
+                                               0x04, 0x4A, 0x53, 0x69, 0x78,
+                                               0x8F, 0x06, 0x49, 0x53, 0x65,
+                                               0x76, 0x65, 0x6E, 0x8F, 0x06,
+                                               0x48, 0x45, 0x69, 0x67, 0x68,
+                                               0x74, 0x8F, 0x05, 0x47, 0x4E,
+                                               0x69, 0x6E, 0x65, 0x8F, 0x06,
+                                               0x46, 0x41, 0x6C, 0x70, 0x68,
+                                               0x61, 0x8F, 0x06, 0x45, 0x42,
+                                               0x72, 0x61, 0x76, 0x6F, 0x8F,
+                                               0x08, 0x44, 0x43, 0x68, 0x61,
+                                               0x72, 0x6C, 0x69, 0x65, 0x8F,
+                                               0x06, 0x43, 0x44, 0x65, 0x6C,
+                                               0x74, 0x61, 0x8F, 0x05, 0x42,
+                                               0x45, 0x63, 0x68, 0x6F, 0x8F,
+                                               0x09, 0x41, 0x46, 0x6F, 0x78,
+                                               0x2D, 0x74, 0x72, 0x6F, 0x74,
+                                               0x8F, 0x06, 0x40, 0x42, 0x6C,
+                                               0x61, 0x63, 0x6B, 0x8F, 0x06,
+                                               0x3F, 0x42, 0x72, 0x6F, 0x77,
+                                               0x6E, 0x8F, 0x04, 0x3E, 0x52,
+                                               0x65, 0x64, 0x8F, 0x07, 0x3D,
+                                               0x4F, 0x72, 0x61, 0x6E, 0x67,
+                                               0x65, 0x8F, 0x07, 0x3C, 0x59,
+                                               0x65, 0x6C, 0x6C, 0x6F, 0x77,
+                                               0x8F, 0x06, 0x3B, 0x47, 0x72,
+                                               0x65, 0x65, 0x6E, 0x8F, 0x05,
+                                               0x3A, 0x42, 0x6C, 0x75, 0x65,
+                                               0x8F, 0x07, 0x39, 0x56, 0x69,
+                                               0x6F, 0x6C, 0x65, 0x74, 0x8F,
+                                               0x05, 0x38, 0x47, 0x72, 0x65,
+                                               0x79, 0x8F, 0x06, 0x37, 0x57,
+                                               0x68, 0x69, 0x74, 0x65, 0x8F,
+                                               0x06, 0x36, 0x6D, 0x69, 0x6C,
+                                               0x6C, 0x69, 0x8F, 0x06, 0x35,
+                                               0x6D, 0x69, 0x63, 0x72, 0x6F,
+                                               0x8F, 0x05, 0x34, 0x6E, 0x61,
+                                               0x6E, 0x6F, 0x8F, 0x05, 0x33,
+                                               0x70, 0x69, 0x63, 0x6F };
+
+static unsigned char select_item_131[] = { 0xD0, 0x81, 0xFB, 0x81, 0x03, 0x01,
+                                               0x24, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0A, 0x4C, 0x61,
+                                               0x72, 0x67, 0x65, 0x4D, 0x65,
+                                               0x6E, 0x75, 0x32, 0x8F, 0x1E,
+                                               0xFF, 0x43, 0x61, 0x6C, 0x6C,
+                                               0x20, 0x46, 0x6F, 0x72, 0x77,
+                                               0x61, 0x72, 0x64, 0x69, 0x6E,
+                                               0x67, 0x20, 0x55, 0x6E, 0x63,
+                                               0x6F, 0x6E, 0x64, 0x69, 0x74,
+                                               0x69, 0x6F, 0x6E, 0x61, 0x6C,
+                                               0x8F, 0x1D, 0xFE, 0x43, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x46, 0x6F,
+                                               0x72, 0x77, 0x61, 0x72, 0x64,
+                                               0x69, 0x6E, 0x67, 0x20, 0x4F,
+                                               0x6E, 0x20, 0x55, 0x73, 0x65,
+                                               0x72, 0x20, 0x42, 0x75, 0x73,
+                                               0x79, 0x8F, 0x1C, 0xFD, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x46,
+                                               0x6F, 0x72, 0x77, 0x61, 0x72,
+                                               0x64, 0x69, 0x6E, 0x67, 0x20,
+                                               0x4F, 0x6E, 0x20, 0x4E, 0x6F,
+                                               0x20, 0x52, 0x65, 0x70, 0x6C,
+                                               0x79, 0x8F, 0x26, 0xFC, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x46,
+                                               0x6F, 0x72, 0x77, 0x61, 0x72,
+                                               0x64, 0x69, 0x6E, 0x67, 0x20,
+                                               0x4F, 0x6E, 0x20, 0x55, 0x73,
+                                               0x65, 0x72, 0x20, 0x4E, 0x6F,
+                                               0x74, 0x20, 0x52, 0x65, 0x61,
+                                               0x63, 0x68, 0x61, 0x62, 0x6C,
+                                               0x65, 0x8F, 0x1E, 0xFB, 0x42,
+                                               0x61, 0x72, 0x72, 0x69, 0x6E,
+                                               0x67, 0x20, 0x4F, 0x66, 0x20,
+                                               0x41, 0x6C, 0x6C, 0x20, 0x4F,
+                                               0x75, 0x74, 0x67, 0x6F, 0x69,
+                                               0x6E, 0x67, 0x20, 0x43, 0x61,
+                                               0x6C, 0x6C, 0x73, 0x8F, 0x2C,
+                                               0xFA, 0x42, 0x61, 0x72, 0x72,
+                                               0x69, 0x6E, 0x67, 0x20, 0x4F,
+                                               0x66, 0x20, 0x41, 0x6C, 0x6C,
+                                               0x20, 0x4F, 0x75, 0x74, 0x67,
+                                               0x6F, 0x69, 0x6E, 0x67, 0x20,
+                                               0x49, 0x6E, 0x74, 0x65, 0x72,
+                                               0x6E, 0x61, 0x74, 0x69, 0x6F,
+                                               0x6E, 0x61, 0x6C, 0x20, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x73, 0x8F,
+                                               0x11, 0xF9, 0x43, 0x4C, 0x49,
+                                               0x20, 0x50, 0x72, 0x65, 0x73,
+                                               0x65, 0x6E, 0x74, 0x61, 0x74,
+                                               0x69, 0x6F, 0x6E };
+
+static unsigned char select_item_141[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6C,
+                                               0x65, 0x63, 0x74, 0x20, 0x49,
+                                               0x74, 0x65, 0x6D, 0x8F, 0x04,
+                                               0x11, 0x4F, 0x6E, 0x65, 0x8F,
+                                               0x04, 0x12, 0x54, 0x77, 0x6F };
+
+static unsigned char select_item_151[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01,
+                                               0x24, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x81, 0xED, 0x54,
+                                               0x68, 0x65, 0x20, 0x53, 0x49,
+                                               0x4D, 0x20, 0x73, 0x68, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x73, 0x75,
+                                               0x70, 0x70, 0x6C, 0x79, 0x20,
+                                               0x61, 0x20, 0x73, 0x65, 0x74,
+                                               0x20, 0x6F, 0x66, 0x20, 0x69,
+                                               0x74, 0x65, 0x6D, 0x73, 0x20,
+                                               0x66, 0x72, 0x6F, 0x6D, 0x20,
+                                               0x77, 0x68, 0x69, 0x63, 0x68,
+                                               0x20, 0x74, 0x68, 0x65, 0x20,
+                                               0x75, 0x73, 0x65, 0x72, 0x20,
+                                               0x6D, 0x61, 0x79, 0x20, 0x63,
+                                               0x68, 0x6F, 0x6F, 0x73, 0x65,
+                                               0x20, 0x6F, 0x6E, 0x65, 0x2E,
+                                               0x20, 0x45, 0x61, 0x63, 0x68,
+                                               0x20, 0x69, 0x74, 0x65, 0x6D,
+                                               0x20, 0x63, 0x6F, 0x6D, 0x70,
+                                               0x72, 0x69, 0x73, 0x65, 0x73,
+                                               0x20, 0x61, 0x20, 0x73, 0x68,
+                                               0x6F, 0x72, 0x74, 0x20, 0x69,
+                                               0x64, 0x65, 0x6E, 0x74, 0x69,
+                                               0x66, 0x69, 0x65, 0x72, 0x20,
+                                               0x28, 0x75, 0x73, 0x65, 0x64,
+                                               0x20, 0x74, 0x6F, 0x20, 0x69,
+                                               0x6E, 0x64, 0x69, 0x63, 0x61,
+                                               0x74, 0x65, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x73, 0x65, 0x6C,
+                                               0x65, 0x63, 0x74, 0x69, 0x6F,
+                                               0x6E, 0x29, 0x20, 0x61, 0x6E,
+                                               0x64, 0x20, 0x61, 0x20, 0x74,
+                                               0x65, 0x78, 0x74, 0x20, 0x73,
+                                               0x74, 0x72, 0x69, 0x6E, 0x67,
+                                               0x2E, 0x20, 0x4F, 0x70, 0x74,
+                                               0x69, 0x6F, 0x6E, 0x61, 0x6C,
+                                               0x6C, 0x79, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x53, 0x49, 0x4D,
+                                               0x20, 0x6D, 0x61, 0x79, 0x20,
+                                               0x69, 0x6E, 0x63, 0x6C, 0x75,
+                                               0x64, 0x65, 0x20, 0x61, 0x6E,
+                                               0x20, 0x61, 0x6C, 0x70, 0x68,
+                                               0x61, 0x20, 0x69, 0x64, 0x65,
+                                               0x6E, 0x74, 0x69, 0x66, 0x69,
+                                               0x65, 0x72, 0x2E, 0x20, 0x54,
+                                               0x68, 0x65, 0x20, 0x61, 0x6C,
+                                               0x70, 0x68, 0x61, 0x20, 0x69,
+                                               0x64, 0x65, 0x6E, 0x74, 0x69,
+                                               0x66, 0x69, 0x65, 0x72, 0x20,
+                                               0x69, 0x8F, 0x02, 0x01, 0x59 };
+
+static unsigned char select_item_161[] = { 0xD0, 0x81, 0xF3, 0x81, 0x03, 0x01,
+                                               0x24, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0A, 0x30, 0x4C,
+                                               0x61, 0x72, 0x67, 0x65, 0x4D,
+                                               0x65, 0x6E, 0x75, 0x8F, 0x1D,
+                                               0xFF, 0x31, 0x20, 0x43, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x46, 0x6F,
+                                               0x72, 0x77, 0x61, 0x72, 0x64,
+                                               0x20, 0x55, 0x6E, 0x63, 0x6F,
+                                               0x6E, 0x64, 0x69, 0x74, 0x69,
+                                               0x6F, 0x6E, 0x61, 0x6C, 0x8F,
+                                               0x1C, 0xFE, 0x32, 0x20, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x46,
+                                               0x6F, 0x72, 0x77, 0x61, 0x72,
+                                               0x64, 0x20, 0x4F, 0x6E, 0x20,
+                                               0x55, 0x73, 0x65, 0x72, 0x20,
+                                               0x42, 0x75, 0x73, 0x79, 0x8F,
+                                               0x1B, 0xFD, 0x33, 0x20, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x46,
+                                               0x6F, 0x72, 0x77, 0x61, 0x72,
+                                               0x64, 0x20, 0x4F, 0x6E, 0x20,
+                                               0x4E, 0x6F, 0x20, 0x52, 0x65,
+                                               0x70, 0x6C, 0x79, 0x8F, 0x25,
+                                               0xFC, 0x34, 0x20, 0x43, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x46, 0x6F,
+                                               0x72, 0x77, 0x61, 0x72, 0x64,
+                                               0x20, 0x4F, 0x6E, 0x20, 0x55,
+                                               0x73, 0x65, 0x72, 0x20, 0x4E,
+                                               0x6F, 0x74, 0x20, 0x52, 0x65,
+                                               0x61, 0x63, 0x68, 0x61, 0x62,
+                                               0x6C, 0x65, 0x8F, 0x20, 0xFB,
+                                               0x35, 0x20, 0x42, 0x61, 0x72,
+                                               0x72, 0x69, 0x6E, 0x67, 0x20,
+                                               0x4F, 0x66, 0x20, 0x41, 0x6C,
+                                               0x6C, 0x20, 0x4F, 0x75, 0x74,
+                                               0x67, 0x6F, 0x69, 0x6E, 0x67,
+                                               0x20, 0x43, 0x61, 0x6C, 0x6C,
+                                               0x73, 0x8F, 0x24, 0xFA, 0x36,
+                                               0x20, 0x42, 0x61, 0x72, 0x72,
+                                               0x69, 0x6E, 0x67, 0x20, 0x4F,
+                                               0x66, 0x20, 0x41, 0x6C, 0x6C,
+                                               0x20, 0x4F, 0x75, 0x74, 0x67,
+                                               0x6F, 0x69, 0x6E, 0x67, 0x20,
+                                               0x49, 0x6E, 0x74, 0x20, 0x43,
+                                               0x61, 0x6C, 0x6C, 0x73, 0x8F,
+                                               0x13, 0xF9, 0x37, 0x20, 0x43,
+                                               0x4C, 0x49, 0x20, 0x50, 0x72,
+                                               0x65, 0x73, 0x65, 0x6E, 0x74,
+                                               0x61, 0x74, 0x69, 0x6F, 0x6E };
+
+static unsigned char select_item_211[] = { 0xD0, 0x39, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0x18, 0x03,
+                                               0x13, 0x10, 0x26 };
+
+static unsigned char select_item_311[] = { 0xD0, 0x37, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0x90, 0x01,
+                                               0x02 };
+
+static unsigned char select_item_411[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24,
+                                               0x80, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33 };
+
+static unsigned char select_item_511[] = { 0xD0, 0x3E, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0x9E, 0x02,
+                                               0x01, 0x01, 0x9F, 0x04, 0x01,
+                                               0x05, 0x05, 0x05 };
+
+static unsigned char select_item_521[] = { 0xD0, 0x3E, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33, 0x9E, 0x02,
+                                               0x00, 0x01, 0x9F, 0x04, 0x00,
+                                               0x05, 0x05, 0x05 };
+
+static unsigned char select_item_611[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24,
+                                               0x03, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33 };
+
+static unsigned char select_item_621[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32, 0x8F,
+                                               0x07, 0x03, 0x49, 0x74, 0x65,
+                                               0x6D, 0x20, 0x33 };
+
+static unsigned char select_item_711[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x24,
+                                               0x04, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0E, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x8F, 0x07, 0x01, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x31,
+                                               0x8F, 0x07, 0x02, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x32 };
+
+static unsigned char select_item_811[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0A, 0x3C, 0x54, 0x49,
+                                               0x4D, 0x45, 0x2D, 0x4F, 0x55,
+                                               0x54, 0x3E, 0x8F, 0x07, 0x01,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x31, 0x8F, 0x07, 0x02, 0x49,
+                                               0x74, 0x65, 0x6D, 0x20, 0x32,
+                                               0x8F, 0x07, 0x03, 0x49, 0x74,
+                                               0x65, 0x6D, 0x20, 0x33 };
+
+static unsigned char select_item_911[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_912[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34 };
+
+static unsigned char select_item_921[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x01, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x01, 0xB4, 0x00, 0x06,
+                                               0x01, 0xB4 };
+
+static unsigned char select_item_922[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34 };
+
+static unsigned char select_item_931[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x02, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x02, 0xB4, 0x00, 0x06,
+                                               0x02, 0xB4 };
+
+static unsigned char select_item_932[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34 };
+
+static unsigned char select_item_941[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x04, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x04, 0xB4, 0x00, 0x06,
+                                               0x04, 0xB4 };
+
+static unsigned char select_item_942[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_943[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x33, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x35, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x36 };
+
+static unsigned char select_item_951[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x08, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x08, 0xB4, 0x00, 0x06,
+                                               0x08, 0xB4 };
+
+static unsigned char select_item_952[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_953[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x33, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x35, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x36 };
+
+static unsigned char select_item_961[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x10, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x10, 0xB4, 0x00, 0x06,
+                                               0x10, 0xB4 };
+
+static unsigned char select_item_962[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_963[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x33, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x35, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x36 };
+
+static unsigned char select_item_971[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x20, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x20, 0xB4, 0x00, 0x06,
+                                               0x20, 0xB4 };
+
+static unsigned char select_item_972[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_973[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x33, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x35, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x36 };
+
+static unsigned char select_item_981[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x40, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x40, 0xB4, 0x00, 0x06,
+                                               0x40, 0xB4 };
+
+static unsigned char select_item_982[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_983[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x33, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x35, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x36 };
+
+static unsigned char select_item_991[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x80, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x80, 0xB4, 0x00, 0x06,
+                                               0x80, 0xB4 };
+
+static unsigned char select_item_992[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_993[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x33, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x35, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x36 };
+
+static unsigned char select_item_9101[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x31, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x31, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4, 0xD1, 0x08, 0x00,
+                                               0x06, 0x00, 0xB4, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char select_item_9102[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x54, 0x6F, 0x6F,
+                                               0x6C, 0x6B, 0x69, 0x74, 0x20,
+                                               0x53, 0x65, 0x6C, 0x65, 0x63,
+                                               0x74, 0x20, 0x32, 0x8F, 0x07,
+                                               0x01, 0x49, 0x74, 0x65, 0x6D,
+                                               0x20, 0x33, 0x8F, 0x07, 0x02,
+                                               0x49, 0x74, 0x65, 0x6D, 0x20,
+                                               0x34 };
+
+static unsigned char select_item_1011[] = { 0xD0, 0x7E, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x8F, 0x1C, 0x01,
+                                               0x80, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x00, 0x31, 0x8F, 0x1C, 0x02,
+                                               0x80, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x00, 0x32, 0x8F, 0x1C, 0x03,
+                                               0x80, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x00, 0x33 };
+
+static unsigned char select_item_1021[] = { 0xD0, 0x53, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0F, 0x81, 0x0C, 0x08,
+                                               0x97, 0x94, 0xA0, 0x90, 0x92,
+                                               0xA1, 0xA2, 0x92, 0xA3, 0x99,
+                                               0xA2, 0x95, 0x8F, 0x11, 0x01,
+                                               0x81, 0x0D, 0x08, 0x97, 0x94,
+                                               0xA0, 0x90, 0x92, 0xA1, 0xA2,
+                                               0x92, 0xA3, 0x99, 0xA2, 0x95,
+                                               0x31, 0x8F, 0x11, 0x02, 0x81,
+                                               0x0D, 0x08, 0x97, 0x94, 0xA0,
+                                               0x90, 0x92, 0xA1, 0xA2, 0x92,
+                                               0xA3, 0x99, 0xA2, 0x95, 0x32,
+                                               0x8F, 0x11, 0x03, 0x81, 0x0D,
+                                               0x08, 0x97, 0x94, 0xA0, 0x90,
+                                               0x92, 0xA1, 0xA2, 0x92, 0xA3,
+                                               0x99, 0xA2, 0x95, 0x33 };
+
+static unsigned char select_item_1031[] = { 0xD0, 0x57, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x10, 0x82, 0x0C, 0x04,
+                                               0x10, 0x87, 0x84, 0x90, 0x80,
+                                               0x82, 0x91, 0x92, 0x82, 0x93,
+                                               0x89, 0x92, 0x85, 0x8F, 0x12,
+                                               0x01, 0x82, 0x0D, 0x04, 0x10,
+                                               0x87, 0x84, 0x90, 0x80, 0x82,
+                                               0x91, 0x92, 0x82, 0x93, 0x89,
+                                               0x92, 0x85, 0x31, 0x8F, 0x12,
+                                               0x02, 0x82, 0x0D, 0x04, 0x10,
+                                               0x87, 0x84, 0x90, 0x80, 0x82,
+                                               0x91, 0x92, 0x82, 0x93, 0x89,
+                                               0x92, 0x85, 0x32, 0x8F, 0x12,
+                                               0x03, 0x82, 0x0D, 0x04, 0x10,
+                                               0x87, 0x84, 0x90, 0x80, 0x82,
+                                               0x91, 0x92, 0x82, 0x93, 0x89,
+                                               0x92, 0x85, 0x33 };
+
+static unsigned char select_item_1111[] = { 0xD0, 0x3E, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x0B, 0x80, 0x5D, 0xE5,
+                                               0x51, 0x77, 0x7B, 0xB1, 0x90,
+                                               0x09, 0x62, 0xE9, 0x8F, 0x08,
+                                               0x01, 0x80, 0x98, 0x79, 0x76,
+                                               0xEE, 0x4E, 0x00, 0x8F, 0x08,
+                                               0x02, 0x80, 0x98, 0x79, 0x76,
+                                               0xEE, 0x4E, 0x8C, 0x8F, 0x08,
+                                               0x03, 0x80, 0x98, 0x79, 0x76,
+                                               0xEE, 0x4E, 0x09, 0x8F, 0x08,
+                                               0x04, 0x80, 0x98, 0x79, 0x76,
+                                               0xEE, 0x56, 0xDB };
+
+static unsigned char select_item_1211[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x09, 0x80, 0x00, 0x38,
+                                               0x00, 0x30, 0x30, 0xEB, 0x00,
+                                               0x30, 0x8F, 0x0A, 0x01, 0x80,
+                                               0x00, 0x38, 0x00, 0x30, 0x30,
+                                               0xEB, 0x00, 0x31, 0x8F, 0x0A,
+                                               0x02, 0x80, 0x00, 0x38, 0x00,
+                                               0x30, 0x30, 0xEB, 0x00, 0x32,
+                                               0x8F, 0x0A, 0x03, 0x80, 0x00,
+                                               0x38, 0x00, 0x30, 0x30, 0xEB,
+                                               0x00, 0x33 };
+
+static unsigned char select_item_1221[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x07, 0x81, 0x04, 0x61,
+                                               0x38, 0x31, 0xEB, 0x30, 0x8F,
+                                               0x08, 0x01, 0x81, 0x04, 0x61,
+                                               0x38, 0x31, 0xEB, 0x31, 0x8F,
+                                               0x08, 0x02, 0x81, 0x04, 0x61,
+                                               0x38, 0x31, 0xEB, 0x32, 0x8F,
+                                               0x08, 0x03, 0x81, 0x04, 0x61,
+                                               0x38, 0x31, 0xEB, 0x33 };
+
+static unsigned char select_item_1231[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0x85, 0x08, 0x82, 0x04, 0x30,
+                                               0xA0, 0x38, 0x32, 0xCB, 0x30,
+                                               0x8F, 0x09, 0x01, 0x82, 0x04,
+                                               0x30, 0xA0, 0x38, 0x32, 0xCB,
+                                               0x31, 0x8F, 0x09, 0x02, 0x82,
+                                               0x04, 0x30, 0xA0, 0x38, 0x32,
+                                               0xCB, 0x32, 0x8F, 0x09, 0x03,
+                                               0x82, 0x04, 0x30, 0xA0, 0x38,
+                                               0x32, 0xCB, 0x33 };
+
+static struct select_item_test select_item_data_111 = {
+       .pdu = select_item_111,
+       .pdu_len = sizeof(select_item_111),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+               { .id = 4, .text = "Item 4" },
+       }
+};
+
+static struct select_item_test select_item_data_121 = {
+       .pdu = select_item_121,
+       .pdu_len = sizeof(select_item_121),
+       .qualifier = 0x00,
+       .alpha_id = "LargeMenu1",
+       .items = {
+               { .id = 0x50, .text = "Zero" },
+               { .id = 0x4F, .text = "One" },
+               { .id = 0x4E, .text = "Two" },
+               { .id = 0x4D, .text = "Three" },
+               { .id = 0x4C, .text = "Four" },
+               { .id = 0x4B, .text = "Five" },
+               { .id = 0x4A, .text = "Six" },
+               { .id = 0x49, .text = "Seven" },
+               { .id = 0x48, .text = "Eight" },
+               { .id = 0x47, .text = "Nine" },
+               { .id = 0x46, .text = "Alpha" },
+               { .id = 0x45, .text = "Bravo" },
+               { .id = 0x44, .text = "Charlie" },
+               { .id = 0x43, .text = "Delta" },
+               { .id = 0x42, .text = "Echo" },
+               { .id = 0x41, .text = "Fox-trot" },
+               { .id = 0x40, .text = "Black" },
+               { .id = 0x3F, .text = "Brown" },
+               { .id = 0x3E, .text = "Red" },
+               { .id = 0x3D, .text = "Orange" },
+               { .id = 0x3C, .text = "Yellow" },
+               { .id = 0x3B, .text = "Green" },
+               { .id = 0x3A, .text = "Blue" },
+               { .id = 0x39, .text = "Violet" },
+               { .id = 0x38, .text = "Grey" },
+               { .id = 0x37, .text = "White" },
+               { .id = 0x36, .text = "milli" },
+               { .id = 0x35, .text = "micro" },
+               { .id = 0x34, .text = "nano" },
+               { .id = 0x33, .text = "pico" },
+       }
+};
+
+static struct select_item_test select_item_data_131 = {
+       .pdu = select_item_131,
+       .pdu_len = sizeof(select_item_131),
+       .qualifier = 0x00,
+       .alpha_id = "LargeMenu2",
+       .items = {
+               { .id = 0xFF, .text = "Call Forwarding Unconditional" },
+               { .id = 0xFE, .text = "Call Forwarding On User Busy" },
+               { .id = 0xFD, .text = "Call Forwarding On No Reply" },
+               { .id = 0xFC, .text = "Call Forwarding On User Not Reachable" },
+               { .id = 0xFB, .text = "Barring Of All Outgoing Calls" },
+               { .id = 0xFA,
+                       .text = "Barring Of All Outgoing International Calls" },
+               { .id = 0xF9, .text = "CLI Presentation" },
+       }
+};
+
+static struct select_item_test select_item_data_141 = {
+       .pdu = select_item_141,
+       .pdu_len = sizeof(select_item_141),
+       .qualifier = 0x00,
+       .alpha_id = "Select Item",
+       .items = {
+               { .id = 0x11, .text = "One" },
+               { .id = 0x12, .text = "Two" },
+       }
+};
+
+static struct select_item_test select_item_data_151 = {
+       .pdu = select_item_151,
+       .pdu_len = sizeof(select_item_151),
+       .qualifier = 0x00,
+       .alpha_id = "The SIM shall supply a set of items from which the user "
+               "may choose one. Each item comprises a short identifier (used "
+               "to indicate the selection) and a text string. Optionally the "
+               "SIM may include an alpha identifier. The alpha identifier i",
+       .items = {
+               { .id = 0x01, .text = "Y" },
+       }
+};
+
+static struct select_item_test select_item_data_161 = {
+       .pdu = select_item_161,
+       .pdu_len = sizeof(select_item_161),
+       .qualifier = 0x00,
+       .alpha_id = "0LargeMenu",
+       .items = {
+               { .id = 0xFF, .text = "1 Call Forward Unconditional" },
+               { .id = 0xFE, .text = "2 Call Forward On User Busy" },
+               { .id = 0xFD, .text = "3 Call Forward On No Reply" },
+               { .id = 0xFC, .text = "4 Call Forward On User Not Reachable" },
+               { .id = 0xFB, .text = "5 Barring Of All Outgoing Calls" },
+               { .id = 0xFA, .text = "6 Barring Of All Outgoing Int Calls" },
+               { .id = 0xF9, .text = "7 CLI Presentation" },
+       }
+};
+
+static struct select_item_test select_item_data_211 = {
+       .pdu = select_item_211,
+       .pdu_len = sizeof(select_item_211),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .next_act = {
+               .list = { STK_COMMAND_TYPE_SEND_SMS,
+                               STK_COMMAND_TYPE_SETUP_CALL,
+                               STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO},
+               .len = 3
+       }
+};
+
+static struct select_item_test select_item_data_311 = {
+       .pdu = select_item_311,
+       .pdu_len = sizeof(select_item_311),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .item_id = 0x02
+};
+
+static struct select_item_test select_item_data_411 = {
+       .pdu = select_item_411,
+       .pdu_len = sizeof(select_item_411),
+       .qualifier = 0x80,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       }
+};
+
+static struct select_item_test select_item_data_511 = {
+       .pdu = select_item_511,
+       .pdu_len = sizeof(select_item_511),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 1
+       },
+       .item_icon_id_list = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .list = { 5, 5, 5 },
+               .len = 3
+       }
+};
+
+static struct select_item_test select_item_data_521 = {
+       .pdu = select_item_521,
+       .pdu_len = sizeof(select_item_521),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 1
+       },
+       .item_icon_id_list = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .list = { 5, 5, 5 },
+               .len = 3
+       }
+};
+
+static struct select_item_test select_item_data_611 = {
+       .pdu = select_item_611,
+       .pdu_len = sizeof(select_item_611),
+       .qualifier = 0x03,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       }
+};
+
+static struct select_item_test select_item_data_621 = {
+       .pdu = select_item_621,
+       .pdu_len = sizeof(select_item_621),
+       .qualifier = 0x01,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       }
+};
+
+static struct select_item_test select_item_data_711 = {
+       .pdu = select_item_711,
+       .pdu_len = sizeof(select_item_711),
+       .qualifier = 0x04,
+       .alpha_id = "Toolkit Select",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       }
+};
+
+static struct select_item_test select_item_data_811 = {
+       .pdu = select_item_811,
+       .pdu_len = sizeof(select_item_811),
+       .qualifier = 0x00,
+       .alpha_id = "<TIME-OUT>",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+               { .id = 3, .text = "Item 3" },
+       }
+};
+
+static struct select_item_test select_item_data_911 = {
+       .pdu = select_item_911,
+       .pdu_len = sizeof(select_item_911),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 1</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_912 = {
+       .pdu = select_item_912,
+       .pdu_len = sizeof(select_item_912),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       }
+};
+
+static struct select_item_test select_item_data_921 = {
+       .pdu = select_item_921,
+       .pdu_len = sizeof(select_item_921),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4 }
+       },
+       .html = "<div style=\"text-align: center;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 1</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_922 = {
+       .pdu = select_item_922,
+       .pdu_len = sizeof(select_item_922),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       }
+};
+
+static struct select_item_test select_item_data_931 = {
+       .pdu = select_item_931,
+       .pdu_len = sizeof(select_item_931),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4 }
+       },
+       .html = "<div style=\"text-align: right;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 1</span>"
+               "</div>"
+};
+
+static struct select_item_test select_item_data_932 = {
+       .pdu = select_item_932,
+       .pdu_len = sizeof(select_item_932),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       }
+};
+
+static struct select_item_test select_item_data_941 = {
+       .pdu = select_item_941,
+       .pdu_len = sizeof(select_item_941),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "big;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Select 1</span></div>",
+};
+
+static struct select_item_test select_item_data_942 = {
+       .pdu = select_item_942,
+       .pdu_len = sizeof(select_item_942),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 2</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_943 = {
+       .pdu = select_item_943,
+       .pdu_len = sizeof(select_item_943),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 3",
+       .items = {
+               { .id = 1, .text = "Item 5" },
+               { .id = 2, .text = "Item 6" },
+       }
+};
+
+static struct select_item_test select_item_data_951 = {
+       .pdu = select_item_951,
+       .pdu_len = sizeof(select_item_951),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "small;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Select 1</span></div>",
+};
+
+static struct select_item_test select_item_data_952 = {
+       .pdu = select_item_952,
+       .pdu_len = sizeof(select_item_952),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 2</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_953 = {
+       .pdu = select_item_953,
+       .pdu_len = sizeof(select_item_953),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 3",
+       .items = {
+               { .id = 1, .text = "Item 5" },
+               { .id = 2, .text = "Item 6" },
+       }
+};
+
+static struct select_item_test select_item_data_961 = {
+       .pdu = select_item_961,
+       .pdu_len = sizeof(select_item_961),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x10, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-weight: "
+               "bold;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Select 1</span></div>",
+};
+
+static struct select_item_test select_item_data_962 = {
+       .pdu = select_item_962,
+       .pdu_len = sizeof(select_item_962),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 2</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_963 = {
+       .pdu = select_item_963,
+       .pdu_len = sizeof(select_item_963),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 3",
+       .items = {
+               { .id = 1, .text = "Item 5" },
+               { .id = 2, .text = "Item 6" },
+       }
+};
+
+static struct select_item_test select_item_data_971 = {
+       .pdu = select_item_971,
+       .pdu_len = sizeof(select_item_971),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x20, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-style: "
+               "italic;color: #347235;background-color: #FFFF00;\">"
+               "Toolkit Select 1</span></div>"
+};
+
+static struct select_item_test select_item_data_972 = {
+       .pdu = select_item_972,
+       .pdu_len = sizeof(select_item_972),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 2</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_973 = {
+       .pdu = select_item_973,
+       .pdu_len = sizeof(select_item_973),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 3",
+       .items = {
+               { .id = 1, .text = "Item 5" },
+               { .id = 2, .text = "Item 6" },
+       }
+};
+
+static struct select_item_test select_item_data_981 = {
+       .pdu = select_item_981,
+       .pdu_len = sizeof(select_item_981),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: underline;color: #347235;"
+               "background-color: #FFFF00;\">Toolkit Select 1</span></div>",
+};
+
+static struct select_item_test select_item_data_982 = {
+       .pdu = select_item_982,
+       .pdu_len = sizeof(select_item_982),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 2</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_983 = {
+       .pdu = select_item_983,
+       .pdu_len = sizeof(select_item_983),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 3",
+       .items = {
+               { .id = 1, .text = "Item 5" },
+               { .id = 2, .text = "Item 6" },
+       }
+};
+
+static struct select_item_test select_item_data_991 = {
+       .pdu = select_item_991,
+       .pdu_len = sizeof(select_item_991),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: line-through;color: #347235;"
+               "background-color: #FFFF00;\">Toolkit Select 1</span></div>",
+};
+
+static struct select_item_test select_item_data_992 = {
+       .pdu = select_item_992,
+       .pdu_len = sizeof(select_item_992),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 2</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_993 = {
+       .pdu = select_item_993,
+       .pdu_len = sizeof(select_item_993),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 3",
+       .items = {
+               { .id = 1, .text = "Item 5" },
+               { .id = 2, .text = "Item 6" },
+       }
+};
+
+static struct select_item_test select_item_data_9101 = {
+       .pdu = select_item_9101,
+       .pdu_len = sizeof(select_item_9101),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 1",
+       .items = {
+               { .id = 1, .text = "Item 1" },
+               { .id = 2, .text = "Item 2" },
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .item_text_attr_list = {
+               .len = 8,
+               .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Toolkit Select 1</span>"
+               "</div>",
+};
+
+static struct select_item_test select_item_data_9102 = {
+       .pdu = select_item_9102,
+       .pdu_len = sizeof(select_item_9102),
+       .qualifier = 0x00,
+       .alpha_id = "Toolkit Select 2",
+       .items = {
+               { .id = 1, .text = "Item 3" },
+               { .id = 2, .text = "Item 4" },
+       }
+};
+
+static struct select_item_test select_item_data_1011 = {
+       .pdu = select_item_1011,
+       .pdu_len = sizeof(select_item_1011),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .items = {
+               { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" },
+               { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" },
+               { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" },
+       }
+};
+
+static struct select_item_test select_item_data_1021 = {
+       .pdu = select_item_1021,
+       .pdu_len = sizeof(select_item_1021),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .items = {
+               { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" },
+               { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" },
+               { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" },
+       }
+};
+
+static struct select_item_test select_item_data_1031 = {
+       .pdu = select_item_1031,
+       .pdu_len = sizeof(select_item_1031),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .items = {
+               { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" },
+               { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" },
+               { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" },
+       }
+};
+
+static struct select_item_test select_item_data_1111 = {
+       .pdu = select_item_1111,
+       .pdu_len = sizeof(select_item_1111),
+       .qualifier = 0x00,
+       .alpha_id = "工具箱选择",
+       .items = {
+               { .id = 1, .text = "项目一" },
+               { .id = 2, .text = "项目二" },
+               { .id = 3, .text = "项目三" },
+               { .id = 4, .text = "项目四" },
+       }
+};
+
+static struct select_item_test select_item_data_1211 = {
+       .pdu = select_item_1211,
+       .pdu_len = sizeof(select_item_1211),
+       .qualifier = 0x00,
+       .alpha_id = "80ル0",
+       .items = {
+               { .id = 1, .text = "80ル1" },
+               { .id = 2, .text = "80ル2" },
+               { .id = 3, .text = "80ル3" },
+       }
+};
+
+static struct select_item_test select_item_data_1221 = {
+       .pdu = select_item_1221,
+       .pdu_len = sizeof(select_item_1221),
+       .qualifier = 0x00,
+       .alpha_id = "81ル0",
+       .items = {
+               { .id = 1, .text = "81ル1" },
+               { .id = 2, .text = "81ル2" },
+               { .id = 3, .text = "81ル3" },
+       }
+};
+
+static struct select_item_test select_item_data_1231 = {
+       .pdu = select_item_1231,
+       .pdu_len = sizeof(select_item_1231),
+       .qualifier = 0x00,
+       .alpha_id = "82ル0",
+       .items = {
+               { .id = 1, .text = "82ル1" },
+               { .id = 2, .text = "82ル2" },
+               { .id = 3, .text = "82ル3" },
+       }
+};
+
+static void test_select_item(gconstpointer data)
+{
+       const struct select_item_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SELECT_ITEM);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_alpha_id(command->select_item.alpha_id, test->alpha_id);
+       check_items(command->select_item.items, test->items);
+       check_items_next_action_indicator(&command->select_item.next_act,
+                                               &test->next_act);
+       check_item_id(command->select_item.item_id, test->item_id);
+       check_icon_id(&command->select_item.icon_id, &test->icon_id);
+       check_item_icon_id_list(&command->select_item.item_icon_id_list,
+                                       &test->item_icon_id_list);
+       check_text_attr(&command->select_item.text_attr, &test->text_attr);
+       check_item_text_attribute_list(
+                               &command->select_item.item_text_attr_list,
+                               &test->item_text_attr_list);
+       check_text_attr_html(&command->select_item.text_attr,
+                               command->select_item.alpha_id, test->html);
+       check_frame_id(&command->select_item.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct send_sms_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       struct sms_test gsm_sms;
+       struct stk_common_byte_array cdma_sms;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+/* 3GPP TS 31.124 Section 27.22.4.10.1.4.2 */
+static unsigned char send_sms_111[] = { 0xD0, 0x37, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x07, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x53, 0x4D, 0x86,
+                                               0x09, 0x91, 0x11, 0x22, 0x33,
+                                               0x44, 0x55, 0x66, 0x77, 0xF8,
+                                               0x8B, 0x18, 0x01, 0x00, 0x09,
+                                               0x91, 0x10, 0x32, 0x54, 0x76,
+                                               0xF8, 0x40, 0xF4, 0x0C, 0x54,
+                                               0x65, 0x73, 0x74, 0x20, 0x4D,
+                                               0x65, 0x73, 0x73, 0x61, 0x67,
+                                               0x65 };
+
+static unsigned char send_sms_121[] = { 0xD0, 0x32, 0x81, 0x03, 0x01, 0x13,
+                                               0x01, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x07, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x53, 0x4D, 0x86,
+                                               0x09, 0x91, 0x11, 0x22, 0x33,
+                                               0x44, 0x55, 0x66, 0x77, 0xF8,
+                                               0x8B, 0x13, 0x01, 0x00, 0x09,
+                                               0x91, 0x10, 0x32, 0x54, 0x76,
+                                               0xF8, 0x40, 0xF4, 0x07, 0x53,
+                                               0x65, 0x6E, 0x64, 0x20, 0x53,
+                                               0x4D };
+
+static unsigned char send_sms_131[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0D, 0x53, 0x68, 0x6F,
+                                               0x72, 0x74, 0x20, 0x4D, 0x65,
+                                               0x73, 0x73, 0x61, 0x67, 0x65,
+                                               0x86, 0x09, 0x91, 0x11, 0x22,
+                                               0x33, 0x44, 0x55, 0x66, 0x77,
+                                               0xF8, 0x8B, 0x18, 0x01, 0x00,
+                                               0x09, 0x91, 0x10, 0x32, 0x54,
+                                               0x76, 0xF8, 0x40, 0xF0, 0x0D,
+                                               0x53, 0xF4, 0x5B, 0x4E, 0x07,
+                                               0x35, 0xCB, 0xF3, 0x79, 0xF8,
+                                               0x5C, 0x06 };
+
+static unsigned char send_sms_141[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01,
+                                               0x13, 0x01, 0x82, 0x02, 0x81,
+                                               0x83, 0x85, 0x38, 0x54, 0x68,
+                                               0x65, 0x20, 0x61, 0x64, 0x64,
+                                               0x72, 0x65, 0x73, 0x73, 0x20,
+                                               0x64, 0x61, 0x74, 0x61, 0x20,
+                                               0x6F, 0x62, 0x6A, 0x65, 0x63,
+                                               0x74, 0x20, 0x68, 0x6F, 0x6C,
+                                               0x64, 0x73, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x52, 0x50, 0x11,
+                                               0x44, 0x65, 0x73, 0x74, 0x69,
+                                               0x6E, 0x61, 0x74, 0x69, 0x6F,
+                                               0x6E, 0x11, 0x41, 0x64, 0x64,
+                                               0x72, 0x65, 0x73, 0x73, 0x86,
+                                               0x09, 0x91, 0x11, 0x22, 0x33,
+                                               0x44, 0x55, 0x66, 0x77, 0xF8,
+                                               0x8B, 0x81, 0xAC, 0x01, 0x00,
+                                               0x09, 0x91, 0x10, 0x32, 0x54,
+                                               0x76, 0xF8, 0x40, 0xF4, 0xA0,
+                                               0x54, 0x77, 0x6F, 0x20, 0x74,
+                                               0x79, 0x70, 0x65, 0x73, 0x20,
+                                               0x61, 0x72, 0x65, 0x20, 0x64,
+                                               0x65, 0x66, 0x69, 0x6E, 0x65,
+                                               0x64, 0x3A, 0x20, 0x2D, 0x20,
+                                               0x41, 0x20, 0x73, 0x68, 0x6F,
+                                               0x72, 0x74, 0x20, 0x6D, 0x65,
+                                               0x73, 0x73, 0x61, 0x67, 0x65,
+                                               0x20, 0x74, 0x6F, 0x20, 0x62,
+                                               0x65, 0x20, 0x73, 0x65, 0x6E,
+                                               0x74, 0x20, 0x74, 0x6F, 0x20,
+                                               0x74, 0x68, 0x65, 0x20, 0x6E,
+                                               0x65, 0x74, 0x77, 0x6F, 0x72,
+                                               0x6B, 0x20, 0x69, 0x6E, 0x20,
+                                               0x61, 0x6E, 0x20, 0x53, 0x4D,
+                                               0x53, 0x2D, 0x53, 0x55, 0x42,
+                                               0x4D, 0x49, 0x54, 0x20, 0x6D,
+                                               0x65, 0x73, 0x73, 0x61, 0x67,
+                                               0x65, 0x2C, 0x20, 0x6F, 0x72,
+                                               0x20, 0x61, 0x6E, 0x20, 0x53,
+                                               0x4D, 0x53, 0x2D, 0x43, 0x4F,
+                                               0x4D, 0x4D, 0x41, 0x4E, 0x44,
+                                               0x20, 0x6D, 0x65, 0x73, 0x73,
+                                               0x61, 0x67, 0x65, 0x2C, 0x20,
+                                               0x77, 0x68, 0x65, 0x72, 0x65,
+                                               0x20, 0x74, 0x68, 0x65, 0x20,
+                                               0x75, 0x73, 0x65, 0x72, 0x20,
+                                               0x64, 0x61, 0x74, 0x61, 0x20,
+                                               0x63, 0x61, 0x6E, 0x20, 0x62,
+                                               0x65, 0x20, 0x70, 0x61, 0x73,
+                                               0x73, 0x65, 0x64, 0x20, 0x74,
+                                               0x72, 0x61, 0x6E, 0x73, 0x70 };
+
+static unsigned char send_sms_151[] = { 0xD0, 0x81, 0xE9, 0x81, 0x03, 0x01,
+                                               0x13, 0x00, 0x82, 0x02, 0x81,
+                                               0x83, 0x85, 0x38, 0x54, 0x68,
+                                               0x65, 0x20, 0x61, 0x64, 0x64,
+                                               0x72, 0x65, 0x73, 0x73, 0x20,
+                                               0x64, 0x61, 0x74, 0x61, 0x20,
+                                               0x6F, 0x62, 0x6A, 0x65, 0x63,
+                                               0x74, 0x20, 0x68, 0x6F, 0x6C,
+                                               0x64, 0x73, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x52, 0x50, 0x20,
+                                               0x44, 0x65, 0x73, 0x74, 0x69,
+                                               0x6E, 0x61, 0x74, 0x69, 0x6F,
+                                               0x6E, 0x20, 0x41, 0x64, 0x64,
+                                               0x72, 0x65, 0x73, 0x73, 0x86,
+                                               0x09, 0x91, 0x11, 0x22, 0x33,
+                                               0x44, 0x55, 0x66, 0x77, 0xF8,
+                                               0x8B, 0x81, 0x98, 0x01, 0x00,
+                                               0x09, 0x91, 0x10, 0x32, 0x54,
+                                               0x76, 0xF8, 0x40, 0xF0, 0xA0,
+                                               0xD4, 0xFB, 0x1B, 0x44, 0xCF,
+                                               0xC3, 0xCB, 0x73, 0x50, 0x58,
+                                               0x5E, 0x06, 0x91, 0xCB, 0xE6,
+                                               0xB4, 0xBB, 0x4C, 0xD6, 0x81,
+                                               0x5A, 0xA0, 0x20, 0x68, 0x8E,
+                                               0x7E, 0xCB, 0xE9, 0xA0, 0x76,
+                                               0x79, 0x3E, 0x0F, 0x9F, 0xCB,
+                                               0x20, 0xFA, 0x1B, 0x24, 0x2E,
+                                               0x83, 0xE6, 0x65, 0x37, 0x1D,
+                                               0x44, 0x7F, 0x83, 0xE8, 0xE8,
+                                               0x32, 0xC8, 0x5D, 0xA6, 0xDF,
+                                               0xDF, 0xF2, 0x35, 0x28, 0xED,
+                                               0x06, 0x85, 0xDD, 0xA0, 0x69,
+                                               0x73, 0xDA, 0x9A, 0x56, 0x85,
+                                               0xCD, 0x24, 0x15, 0xD4, 0x2E,
+                                               0xCF, 0xE7, 0xE1, 0x73, 0x99,
+                                               0x05, 0x7A, 0xCB, 0x41, 0x61,
+                                               0x37, 0x68, 0xDA, 0x9C, 0xB6,
+                                               0x86, 0xCF, 0x66, 0x33, 0xE8,
+                                               0x24, 0x82, 0xDA, 0xE5, 0xF9,
+                                               0x3C, 0x7C, 0x2E, 0xB3, 0x40,
+                                               0x77, 0x74, 0x59, 0x5E, 0x06,
+                                               0xD1, 0xD1, 0x65, 0x50, 0x7D,
+                                               0x5E, 0x96, 0x83, 0xC8, 0x61,
+                                               0x7A, 0x18, 0x34, 0x0E, 0xBB,
+                                               0x41, 0xE2, 0x32, 0x08, 0x1E,
+                                               0x9E, 0xCF, 0xCB, 0x64, 0x10,
+                                               0x5D, 0x1E, 0x76, 0xCF, 0xE1 };
+
+static unsigned char send_sms_161[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01,
+                                               0x13, 0x00, 0x82, 0x02, 0x81,
+                                               0x83, 0x85, 0x81, 0xE6, 0x54,
+                                               0x77, 0x6F, 0x20, 0x74, 0x79,
+                                               0x70, 0x65, 0x73, 0x20, 0x61,
+                                               0x72, 0x65, 0x20, 0x64, 0x65,
+                                               0x66, 0x69, 0x6E, 0x65, 0x64,
+                                               0x3A, 0x20, 0x2D, 0x20, 0x41,
+                                               0x20, 0x73, 0x68, 0x6F, 0x72,
+                                               0x74, 0x20, 0x6D, 0x65, 0x73,
+                                               0x73, 0x61, 0x67, 0x65, 0x20,
+                                               0x74, 0x6F, 0x20, 0x62, 0x65,
+                                               0x20, 0x73, 0x65, 0x6E, 0x74,
+                                               0x20, 0x74, 0x6F, 0x20, 0x74,
+                                               0x68, 0x65, 0x20, 0x6E, 0x65,
+                                               0x74, 0x77, 0x6F, 0x72, 0x6B,
+                                               0x20, 0x69, 0x6E, 0x20, 0x61,
+                                               0x6E, 0x20, 0x53, 0x4D, 0x53,
+                                               0x2D, 0x53, 0x55, 0x42, 0x4D,
+                                               0x49, 0x54, 0x20, 0x6D, 0x65,
+                                               0x73, 0x73, 0x61, 0x67, 0x65,
+                                               0x2C, 0x20, 0x6F, 0x72, 0x20,
+                                               0x61, 0x6E, 0x20, 0x53, 0x4D,
+                                               0x53, 0x2D, 0x43, 0x4F, 0x4D,
+                                               0x4D, 0x41, 0x4E, 0x44, 0x20,
+                                               0x6D, 0x65, 0x73, 0x73, 0x61,
+                                               0x67, 0x65, 0x2C, 0x20, 0x77,
+                                               0x68, 0x65, 0x72, 0x65, 0x20,
+                                               0x74, 0x68, 0x65, 0x20, 0x75,
+                                               0x73, 0x65, 0x72, 0x20, 0x64,
+                                               0x61, 0x74, 0x61, 0x20, 0x63,
+                                               0x61, 0x6E, 0x20, 0x62, 0x65,
+                                               0x20, 0x70, 0x61, 0x73, 0x73,
+                                               0x65, 0x64, 0x20, 0x74, 0x72,
+                                               0x61, 0x6E, 0x73, 0x70, 0x61,
+                                               0x72, 0x65, 0x6E, 0x74, 0x6C,
+                                               0x79, 0x3B, 0x20, 0x2D, 0x20,
+                                               0x41, 0x20, 0x73, 0x68, 0x6F,
+                                               0x72, 0x74, 0x20, 0x6D, 0x65,
+                                               0x73, 0x73, 0x61, 0x67, 0x65,
+                                               0x20, 0x74, 0x6F, 0x20, 0x62,
+                                               0x65, 0x20, 0x73, 0x65, 0x6E,
+                                               0x74, 0x20, 0x74, 0x6F, 0x20,
+                                               0x74, 0x68, 0x65, 0x20, 0x6E,
+                                               0x65, 0x74, 0x77, 0x6F, 0x72,
+                                               0x6B, 0x20, 0x69, 0x6E, 0x20,
+                                               0x61, 0x6E, 0x20, 0x53, 0x4D,
+                                               0x53, 0x2D, 0x53, 0x55, 0x42,
+                                               0x4D, 0x49, 0x54, 0x20, 0x8B,
+                                               0x09, 0x01, 0x00, 0x02, 0x91,
+                                               0x10, 0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_171[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x00, 0x86, 0x09, 0x91,
+                                               0x11, 0x22, 0x33, 0x44, 0x55,
+                                               0x66, 0x77, 0xF8, 0x8B, 0x18,
+                                               0x01, 0x00, 0x09, 0x91, 0x10,
+                                               0x32, 0x54, 0x76, 0xF8, 0x40,
+                                               0xF4, 0x0C, 0x54, 0x65, 0x73,
+                                               0x74, 0x20, 0x4D, 0x65, 0x73,
+                                               0x73, 0x61, 0x67, 0x65 };
+
+static unsigned char send_sms_181[] = { 0xD0, 0x2E, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x86, 0x09, 0x91, 0x11, 0x22,
+                                               0x33, 0x44, 0x55, 0x66, 0x77,
+                                               0xF8, 0x8B, 0x18, 0x01, 0x00,
+                                               0x09, 0x91, 0x10, 0x32, 0x54,
+                                               0x76, 0xF8, 0x40, 0xF4, 0x0C,
+                                               0x54, 0x65, 0x73, 0x74, 0x20,
+                                               0x4D, 0x65, 0x73, 0x73, 0x61,
+                                               0x67, 0x65 };
+
+static unsigned char send_sms_211[] = { 0xD0, 0x55, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x86, 0x09, 0x91,
+                                               0x11, 0x22, 0x33, 0x44, 0x55,
+                                               0x66, 0x77, 0xF8, 0x8B, 0x24,
+                                               0x01, 0x00, 0x09, 0x91, 0x10,
+                                               0x32, 0x54, 0x76, 0xF8, 0x40,
+                                               0x08, 0x18, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15 };
+
+static unsigned char send_sms_212[] = { 0xD0, 0x4B, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0F, 0x81, 0x0C, 0x08,
+                                               0x97, 0x94, 0xA0, 0x90, 0x92,
+                                               0xA1, 0xA2, 0x92, 0xA3, 0x99,
+                                               0xA2, 0x95, 0x86, 0x09, 0x91,
+                                               0x11, 0x22, 0x33, 0x44, 0x55,
+                                               0x66, 0x77, 0xF8, 0x8B, 0x24,
+                                               0x01, 0x00, 0x09, 0x91, 0x10,
+                                               0x32, 0x54, 0x76, 0xF8, 0x40,
+                                               0x08, 0x18, 0x04, 0x17, 0x04,
+                                               0x14, 0x04, 0x20, 0x04, 0x10,
+                                               0x04, 0x12, 0x04, 0x21, 0x04,
+                                               0x22, 0x04, 0x12, 0x04, 0x23,
+                                               0x04, 0x19, 0x04, 0x22, 0x04,
+                                               0x15 };
+
+static unsigned char send_sms_213[] = { 0xD0, 0x4C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x82, 0x0C, 0x04,
+                                               0x10, 0x87, 0x84, 0x90, 0x80,
+                                               0x82, 0x91, 0x92, 0x82, 0x93,
+                                               0x89, 0x92, 0x85, 0x86, 0x09,
+                                               0x91, 0x11, 0x22, 0x33, 0x44,
+                                               0x55, 0x66, 0x77, 0xF8, 0x8B,
+                                               0x24, 0x01, 0x00, 0x09, 0x91,
+                                               0x10, 0x32, 0x54, 0x76, 0xF8,
+                                               0x40, 0x08, 0x18, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15 };
+
+static unsigned char send_sms_311[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x07, 0x4E, 0x4F, 0x20,
+                                               0x49, 0x43, 0x4F, 0x4E, 0x86,
+                                               0x09, 0x91, 0x11, 0x22, 0x33,
+                                               0x44, 0x55, 0x66, 0x77, 0xF8,
+                                               0x8B, 0x18, 0x01, 0x00, 0x09,
+                                               0x91, 0x10, 0x32, 0x54, 0x76,
+                                               0xF8, 0x40, 0xF4, 0x0C, 0x54,
+                                               0x65, 0x73, 0x74, 0x20, 0x4D,
+                                               0x65, 0x73, 0x73, 0x61, 0x67,
+                                               0x65, 0x9E, 0x02, 0x00, 0x01 };
+
+static unsigned char send_sms_321[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x07, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x53, 0x4D, 0x86,
+                                               0x09, 0x91, 0x11, 0x22, 0x33,
+                                               0x44, 0x55, 0x66, 0x77, 0xF8,
+                                               0x8B, 0x18, 0x01, 0x00, 0x09,
+                                               0x91, 0x10, 0x32, 0x54, 0x76,
+                                               0xF8, 0x40, 0xF4, 0x0C, 0x54,
+                                               0x65, 0x73, 0x74, 0x20, 0x4D,
+                                               0x65, 0x73, 0x73, 0x61, 0x67,
+                                               0x65, 0x1E, 0x02, 0x01, 0x01 };
+
+static unsigned char send_sms_411[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_412[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_421[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x01, 0xB4 };
+
+static unsigned char send_sms_422[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_431[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x02, 0xB4 };
+
+static unsigned char send_sms_432[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_441[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x04, 0xB4 };
+
+static unsigned char send_sms_442[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_443[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_451[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x08, 0xB4 };
+
+static unsigned char send_sms_452[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_453[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_461[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x10, 0xB4 };
+
+static unsigned char send_sms_462[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_463[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_471[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x20, 0xB4 };
+
+static unsigned char send_sms_472[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_473[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_481[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x40, 0xB4 };
+
+static unsigned char send_sms_482[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_483[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_491[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x80, 0xB4 };
+
+static unsigned char send_sms_492[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_493[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_4101[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20, 0xD0,
+                                               0x04, 0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char send_sms_4102[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8B, 0x09,
+                                               0x01, 0x00, 0x02, 0x91, 0x10,
+                                               0x40, 0xF0, 0x01, 0x20 };
+
+static unsigned char send_sms_511[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x80, 0x4E, 0x2D,
+                                               0x4E, 0x00, 0x86, 0x09, 0x91,
+                                               0x11, 0x22, 0x33, 0x44, 0x55,
+                                               0x66, 0x77, 0xF8, 0x8B, 0x10,
+                                               0x01, 0x00, 0x09, 0x91, 0x10,
+                                               0x32, 0x54, 0x76, 0xF8, 0x40,
+                                               0x08, 0x04, 0x4E, 0x2D, 0x4E,
+                                               0x00 };
+
+static unsigned char send_sms_512[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x81, 0x02, 0x9C,
+                                               0xAD, 0x80, 0x86, 0x09, 0x91,
+                                               0x11, 0x22, 0x33, 0x44, 0x55,
+                                               0x66, 0x77, 0xF8, 0x8B, 0x10,
+                                               0x01, 0x00, 0x09, 0x91, 0x10,
+                                               0x32, 0x54, 0x76, 0xF8, 0x40,
+                                               0x08, 0x04, 0x4E, 0x2D, 0x4E,
+                                               0x00 };
+
+static unsigned char send_sms_513[] = { 0xD0, 0x2E, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x06, 0x82, 0x02, 0x4E,
+                                               0x00, 0xAD, 0x80, 0x86, 0x09,
+                                               0x91, 0x11, 0x22, 0x33, 0x44,
+                                               0x55, 0x66, 0x77, 0xF8, 0x8B,
+                                               0x10, 0x01, 0x00, 0x09, 0x91,
+                                               0x10, 0x32, 0x54, 0x76, 0xF8,
+                                               0x40, 0x08, 0x04, 0x4E, 0x2D,
+                                               0x4E, 0x00 };
+
+static unsigned char send_sms_611[] = { 0xD0, 0x35, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x09, 0x80, 0x00, 0x38,
+                                               0x00, 0x30, 0x30, 0xEB, 0x00,
+                                               0x30, 0x86, 0x09, 0x91, 0x11,
+                                               0x22, 0x33, 0x44, 0x55, 0x66,
+                                               0x77, 0xF8, 0x8B, 0x14, 0x01,
+                                               0x00, 0x09, 0x91, 0x10, 0x32,
+                                               0x54, 0x76, 0xF8, 0x40, 0x08,
+                                               0x08, 0x00, 0x38, 0x00, 0x30,
+                                               0x30, 0xEB, 0x00, 0x31 };
+
+static unsigned char send_sms_612[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x07, 0x81, 0x04, 0x61,
+                                               0x38, 0x31, 0xEB, 0x31, 0x86,
+                                               0x09, 0x91, 0x11, 0x22, 0x33,
+                                               0x44, 0x55, 0x66, 0x77, 0xF8,
+                                               0x8B, 0x14, 0x01, 0x00, 0x09,
+                                               0x91, 0x10, 0x32, 0x54, 0x76,
+                                               0xF8, 0x40, 0x08, 0x08, 0x00,
+                                               0x38, 0x00, 0x30, 0x30, 0xEB,
+                                               0x00, 0x32 };
+
+static unsigned char send_sms_613[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x13,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x08, 0x82, 0x04, 0x30,
+                                               0xA0, 0x38, 0x32, 0xCB, 0x32,
+                                               0x86, 0x09, 0x91, 0x11, 0x22,
+                                               0x33, 0x44, 0x55, 0x66, 0x77,
+                                               0xF8, 0x8B, 0x14, 0x01, 0x00,
+                                               0x09, 0x91, 0x10, 0x32, 0x54,
+                                               0x76, 0xF8, 0x40, 0x08, 0x08,
+                                               0x00, 0x38, 0x00, 0x30, 0x30,
+                                               0xEB, 0x00, 0x33 };
+
+static struct send_sms_test send_sms_data_111 = {
+       .pdu = send_sms_111,
+       .pdu_len = sizeof(send_sms_111),
+       .qualifier = 0x00,
+       .alpha_id = "Send SM",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF4,
+                       .udl = 12,
+                       .ud = "Test Message"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_121 = {
+       .pdu = send_sms_121,
+       .pdu_len = sizeof(send_sms_121),
+       .qualifier = 0x01,
+       .alpha_id = "Send SM",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 7,
+                       .ud = "Send SM"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_131 = {
+       .pdu = send_sms_131,
+       .pdu_len = sizeof(send_sms_131),
+       .qualifier = 0x00,
+       .alpha_id = "Short Message",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 13,
+                       .ud = "Short Message"
+               } }
+       },
+};
+
+static struct send_sms_test send_sms_data_141 = {
+       .pdu = send_sms_141,
+       .pdu_len = sizeof(send_sms_141),
+       .qualifier = 0x01,
+       .alpha_id = "The address data object holds the RP_Destination_Address",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 160,
+                       .ud = "Two types are defined: - A short message to be "
+                               "sent to the network in an SMS-SUBMIT message, "
+                               "or an SMS-COMMAND message, where the user "
+                               "data can be passed transp"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_151 = {
+       .pdu = send_sms_151,
+       .pdu_len = sizeof(send_sms_151),
+       .qualifier = 0x00,
+       .alpha_id = "The address data object holds the RP Destination Address",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 160,
+                       .ud = "Two types are defined: - A short message to be "
+                               "sent to the network in an SMS-SUBMIT message, "
+                               "or an SMS-COMMAND message, where the user "
+                               "data can be passed transp"
+               } }
+       }
+};
+
+/* There should be a space after alpha_id */
+static struct send_sms_test send_sms_data_161 = {
+       .pdu = send_sms_161,
+       .pdu_len = sizeof(send_sms_161),
+       .qualifier = 0x00,
+       .alpha_id = "Two types are defined: - A short message to be sent to "
+                       "the network in an SMS-SUBMIT message, or an "
+                       "SMS-COMMAND message, where the user data can be "
+                       "passed transparently; - A short message to be sent "
+                       "to the network in an SMS-SUBMIT ",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_171 = {
+       .pdu = send_sms_171,
+       .pdu_len = sizeof(send_sms_171),
+       .qualifier = 0x00,
+       .alpha_id = "",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF4,
+                       .udl = 12,
+                       .ud = "Test Message"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_181 = {
+       .pdu = send_sms_181,
+       .pdu_len = sizeof(send_sms_181),
+       .qualifier = 0x00,
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF4,
+                       .udl = 12,
+                       .ud = "Test Message"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_211 = {
+       .pdu = send_sms_211,
+       .pdu_len = sizeof(send_sms_211),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 24,
+                       .ud = "ЗДРАВСТВУЙТЕ"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_212 = {
+       .pdu = send_sms_212,
+       .pdu_len = sizeof(send_sms_212),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 24,
+                       .ud = "ЗДРАВСТВУЙТЕ"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_213 = {
+       .pdu = send_sms_213,
+       .pdu_len = sizeof(send_sms_213),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 24,
+                       .ud = "ЗДРАВСТВУЙТЕ"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_311 = {
+       .pdu = send_sms_311,
+       .pdu_len = sizeof(send_sms_311),
+       .qualifier = 0x00,
+       .alpha_id = "NO ICON",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF4,
+                       .udl = 12,
+                       .ud = "Test Message"
+               } }
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_sms_test send_sms_data_321 = {
+       .pdu = send_sms_321,
+       .pdu_len = sizeof(send_sms_321),
+       .qualifier = 0x00,
+       .alpha_id = "Send SM",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF4,
+                       .udl = 12,
+                       .ud = "Test Message"
+               } }
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_sms_test send_sms_data_411 = {
+       .pdu = send_sms_411,
+       .pdu_len = sizeof(send_sms_411),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_412 = {
+       .pdu = send_sms_412,
+       .pdu_len = sizeof(send_sms_412),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_421 = {
+       .pdu = send_sms_421,
+       .pdu_len = sizeof(send_sms_421),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_422 = {
+       .pdu = send_sms_422,
+       .pdu_len = sizeof(send_sms_422),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_431 = {
+       .pdu = send_sms_431,
+       .pdu_len = sizeof(send_sms_431),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_432 = {
+       .pdu = send_sms_432,
+       .pdu_len = sizeof(send_sms_432),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_441 = {
+       .pdu = send_sms_441,
+       .pdu_len = sizeof(send_sms_441),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_442 = {
+       .pdu = send_sms_442,
+       .pdu_len = sizeof(send_sms_442),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_443 = {
+       .pdu = send_sms_443,
+       .pdu_len = sizeof(send_sms_443),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_451 = {
+       .pdu = send_sms_451,
+       .pdu_len = sizeof(send_sms_451),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_452 = {
+       .pdu = send_sms_452,
+       .pdu_len = sizeof(send_sms_452),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_453 = {
+       .pdu = send_sms_453,
+       .pdu_len = sizeof(send_sms_453),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_461 = {
+       .pdu = send_sms_461,
+       .pdu_len = sizeof(send_sms_461),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x10, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_462 = {
+       .pdu = send_sms_462,
+       .pdu_len = sizeof(send_sms_462),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_463 = {
+       .pdu = send_sms_463,
+       .pdu_len = sizeof(send_sms_463),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_471 = {
+       .pdu = send_sms_471,
+       .pdu_len = sizeof(send_sms_471),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x20, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_472 = {
+       .pdu = send_sms_472,
+       .pdu_len = sizeof(send_sms_472),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_473 = {
+       .pdu = send_sms_473,
+       .pdu_len = sizeof(send_sms_473),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_481 = {
+       .pdu = send_sms_481,
+       .pdu_len = sizeof(send_sms_481),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_482 = {
+       .pdu = send_sms_482,
+       .pdu_len = sizeof(send_sms_482),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_483 = {
+       .pdu = send_sms_483,
+       .pdu_len = sizeof(send_sms_483),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_491 = {
+       .pdu = send_sms_491,
+       .pdu_len = sizeof(send_sms_491),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_492 = {
+       .pdu = send_sms_492,
+       .pdu_len = sizeof(send_sms_492),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_493 = {
+       .pdu = send_sms_493,
+       .pdu_len = sizeof(send_sms_493),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_4101 = {
+       .pdu = send_sms_4101,
+       .pdu_len = sizeof(send_sms_4101),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_sms_test send_sms_data_4102 = {
+       .pdu = send_sms_4102,
+       .pdu_len = sizeof(send_sms_4102),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .gsm_sms = {
+               {}, SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "01",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0xF0,
+                       .udl = 1,
+                       .ud = " "
+               } }
+       }
+};
+
+/* The TP-UDL should be 4, instead of 24 */
+static struct send_sms_test send_sms_data_511 = {
+       .pdu = send_sms_511,
+       .pdu_len = sizeof(send_sms_511),
+       .qualifier = 0x00,
+       .alpha_id = "中一",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 4,
+                       .ud = "中一"
+               } }
+       }
+};
+
+/* The TP-UDL should be 4, instead of 24 */
+static struct send_sms_test send_sms_data_512 = {
+       .pdu = send_sms_512,
+       .pdu_len = sizeof(send_sms_512),
+       .qualifier = 0x00,
+       .alpha_id = "中一",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 4,
+                       .ud = "中一"
+               } }
+       }
+};
+
+/* The TP-UDL should be 4, instead of 24 */
+static struct send_sms_test send_sms_data_513 = {
+       .pdu = send_sms_513,
+       .pdu_len = sizeof(send_sms_513),
+       .qualifier = 0x00,
+       .alpha_id = "中一",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 4,
+                       .ud = "中一"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_611 = {
+       .pdu = send_sms_611,
+       .pdu_len = sizeof(send_sms_611),
+       .qualifier = 0x00,
+       .alpha_id = "80ル0",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 8,
+                       .ud = "80ル1"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_612 = {
+       .pdu = send_sms_612,
+       .pdu_len = sizeof(send_sms_612),
+       .qualifier = 0x00,
+       .alpha_id = "81ル1",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 8,
+                       .ud = "80ル2"
+               } }
+       }
+};
+
+static struct send_sms_test send_sms_data_613 = {
+       .pdu = send_sms_613,
+       .pdu_len = sizeof(send_sms_613),
+       .qualifier = 0x00,
+       .alpha_id = "82ル2",
+       .gsm_sms = {
+               {
+                       .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                       .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                       .address = "112233445566778",
+               },
+               SMS_TYPE_SUBMIT,
+               {.submit = {
+                       .mr = 0x00,
+                       .daddr = {
+                               .number_type = SMS_NUMBER_TYPE_INTERNATIONAL,
+                               .numbering_plan = SMS_NUMBERING_PLAN_ISDN,
+                               .address = "012345678",
+                       },
+                       .pid = 0x40,
+                       .dcs = 0x08,
+                       .udl = 8,
+                       .ud = "80ル3"
+               } }
+       }
+};
+
+static void test_send_sms(gconstpointer data)
+{
+       const struct send_sms_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SEND_SMS);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK);
+
+       check_alpha_id(command->send_sms.alpha_id, test->alpha_id);
+       check_gsm_sms(&command->send_sms.gsm_sms, &test->gsm_sms);
+       check_cdma_sms_tpdu(&command->send_sms.cdma_sms, &test->cdma_sms);
+       check_icon_id(&command->send_sms.icon_id, &test->icon_id);
+       check_text_attr(&command->send_sms.text_attr, &test->text_attr);
+       check_frame_id(&command->send_sms.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct send_ss_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       struct stk_ss ss;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char send_ss_111[] = { 0xD0, 0x29, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0C, 0x43, 0x61, 0x6C,
+                                               0x6C, 0x20, 0x46, 0x6F, 0x72,
+                                               0x77, 0x61, 0x72, 0x64, 0x89,
+                                               0x10, 0x91, 0xAA, 0x12, 0x0A,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0x21, 0x43, 0x65, 0x87, 0xA9,
+                                               0x01, 0xFB };
+
+static unsigned char send_ss_141[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0C, 0x43, 0x61, 0x6C,
+                                               0x6C, 0x20, 0x46, 0x6F, 0x72,
+                                               0x77, 0x61, 0x72, 0x64, 0x89,
+                                               0x14, 0x91, 0xAA, 0x12, 0x0A,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0x21, 0x43, 0x65, 0xA7, 0x11,
+                                               0xFB };
+
+static unsigned char send_ss_151[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01,
+                                               0x11, 0x00, 0x82, 0x02, 0x81,
+                                               0x83, 0x85, 0x81, 0xEB, 0x45,
+                                               0x76, 0x65, 0x6E, 0x20, 0x69,
+                                               0x66, 0x20, 0x74, 0x68, 0x65,
+                                               0x20, 0x46, 0x69, 0x78, 0x65,
+                                               0x64, 0x20, 0x44, 0x69, 0x61,
+                                               0x6C, 0x6C, 0x69, 0x6E, 0x67,
+                                               0x20, 0x4E, 0x75, 0x6D, 0x62,
+                                               0x65, 0x72, 0x20, 0x73, 0x65,
+                                               0x72, 0x76, 0x69, 0x63, 0x65,
+                                               0x20, 0x69, 0x73, 0x20, 0x65,
+                                               0x6E, 0x61, 0x62, 0x6C, 0x65,
+                                               0x64, 0x2C, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x73, 0x75, 0x70,
+                                               0x70, 0x6C, 0x65, 0x6D, 0x65,
+                                               0x6E, 0x74, 0x61, 0x72, 0x79,
+                                               0x20, 0x73, 0x65, 0x72, 0x76,
+                                               0x69, 0x63, 0x65, 0x20, 0x63,
+                                               0x6F, 0x6E, 0x74, 0x72, 0x6F,
+                                               0x6C, 0x20, 0x73, 0x74, 0x72,
+                                               0x69, 0x6E, 0x67, 0x20, 0x69,
+                                               0x6E, 0x63, 0x6C, 0x75, 0x64,
+                                               0x65, 0x64, 0x20, 0x69, 0x6E,
+                                               0x20, 0x74, 0x68, 0x65, 0x20,
+                                               0x53, 0x45, 0x4E, 0x44, 0x20,
+                                               0x53, 0x53, 0x20, 0x70, 0x72,
+                                               0x6F, 0x61, 0x63, 0x74, 0x69,
+                                               0x76, 0x65, 0x20, 0x63, 0x6F,
+                                               0x6D, 0x6D, 0x61, 0x6E, 0x64,
+                                               0x20, 0x73, 0x68, 0x61, 0x6C,
+                                               0x6C, 0x20, 0x6E, 0x6F, 0x74,
+                                               0x20, 0x62, 0x65, 0x20, 0x63,
+                                               0x68, 0x65, 0x63, 0x6B, 0x65,
+                                               0x64, 0x20, 0x61, 0x67, 0x61,
+                                               0x69, 0x6E, 0x73, 0x74, 0x20,
+                                               0x74, 0x68, 0x6F, 0x73, 0x65,
+                                               0x20, 0x6F, 0x66, 0x20, 0x74,
+                                               0x68, 0x65, 0x20, 0x46, 0x44,
+                                               0x4E, 0x20, 0x6C, 0x69, 0x73,
+                                               0x74, 0x2E, 0x20, 0x55, 0x70,
+                                               0x6F, 0x6E, 0x20, 0x72, 0x65,
+                                               0x63, 0x65, 0x69, 0x76, 0x69,
+                                               0x6E, 0x67, 0x20, 0x74, 0x68,
+                                               0x69, 0x73, 0x20, 0x63, 0x6F,
+                                               0x6D, 0x6D, 0x61, 0x6E, 0x64,
+                                               0x2C, 0x20, 0x74, 0x68, 0x65,
+                                               0x20, 0x4D, 0x45, 0x20, 0x73,
+                                               0x68, 0x61, 0x6C, 0x6C, 0x20,
+                                               0x64, 0x65, 0x63, 0x69, 0x89,
+                                               0x04, 0xFF, 0xBA, 0x13, 0xFB };
+
+static unsigned char send_ss_161[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x00, 0x89, 0x10, 0x91,
+                                               0xAA, 0x12, 0x0A, 0x21, 0x43,
+                                               0x65, 0x87, 0x09, 0x21, 0x43,
+                                               0x65, 0x87, 0xA9, 0x01, 0xFB };
+
+static unsigned char send_ss_211[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x42, 0x61, 0x73,
+                                               0x69, 0x63, 0x20, 0x49, 0x63,
+                                               0x6F, 0x6E, 0x89, 0x10, 0x91,
+                                               0xAA, 0x12, 0x0A, 0x21, 0x43,
+                                               0x65, 0x87, 0x09, 0x21, 0x43,
+                                               0x65, 0x87, 0xA9, 0x01, 0xFB,
+                                               0x9E, 0x02, 0x00, 0x01 };
+
+static unsigned char send_ss_221[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x43, 0x6F, 0x6C,
+                                               0x6F, 0x75, 0x72, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0x9E, 0x02, 0x00, 0x02 };
+
+static unsigned char send_ss_231[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x42, 0x61, 0x73,
+                                               0x69, 0x63, 0x20, 0x49, 0x63,
+                                               0x6F, 0x6E, 0x89, 0x10, 0x91,
+                                               0xAA, 0x12, 0x0A, 0x21, 0x43,
+                                               0x65, 0x87, 0x09, 0x21, 0x43,
+                                               0x65, 0x87, 0xA9, 0x01, 0xFB,
+                                               0x9E, 0x02, 0x01, 0x01 };
+
+static unsigned char send_ss_241[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x89, 0x0E, 0x91, 0xAA, 0x12,
+                                               0x0A, 0x21, 0x43, 0x65, 0x87,
+                                               0x09, 0x21, 0x43, 0x65, 0x87,
+                                               0xB9, 0x9E, 0x02, 0x01, 0x01 };
+
+static unsigned char send_ss_311[] = { 0xD0, 0x36, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x89, 0x10, 0x91,
+                                               0xAA, 0x12, 0x0A, 0x21, 0x43,
+                                               0x65, 0x87, 0x09, 0x21, 0x43,
+                                               0x65, 0x87, 0xA9, 0x01, 0xFB };
+
+static unsigned char send_ss_411[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_412[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_421[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x01, 0xB4 };
+
+static unsigned char send_ss_422[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_431[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x02, 0xB4 };
+
+static unsigned char send_ss_432[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_441[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x04, 0xB4 };
+
+static unsigned char send_ss_442[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_443[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_451[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x08, 0xB4 };
+
+static unsigned char send_ss_452[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_453[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_461[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x10, 0xB4 };
+
+static unsigned char send_ss_462[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_463[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_471[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x20, 0xB4 };
+
+static unsigned char send_ss_472[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_473[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_481[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x40, 0xB4 };
+
+static unsigned char send_ss_482[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_483[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_491[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x80, 0xB4 };
+
+static unsigned char send_ss_492[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_493[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_4101[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char send_ss_4102[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x89, 0x10,
+                                               0x91, 0xAA, 0x12, 0x0A, 0x21,
+                                               0x43, 0x65, 0x87, 0x09, 0x21,
+                                               0x43, 0x65, 0x87, 0xA9, 0x01,
+                                               0xFB };
+
+static unsigned char send_ss_511[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x80, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x89, 0x10, 0x91,
+                                               0xAA, 0x12, 0x0A, 0x21, 0x43,
+                                               0x65, 0x87, 0x09, 0x21, 0x43,
+                                               0x65, 0x87, 0xA9, 0x01, 0xFB };
+
+static unsigned char send_ss_611[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x11,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x03, 0x80, 0x30, 0xEB,
+                                               0x89, 0x10, 0x91, 0xAA, 0x12,
+                                               0x0A, 0x21, 0x43, 0x65, 0x87,
+                                               0x09, 0x21, 0x43, 0x65, 0x87,
+                                               0xA9, 0x01, 0xFB };
+
+static struct send_ss_test send_ss_data_111 = {
+       .pdu = send_ss_111,
+       .pdu_len = sizeof(send_ss_111),
+       .qualifier = 0x00,
+       .alpha_id = "Call Forward",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_141 = {
+       .pdu = send_ss_141,
+       .pdu_len = sizeof(send_ss_141),
+       .qualifier = 0x00,
+       .alpha_id = "Call Forward",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*0123456789012345678901234567*11#"
+       }
+};
+
+static struct send_ss_test send_ss_data_151 = {
+       .pdu = send_ss_151,
+       .pdu_len = sizeof(send_ss_151),
+       .qualifier = 0x00,
+       .alpha_id = "Even if the Fixed Dialling Number service is enabled, the "
+               "supplementary service control string included in the SEND SS "
+               "proactive command shall not be checked against those of the "
+               "FDN list. Upon receiving this command, the ME shall deci",
+       .ss = {
+               .ton_npi = 0xFF,
+               .ss = "*#31#"
+       }
+};
+
+static struct send_ss_test send_ss_data_161 = {
+       .pdu = send_ss_161,
+       .pdu_len = sizeof(send_ss_161),
+       .qualifier = 0x00,
+       .alpha_id = "",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_211 = {
+       .pdu = send_ss_211,
+       .pdu_len = sizeof(send_ss_211),
+       .qualifier = 0x00,
+       .alpha_id = "Basic Icon",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_ss_test send_ss_data_221 = {
+       .pdu = send_ss_221,
+       .pdu_len = sizeof(send_ss_221),
+       .qualifier = 0x00,
+       .alpha_id = "Colour Icon",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct send_ss_test send_ss_data_231 = {
+       .pdu = send_ss_231,
+       .pdu_len = sizeof(send_ss_231),
+       .qualifier = 0x00,
+       .alpha_id = "Basic Icon",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_ss_test send_ss_data_241 = {
+       .pdu = send_ss_241,
+       .pdu_len = sizeof(send_ss_241),
+       .qualifier = 0x00,
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789#"
+       },
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_ss_test send_ss_data_311 = {
+       .pdu = send_ss_311,
+       .pdu_len = sizeof(send_ss_311),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_411 = {
+       .pdu = send_ss_411,
+       .pdu_len = sizeof(send_ss_411),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_412 = {
+       .pdu = send_ss_412,
+       .pdu_len = sizeof(send_ss_412),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_421 = {
+       .pdu = send_ss_421,
+       .pdu_len = sizeof(send_ss_421),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_422 = {
+       .pdu = send_ss_422,
+       .pdu_len = sizeof(send_ss_422),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_431 = {
+       .pdu = send_ss_431,
+       .pdu_len = sizeof(send_ss_431),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_432 = {
+       .pdu = send_ss_432,
+       .pdu_len = sizeof(send_ss_432),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_441 = {
+       .pdu = send_ss_441,
+       .pdu_len = sizeof(send_ss_441),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_442 = {
+       .pdu = send_ss_442,
+       .pdu_len = sizeof(send_ss_442),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_443 = {
+       .pdu = send_ss_443,
+       .pdu_len = sizeof(send_ss_443),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_451 = {
+       .pdu = send_ss_451,
+       .pdu_len = sizeof(send_ss_451),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_452 = {
+       .pdu = send_ss_452,
+       .pdu_len = sizeof(send_ss_452),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_453 = {
+       .pdu = send_ss_453,
+       .pdu_len = sizeof(send_ss_453),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_461 = {
+       .pdu = send_ss_461,
+       .pdu_len = sizeof(send_ss_461),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x10, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_462 = {
+       .pdu = send_ss_462,
+       .pdu_len = sizeof(send_ss_462),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_463 = {
+       .pdu = send_ss_463,
+       .pdu_len = sizeof(send_ss_463),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_471 = {
+       .pdu = send_ss_471,
+       .pdu_len = sizeof(send_ss_471),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x20, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_472 = {
+       .pdu = send_ss_472,
+       .pdu_len = sizeof(send_ss_472),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_473 = {
+       .pdu = send_ss_473,
+       .pdu_len = sizeof(send_ss_473),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_481 = {
+       .pdu = send_ss_481,
+       .pdu_len = sizeof(send_ss_481),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_482 = {
+       .pdu = send_ss_482,
+       .pdu_len = sizeof(send_ss_482),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_483 = {
+       .pdu = send_ss_483,
+       .pdu_len = sizeof(send_ss_483),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_491 = {
+       .pdu = send_ss_491,
+       .pdu_len = sizeof(send_ss_491),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_492 = {
+       .pdu = send_ss_492,
+       .pdu_len = sizeof(send_ss_492),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_493 = {
+       .pdu = send_ss_493,
+       .pdu_len = sizeof(send_ss_493),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_4101 = {
+       .pdu = send_ss_4101,
+       .pdu_len = sizeof(send_ss_4101),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ss_test send_ss_data_4102 = {
+       .pdu = send_ss_4102,
+       .pdu_len = sizeof(send_ss_4102),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_511 = {
+       .pdu = send_ss_511,
+       .pdu_len = sizeof(send_ss_511),
+       .qualifier = 0x00,
+       .alpha_id = "你好",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static struct send_ss_test send_ss_data_611 = {
+       .pdu = send_ss_611,
+       .pdu_len = sizeof(send_ss_611),
+       .qualifier = 0x00,
+       .alpha_id = "ル",
+       .ss = {
+               .ton_npi = 0x91,
+               .ss = "**21*01234567890123456789*10#"
+       }
+};
+
+static void test_send_ss(gconstpointer data)
+{
+       const struct send_ss_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SEND_SS);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK);
+
+       check_alpha_id(command->send_ss.alpha_id, test->alpha_id);
+       check_ss(&command->send_ss.ss, &test->ss);
+       check_icon_id(&command->send_ss.icon_id, &test->icon_id);
+       check_text_attr(&command->send_ss.text_attr, &test->text_attr);
+       check_frame_id(&command->send_ss.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct send_ussd_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       char *ussd;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char send_ussd_111[] = { 0xD0, 0x50, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x37, 0x2D, 0x62,
+                                               0x69, 0x74, 0x20, 0x55, 0x53,
+                                               0x53, 0x44, 0x8A, 0x39, 0xF0,
+                                               0x41, 0xE1, 0x90, 0x58, 0x34,
+                                               0x1E, 0x91, 0x49, 0xE5, 0x92,
+                                               0xD9, 0x74, 0x3E, 0xA1, 0x51,
+                                               0xE9, 0x94, 0x5A, 0xB5, 0x5E,
+                                               0xB1, 0x59, 0x6D, 0x2B, 0x2C,
+                                               0x1E, 0x93, 0xCB, 0xE6, 0x33,
+                                               0x3A, 0xAD, 0x5E, 0xB3, 0xDB,
+                                               0xEE, 0x37, 0x3C, 0x2E, 0x9F,
+                                               0xD3, 0xEB, 0xF6, 0x3B, 0x3E,
+                                               0xAF, 0x6F, 0xC5, 0x64, 0x33,
+                                               0x5A, 0xCD, 0x76, 0xC3, 0xE5,
+                                               0x60 };
+
+static unsigned char send_ussd_121[] = { 0xD0, 0x58, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x38, 0x2D, 0x62,
+                                               0x69, 0x74, 0x20, 0x55, 0x53,
+                                               0x53, 0x44, 0x8A, 0x41, 0x44,
+                                               0x41, 0x42, 0x43, 0x44, 0x45,
+                                               0x46, 0x47, 0x48, 0x49, 0x4A,
+                                               0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+                                               0x50, 0x51, 0x52, 0x53, 0x54,
+                                               0x55, 0x56, 0x57, 0x58, 0x59,
+                                               0x5A, 0x2D, 0x61, 0x62, 0x63,
+                                               0x64, 0x65, 0x66, 0x67, 0x68,
+                                               0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+                                               0x6E, 0x6F, 0x70, 0x71, 0x72,
+                                               0x73, 0x74, 0x75, 0x76, 0x77,
+                                               0x78, 0x79, 0x7A, 0x2D, 0x31,
+                                               0x32, 0x33, 0x34, 0x35, 0x36,
+                                               0x37, 0x38, 0x39, 0x30 };
+
+static unsigned char send_ussd_131[] = { 0xD0, 0x2F, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x09, 0x55, 0x43, 0x53,
+                                               0x32, 0x20, 0x55, 0x53, 0x53,
+                                               0x44, 0x8A, 0x19, 0x48, 0x04,
+                                               0x17, 0x04, 0x14, 0x04, 0x20,
+                                               0x04, 0x10, 0x04, 0x12, 0x04,
+                                               0x21, 0x04, 0x22, 0x04, 0x12,
+                                               0x04, 0x23, 0x04, 0x19, 0x04,
+                                               0x22, 0x04, 0x15 };
+
+static unsigned char send_ussd_161[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01,
+                                               0x12, 0x00, 0x82, 0x02, 0x81,
+                                               0x83, 0x85, 0x81, 0xB6, 0x6F,
+                                               0x6E, 0x63, 0x65, 0x20, 0x61,
+                                               0x20, 0x52, 0x45, 0x4C, 0x45,
+                                               0x41, 0x53, 0x45, 0x20, 0x43,
+                                               0x4F, 0x4D, 0x50, 0x4C, 0x45,
+                                               0x54, 0x45, 0x20, 0x6D, 0x65,
+                                               0x73, 0x73, 0x61, 0x67, 0x65,
+                                               0x20, 0x63, 0x6F, 0x6E, 0x74,
+                                               0x61, 0x69, 0x6E, 0x69, 0x6E,
+                                               0x67, 0x20, 0x74, 0x68, 0x65,
+                                               0x20, 0x55, 0x53, 0x53, 0x44,
+                                               0x20, 0x52, 0x65, 0x74, 0x75,
+                                               0x72, 0x6E, 0x20, 0x52, 0x65,
+                                               0x73, 0x75, 0x6C, 0x74, 0x20,
+                                               0x6D, 0x65, 0x73, 0x73, 0x61,
+                                               0x67, 0x65, 0x20, 0x6E, 0x6F,
+                                               0x74, 0x20, 0x63, 0x6F, 0x6E,
+                                               0x74, 0x61, 0x69, 0x6E, 0x69,
+                                               0x6E, 0x67, 0x20, 0x61, 0x6E,
+                                               0x20, 0x65, 0x72, 0x72, 0x6F,
+                                               0x72, 0x20, 0x68, 0x61, 0x73,
+                                               0x20, 0x62, 0x65, 0x65, 0x6E,
+                                               0x20, 0x72, 0x65, 0x63, 0x65,
+                                               0x69, 0x76, 0x65, 0x64, 0x20,
+                                               0x66, 0x72, 0x6F, 0x6D, 0x20,
+                                               0x74, 0x68, 0x65, 0x20, 0x6E,
+                                               0x65, 0x74, 0x77, 0x6F, 0x72,
+                                               0x6B, 0x2C, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x4D, 0x45, 0x20,
+                                               0x73, 0x68, 0x61, 0x6C, 0x6C,
+                                               0x20, 0x69, 0x6E, 0x66, 0x6F,
+                                               0x72, 0x6D, 0x20, 0x74, 0x68,
+                                               0x65, 0x20, 0x53, 0x49, 0x4D,
+                                               0x20, 0x74, 0x68, 0x61, 0x74,
+                                               0x20, 0x74, 0x68, 0x65, 0x20,
+                                               0x63, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x68, 0x61,
+                                               0x73, 0x8A, 0x39, 0xF0, 0x41,
+                                               0xE1, 0x90, 0x58, 0x34, 0x1E,
+                                               0x91, 0x49, 0xE5, 0x92, 0xD9,
+                                               0x74, 0x3E, 0xA1, 0x51, 0xE9,
+                                               0x94, 0x5A, 0xB5, 0x5E, 0xB1,
+                                               0x59, 0x6D, 0x2B, 0x2C, 0x1E,
+                                               0x93, 0xCB, 0xE6, 0x33, 0x3A,
+                                               0xAD, 0x5E, 0xB3, 0xDB, 0xEE,
+                                               0x37, 0x3C, 0x2E, 0x9F, 0xD3,
+                                               0xEB, 0xF6, 0x3B, 0x3E, 0xAF,
+                                               0x6F, 0xC5, 0x64, 0x33, 0x5A,
+                                               0xCD, 0x76, 0xC3, 0xE5, 0x60 };
+
+static unsigned char send_ussd_171[] = { 0xD0, 0x44, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x8A, 0x39, 0xF0, 0x41, 0xE1,
+                                               0x90, 0x58, 0x34, 0x1E, 0x91,
+                                               0x49, 0xE5, 0x92, 0xD9, 0x74,
+                                               0x3E, 0xA1, 0x51, 0xE9, 0x94,
+                                               0x5A, 0xB5, 0x5E, 0xB1, 0x59,
+                                               0x6D, 0x2B, 0x2C, 0x1E, 0x93,
+                                               0xCB, 0xE6, 0x33, 0x3A, 0xAD,
+                                               0x5E, 0xB3, 0xDB, 0xEE, 0x37,
+                                               0x3C, 0x2E, 0x9F, 0xD3, 0xEB,
+                                               0xF6, 0x3B, 0x3E, 0xAF, 0x6F,
+                                               0xC5, 0x64, 0x33, 0x5A, 0xCD,
+                                               0x76, 0xC3, 0xE5, 0x60 };
+
+static unsigned char send_ussd_181[] = { 0xD0, 0x46, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x00, 0x8A, 0x39, 0xF0,
+                                               0x41, 0xE1, 0x90, 0x58, 0x34,
+                                               0x1E, 0x91, 0x49, 0xE5, 0x92,
+                                               0xD9, 0x74, 0x3E, 0xA1, 0x51,
+                                               0xE9, 0x94, 0x5A, 0xB5, 0x5E,
+                                               0xB1, 0x59, 0x6D, 0x2B, 0x2C,
+                                               0x1E, 0x93, 0xCB, 0xE6, 0x33,
+                                               0x3A, 0xAD, 0x5E, 0xB3, 0xDB,
+                                               0xEE, 0x37, 0x3C, 0x2E, 0x9F,
+                                               0xD3, 0xEB, 0xF6, 0x3B, 0x3E,
+                                               0xAF, 0x6F, 0xC5, 0x64, 0x33,
+                                               0x5A, 0xCD, 0x76, 0xC3, 0xE5,
+                                               0x60 };
+
+static unsigned char send_ussd_211[] = { 0xD0, 0x54, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x42, 0x61, 0x73,
+                                               0x69, 0x63, 0x20, 0x49, 0x63,
+                                               0x6F, 0x6E, 0x8A, 0x39, 0xF0,
+                                               0x41, 0xE1, 0x90, 0x58, 0x34,
+                                               0x1E, 0x91, 0x49, 0xE5, 0x92,
+                                               0xD9, 0x74, 0x3E, 0xA1, 0x51,
+                                               0xE9, 0x94, 0x5A, 0xB5, 0x5E,
+                                               0xB1, 0x59, 0x6D, 0x2B, 0x2C,
+                                               0x1E, 0x93, 0xCB, 0xE6, 0x33,
+                                               0x3A, 0xAD, 0x5E, 0xB3, 0xDB,
+                                               0xEE, 0x37, 0x3C, 0x2E, 0x9F,
+                                               0xD3, 0xEB, 0xF6, 0x3B, 0x3E,
+                                               0xAF, 0x6F, 0xC5, 0x64, 0x33,
+                                               0x5A, 0xCD, 0x76, 0xC3, 0xE5,
+                                               0x60, 0x9E, 0x02, 0x00, 0x01 };
+
+static unsigned char send_ussd_221[] = { 0xD0, 0x54, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x43, 0x6F, 0x6C,
+                                               0x6F, 0x72, 0x20, 0x49, 0x63,
+                                               0x6F, 0x6E, 0x8A, 0x39, 0xF0,
+                                               0x41, 0xE1, 0x90, 0x58, 0x34,
+                                               0x1E, 0x91, 0x49, 0xE5, 0x92,
+                                               0xD9, 0x74, 0x3E, 0xA1, 0x51,
+                                               0xE9, 0x94, 0x5A, 0xB5, 0x5E,
+                                               0xB1, 0x59, 0x6D, 0x2B, 0x2C,
+                                               0x1E, 0x93, 0xCB, 0xE6, 0x33,
+                                               0x3A, 0xAD, 0x5E, 0xB3, 0xDB,
+                                               0xEE, 0x37, 0x3C, 0x2E, 0x9F,
+                                               0xD3, 0xEB, 0xF6, 0x3B, 0x3E,
+                                               0xAF, 0x6F, 0xC5, 0x64, 0x33,
+                                               0x5A, 0xCD, 0x76, 0xC3, 0xE5,
+                                               0x60, 0x9E, 0x02, 0x00, 0x02 };
+
+static unsigned char send_ussd_231[] = { 0xD0, 0x54, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x42, 0x61, 0x73,
+                                               0x69, 0x63, 0x20, 0x49, 0x63,
+                                               0x6F, 0x6E, 0x8A, 0x39, 0xF0,
+                                               0x41, 0xE1, 0x90, 0x58, 0x34,
+                                               0x1E, 0x91, 0x49, 0xE5, 0x92,
+                                               0xD9, 0x74, 0x3E, 0xA1, 0x51,
+                                               0xE9, 0x94, 0x5A, 0xB5, 0x5E,
+                                               0xB1, 0x59, 0x6D, 0x2B, 0x2C,
+                                               0x1E, 0x93, 0xCB, 0xE6, 0x33,
+                                               0x3A, 0xAD, 0x5E, 0xB3, 0xDB,
+                                               0xEE, 0x37, 0x3C, 0x2E, 0x9F,
+                                               0xD3, 0xEB, 0xF6, 0x3B, 0x3E,
+                                               0xAF, 0x6F, 0xC5, 0x64, 0x33,
+                                               0x5A, 0xCD, 0x76, 0xC3, 0xE5,
+                                               0x60, 0x9E, 0x02, 0x01, 0x01 };
+
+static unsigned char send_ussd_241[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x8A, 0x39, 0xF0, 0x41, 0xE1,
+                                               0x90, 0x58, 0x34, 0x1E, 0x91,
+                                               0x49, 0xE5, 0x92, 0xD9, 0x74,
+                                               0x3E, 0xA1, 0x51, 0xE9, 0x94,
+                                               0x5A, 0xB5, 0x5E, 0xB1, 0x59,
+                                               0x6D, 0x2B, 0x2C, 0x1E, 0x93,
+                                               0xCB, 0xE6, 0x33, 0x3A, 0xAD,
+                                               0x5E, 0xB3, 0xDB, 0xEE, 0x37,
+                                               0x3C, 0x2E, 0x9F, 0xD3, 0xEB,
+                                               0xF6, 0x3B, 0x3E, 0xAF, 0x6F,
+                                               0xC5, 0x64, 0x33, 0x5A, 0xCD,
+                                               0x76, 0xC3, 0xE5, 0x60, 0x9E,
+                                               0x02, 0x01, 0x01 };
+
+static unsigned char send_ussd_311[] = { 0xD0, 0x5F, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x8A, 0x39, 0xF0,
+                                               0x41, 0xE1, 0x90, 0x58, 0x34,
+                                               0x1E, 0x91, 0x49, 0xE5, 0x92,
+                                               0xD9, 0x74, 0x3E, 0xA1, 0x51,
+                                               0xE9, 0x94, 0x5A, 0xB5, 0x5E,
+                                               0xB1, 0x59, 0x6D, 0x2B, 0x2C,
+                                               0x1E, 0x93, 0xCB, 0xE6, 0x33,
+                                               0x3A, 0xAD, 0x5E, 0xB3, 0xDB,
+                                               0xEE, 0x37, 0x3C, 0x2E, 0x9F,
+                                               0xD3, 0xEB, 0xF6, 0x3B, 0x3E,
+                                               0xAF, 0x6F, 0xC5, 0x64, 0x33,
+                                               0x5A, 0xCD, 0x76, 0xC3, 0xE5,
+                                               0x60 };
+
+static unsigned char send_ussd_411[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_412[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_421[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x01, 0xB4 };
+
+static unsigned char send_ussd_422[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_431[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x02, 0xB4 };
+
+static unsigned char send_ussd_432[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_441[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x04, 0xB4 };
+
+static unsigned char send_ussd_442[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_443[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_451[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x08, 0xB4 };
+
+static unsigned char send_ussd_452[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_453[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_461[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x10, 0xB4 };
+
+static unsigned char send_ussd_462[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_463[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_471[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x20, 0xB4 };
+
+static unsigned char send_ussd_472[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_473[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_481[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x40, 0xB4 };
+
+static unsigned char send_ussd_482[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_483[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_491[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x80, 0xB4 };
+
+static unsigned char send_ussd_492[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_493[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x33, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_4101[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x31, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60, 0xD0, 0x04, 0x00,
+                                               0x10, 0x00, 0xB4 };
+
+static unsigned char send_ussd_4102[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x10, 0x54, 0x65, 0x78,
+                                               0x74, 0x20, 0x41, 0x74, 0x74,
+                                               0x72, 0x69, 0x62, 0x75, 0x74,
+                                               0x65, 0x20, 0x32, 0x8A, 0x39,
+                                               0xF0, 0x41, 0xE1, 0x90, 0x58,
+                                               0x34, 0x1E, 0x91, 0x49, 0xE5,
+                                               0x92, 0xD9, 0x74, 0x3E, 0xA1,
+                                               0x51, 0xE9, 0x94, 0x5A, 0xB5,
+                                               0x5E, 0xB1, 0x59, 0x6D, 0x2B,
+                                               0x2C, 0x1E, 0x93, 0xCB, 0xE6,
+                                               0x33, 0x3A, 0xAD, 0x5E, 0xB3,
+                                               0xDB, 0xEE, 0x37, 0x3C, 0x2E,
+                                               0x9F, 0xD3, 0xEB, 0xF6, 0x3B,
+                                               0x3E, 0xAF, 0x6F, 0xC5, 0x64,
+                                               0x33, 0x5A, 0xCD, 0x76, 0xC3,
+                                               0xE5, 0x60 };
+
+static unsigned char send_ussd_511[] = { 0xD0, 0x4B, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x80, 0x4F, 0x60,
+                                               0x59, 0x7D, 0x8A, 0x39, 0xF0,
+                                               0x41, 0xE1, 0x90, 0x58, 0x34,
+                                               0x1E, 0x91, 0x49, 0xE5, 0x92,
+                                               0xD9, 0x74, 0x3E, 0xA1, 0x51,
+                                               0xE9, 0x94, 0x5A, 0xB5, 0x5E,
+                                               0xB1, 0x59, 0x6D, 0x2B, 0x2C,
+                                               0x1E, 0x93, 0xCB, 0xE6, 0x33,
+                                               0x3A, 0xAD, 0x5E, 0xB3, 0xDB,
+                                               0xEE, 0x37, 0x3C, 0x2E, 0x9F,
+                                               0xD3, 0xEB, 0xF6, 0x3B, 0x3E,
+                                               0xAF, 0x6F, 0xC5, 0x64, 0x33,
+                                               0x5A, 0xCD, 0x76, 0xC3, 0xE5,
+                                               0x60 };
+
+static unsigned char send_ussd_611[] = { 0xD0, 0x49, 0x81, 0x03, 0x01, 0x12,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x03, 0x80, 0x30, 0xEB,
+                                               0x8A, 0x39, 0xF0, 0x41, 0xE1,
+                                               0x90, 0x58, 0x34, 0x1E, 0x91,
+                                               0x49, 0xE5, 0x92, 0xD9, 0x74,
+                                               0x3E, 0xA1, 0x51, 0xE9, 0x94,
+                                               0x5A, 0xB5, 0x5E, 0xB1, 0x59,
+                                               0x6D, 0x2B, 0x2C, 0x1E, 0x93,
+                                               0xCB, 0xE6, 0x33, 0x3A, 0xAD,
+                                               0x5E, 0xB3, 0xDB, 0xEE, 0x37,
+                                               0x3C, 0x2E, 0x9F, 0xD3, 0xEB,
+                                               0xF6, 0x3B, 0x3E, 0xAF, 0x6F,
+                                               0xC5, 0x64, 0x33, 0x5A, 0xCD,
+                                               0x76, 0xC3, 0xE5, 0x60 };
+
+static struct send_ussd_test send_ussd_data_111 = {
+       .pdu = send_ussd_111,
+       .pdu_len = sizeof(send_ussd_111),
+       .qualifier = 0x00,
+       .alpha_id = "7-bit USSD",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_121 = {
+       .pdu = send_ussd_121,
+       .pdu_len = sizeof(send_ussd_121),
+       .qualifier = 0x00,
+       .alpha_id = "8-bit USSD",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_131 = {
+       .pdu = send_ussd_131,
+       .pdu_len = sizeof(send_ussd_131),
+       .qualifier = 0x00,
+       .alpha_id = "UCS2 USSD",
+       .ussd = "ЗДРАВСТВУЙТЕ"
+};
+
+static struct send_ussd_test send_ussd_data_161 = {
+       .pdu = send_ussd_161,
+       .pdu_len = sizeof(send_ussd_161),
+       .qualifier = 0x00,
+       .alpha_id = "once a RELEASE COMPLETE message containing the USSD "
+               "Return Result message not containing an error has been "
+               "received from the network, the ME shall inform the SIM "
+               "that the command has",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_171 = {
+       .pdu = send_ussd_171,
+       .pdu_len = sizeof(send_ussd_171),
+       .qualifier = 0x00,
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_181 = {
+       .pdu = send_ussd_181,
+       .pdu_len = sizeof(send_ussd_181),
+       .qualifier = 0x00,
+       .alpha_id = "",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_211 = {
+       .pdu = send_ussd_211,
+       .pdu_len = sizeof(send_ussd_211),
+       .qualifier = 0x00,
+       .alpha_id = "Basic Icon",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_ussd_test send_ussd_data_221 = {
+       .pdu = send_ussd_221,
+       .pdu_len = sizeof(send_ussd_221),
+       .qualifier = 0x00,
+       .alpha_id = "Color Icon",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct send_ussd_test send_ussd_data_231 = {
+       .pdu = send_ussd_231,
+       .pdu_len = sizeof(send_ussd_231),
+       .qualifier = 0x00,
+       .alpha_id = "Basic Icon",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_ussd_test send_ussd_data_241 = {
+       .pdu = send_ussd_241,
+       .pdu_len = sizeof(send_ussd_241),
+       .qualifier = 0x00,
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+/* The ussd is not complete in spec */
+static struct send_ussd_test send_ussd_data_311 = {
+       .pdu = send_ussd_311,
+       .pdu_len = sizeof(send_ussd_311),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_411 = {
+       .pdu = send_ussd_411,
+       .pdu_len = sizeof(send_ussd_411),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_412 = {
+       .pdu = send_ussd_412,
+       .pdu_len = sizeof(send_ussd_412),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_421 = {
+       .pdu = send_ussd_421,
+       .pdu_len = sizeof(send_ussd_421),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_422 = {
+       .pdu = send_ussd_422,
+       .pdu_len = sizeof(send_ussd_422),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_431 = {
+       .pdu = send_ussd_431,
+       .pdu_len = sizeof(send_ussd_431),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_432 = {
+       .pdu = send_ussd_432,
+       .pdu_len = sizeof(send_ussd_432),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_441 = {
+       .pdu = send_ussd_441,
+       .pdu_len = sizeof(send_ussd_441),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_442 = {
+       .pdu = send_ussd_442,
+       .pdu_len = sizeof(send_ussd_442),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_443 = {
+       .pdu = send_ussd_443,
+       .pdu_len = sizeof(send_ussd_443),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_451 = {
+       .pdu = send_ussd_451,
+       .pdu_len = sizeof(send_ussd_451),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_452 = {
+       .pdu = send_ussd_452,
+       .pdu_len = sizeof(send_ussd_452),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_453 = {
+       .pdu = send_ussd_453,
+       .pdu_len = sizeof(send_ussd_453),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_461 = {
+       .pdu = send_ussd_461,
+       .pdu_len = sizeof(send_ussd_461),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x10, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_462 = {
+       .pdu = send_ussd_462,
+       .pdu_len = sizeof(send_ussd_462),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_463 = {
+       .pdu = send_ussd_463,
+       .pdu_len = sizeof(send_ussd_463),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_471 = {
+       .pdu = send_ussd_471,
+       .pdu_len = sizeof(send_ussd_471),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x20, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_472 = {
+       .pdu = send_ussd_472,
+       .pdu_len = sizeof(send_ussd_472),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_473 = {
+       .pdu = send_ussd_473,
+       .pdu_len = sizeof(send_ussd_473),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_481 = {
+       .pdu = send_ussd_481,
+       .pdu_len = sizeof(send_ussd_481),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_482 = {
+       .pdu = send_ussd_482,
+       .pdu_len = sizeof(send_ussd_482),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_483 = {
+       .pdu = send_ussd_483,
+       .pdu_len = sizeof(send_ussd_483),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_491 = {
+       .pdu = send_ussd_491,
+       .pdu_len = sizeof(send_ussd_491),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_492 = {
+       .pdu = send_ussd_492,
+       .pdu_len = sizeof(send_ussd_492),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_493 = {
+       .pdu = send_ussd_493,
+       .pdu_len = sizeof(send_ussd_493),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 3",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_4101 = {
+       .pdu = send_ussd_4101,
+       .pdu_len = sizeof(send_ussd_4101),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 1",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct send_ussd_test send_ussd_data_4102 = {
+       .pdu = send_ussd_4102,
+       .pdu_len = sizeof(send_ussd_4102),
+       .qualifier = 0x00,
+       .alpha_id = "Text Attribute 2",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_511 = {
+       .pdu = send_ussd_511,
+       .pdu_len = sizeof(send_ussd_511),
+       .qualifier = 0x00,
+       .alpha_id = "你好",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static struct send_ussd_test send_ussd_data_611 = {
+       .pdu = send_ussd_611,
+       .pdu_len = sizeof(send_ussd_611),
+       .qualifier = 0x00,
+       .alpha_id = "ル",
+       .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-"
+               "1234567890"
+};
+
+static void test_send_ussd(gconstpointer data)
+{
+       const struct send_ussd_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SEND_USSD);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK);
+
+       check_alpha_id(command->send_ussd.alpha_id, test->alpha_id);
+       check_ussd(&command->send_ussd.ussd_string, test->ussd);
+       check_icon_id(&command->send_ussd.icon_id, &test->icon_id);
+       check_text_attr(&command->send_ussd.text_attr, &test->text_attr);
+       check_frame_id(&command->send_ussd.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct setup_call_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id_usr_cfm;
+       struct stk_address addr;
+       struct stk_ccp ccp;
+       struct stk_subaddress subaddr;
+       struct stk_duration duration;
+       struct stk_icon_id icon_id_usr_cfm;
+       char *alpha_id_call_setup;
+       struct stk_icon_id icon_id_call_setup;
+       struct stk_text_attribute text_attr_usr_cfm;
+       struct stk_text_attribute text_attr_call_setup;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char setup_call_111[] = { 0xD0, 0x1E, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x08, 0x4E, 0x6F, 0x74,
+                                               0x20, 0x62, 0x75, 0x73, 0x79,
+                                               0x86, 0x09, 0x91, 0x10, 0x32,
+                                               0x04, 0x21, 0x43, 0x65, 0x1C,
+                                               0x2C };
+
+static unsigned char setup_call_141[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x10,
+                                               0x02, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x07, 0x4F, 0x6E, 0x20,
+                                               0x68, 0x6F, 0x6C, 0x64, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C };
+
+static unsigned char setup_call_151[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x10,
+                                               0x04, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x44, 0x69, 0x73,
+                                               0x63, 0x6F, 0x6E, 0x6E, 0x65,
+                                               0x63, 0x74, 0x86, 0x09, 0x91,
+                                               0x10, 0x32, 0x04, 0x21, 0x43,
+                                               0x65, 0x1C, 0x2C };
+
+static unsigned char setup_call_181[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x11, 0x43, 0x61, 0x70,
+                                               0x61, 0x62, 0x69, 0x6C, 0x69,
+                                               0x74, 0x79, 0x20, 0x63, 0x6F,
+                                               0x6E, 0x66, 0x69, 0x67, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C,
+                                               0x87, 0x02, 0x01, 0xA0 };
+
+static unsigned char setup_call_191[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x10,
+                                               0x01, 0x82, 0x02, 0x81, 0x83,
+                                               0x86, 0x11, 0x91, 0x10, 0x32,
+                                               0x54, 0x76, 0x98, 0x10, 0x32,
+                                               0x54, 0x76, 0x98, 0x10, 0x32,
+                                               0x54, 0x76, 0x98, 0x10 };
+
+static unsigned char setup_call_1101[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01,
+                                               0x10, 0x01, 0x82, 0x02, 0x81,
+                                               0x83, 0x85, 0x81, 0xED, 0x54,
+                                               0x68, 0x72, 0x65, 0x65, 0x20,
+                                               0x74, 0x79, 0x70, 0x65, 0x73,
+                                               0x20, 0x61, 0x72, 0x65, 0x20,
+                                               0x64, 0x65, 0x66, 0x69, 0x6E,
+                                               0x65, 0x64, 0x3A, 0x20, 0x2D,
+                                               0x20, 0x73, 0x65, 0x74, 0x20,
+                                               0x75, 0x70, 0x20, 0x61, 0x20,
+                                               0x63, 0x61, 0x6C, 0x6C, 0x2C,
+                                               0x20, 0x62, 0x75, 0x74, 0x20,
+                                               0x6F, 0x6E, 0x6C, 0x79, 0x20,
+                                               0x69, 0x66, 0x20, 0x6E, 0x6F,
+                                               0x74, 0x20, 0x63, 0x75, 0x72,
+                                               0x72, 0x65, 0x6E, 0x74, 0x6C,
+                                               0x79, 0x20, 0x62, 0x75, 0x73,
+                                               0x79, 0x20, 0x6F, 0x6E, 0x20,
+                                               0x61, 0x6E, 0x6F, 0x74, 0x68,
+                                               0x65, 0x72, 0x20, 0x63, 0x61,
+                                               0x6C, 0x6C, 0x3B, 0x20, 0x2D,
+                                               0x20, 0x73, 0x65, 0x74, 0x20,
+                                               0x75, 0x70, 0x20, 0x61, 0x20,
+                                               0x63, 0x61, 0x6C, 0x6C, 0x2C,
+                                               0x20, 0x70, 0x75, 0x74, 0x74,
+                                               0x69, 0x6E, 0x67, 0x20, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x6F, 0x74,
+                                               0x68, 0x65, 0x72, 0x20, 0x63,
+                                               0x61, 0x6C, 0x6C, 0x73, 0x20,
+                                               0x28, 0x69, 0x66, 0x20, 0x61,
+                                               0x6E, 0x79, 0x29, 0x20, 0x6F,
+                                               0x6E, 0x20, 0x68, 0x6F, 0x6C,
+                                               0x64, 0x3B, 0x20, 0x2D, 0x20,
+                                               0x73, 0x65, 0x74, 0x20, 0x75,
+                                               0x70, 0x20, 0x61, 0x20, 0x63,
+                                               0x61, 0x6C, 0x6C, 0x2C, 0x20,
+                                               0x64, 0x69, 0x73, 0x63, 0x6F,
+                                               0x6E, 0x6E, 0x65, 0x63, 0x74,
+                                               0x69, 0x6E, 0x67, 0x20, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x6F, 0x74,
+                                               0x68, 0x65, 0x72, 0x20, 0x63,
+                                               0x61, 0x6C, 0x6C, 0x73, 0x20,
+                                               0x28, 0x69, 0x66, 0x20, 0x61,
+                                               0x6E, 0x79, 0x29, 0x20, 0x66,
+                                               0x69, 0x72, 0x73, 0x74, 0x2E,
+                                               0x20, 0x46, 0x6F, 0x72, 0x20,
+                                               0x65, 0x61, 0x63, 0x68, 0x20,
+                                               0x6F, 0x66, 0x20, 0x74, 0x68,
+                                               0x65, 0x73, 0x65, 0x20, 0x74,
+                                               0x79, 0x70, 0x65, 0x73, 0x2C,
+                                               0x20, 0x86, 0x02, 0x91, 0x10 };
+
+static unsigned char setup_call_1111[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0C, 0x43, 0x61, 0x6C,
+                                               0x6C, 0x65, 0x64, 0x20, 0x70,
+                                               0x61, 0x72, 0x74, 0x79, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C,
+                                               0x88, 0x07, 0x80, 0x50, 0x95,
+                                               0x95, 0x95, 0x95, 0x95 };
+
+static unsigned char setup_call_1121[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x10,
+                                               0x01, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x08, 0x44, 0x75, 0x72,
+                                               0x61, 0x74, 0x69, 0x6F, 0x6E,
+                                               0x86, 0x09, 0x91, 0x10, 0x32,
+                                               0x04, 0x21, 0x43, 0x65, 0x1C,
+                                               0x2C, 0x84, 0x02, 0x01, 0x0A };
+
+static unsigned char setup_call_211[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0C, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C,
+                                               0x85, 0x04, 0x43, 0x41, 0x4C,
+                                               0x4C };
+
+static unsigned char setup_call_311[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x16, 0x53, 0x65, 0x74,
+                                               0x20, 0x75, 0x70, 0x20, 0x63,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0x20, 0x33,
+                                               0x2E, 0x31, 0x2E, 0x31, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C,
+                                               0x9E, 0x02, 0x01, 0x01 };
+
+static unsigned char setup_call_321[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x16, 0x53, 0x65, 0x74,
+                                               0x20, 0x75, 0x70, 0x20, 0x63,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0x20, 0x33,
+                                               0x2E, 0x32, 0x2E, 0x31, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C,
+                                               0x9E, 0x02, 0x00, 0x01 };
+
+static unsigned char setup_call_331[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x16, 0x53, 0x65, 0x74,
+                                               0x20, 0x75, 0x70, 0x20, 0x63,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0x20, 0x33,
+                                               0x2E, 0x33, 0x2E, 0x31, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C,
+                                               0x9E, 0x02, 0x01, 0x02 };
+
+static unsigned char setup_call_341[] = { 0xD0, 0x4C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x16, 0x53, 0x65, 0x74,
+                                               0x20, 0x75, 0x70, 0x20, 0x63,
+                                               0x61, 0x6C, 0x6C, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0x20, 0x33,
+                                               0x2E, 0x34, 0x2E, 0x31, 0x86,
+                                               0x09, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x1C, 0x2C,
+                                               0x9E, 0x02, 0x00, 0x01, 0x85,
+                                               0x16, 0x53, 0x65, 0x74, 0x20,
+                                               0x75, 0x70, 0x20, 0x63, 0x61,
+                                               0x6C, 0x6C, 0x20, 0x49, 0x63,
+                                               0x6F, 0x6E, 0x20, 0x33, 0x2E,
+                                               0x34, 0x2E, 0x32, 0x9E, 0x02,
+                                               0x00, 0x01 };
+
+static unsigned char setup_call_411[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_call_412[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32 };
+
+static unsigned char setup_call_421[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x01,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x01, 0xB4 };
+
+static unsigned char setup_call_422[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32 };
+
+static unsigned char setup_call_431[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x02,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x02, 0xB4 };
+
+static unsigned char setup_call_432[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32 };
+
+static unsigned char setup_call_441[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x04,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x04, 0xB4 };
+
+static unsigned char setup_call_442[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_call_443[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x33, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x33 };
+
+static unsigned char setup_call_451[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x08,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x08, 0xB4 };
+
+static unsigned char setup_call_452[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_call_453[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x33, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x33 };
+
+static unsigned char setup_call_461[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x10,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x10, 0xB4 };
+
+static unsigned char setup_call_462[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_call_463[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x33, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x33 };
+
+static unsigned char setup_call_471[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x20,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x20, 0xB4 };
+
+static unsigned char setup_call_472[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_call_473[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x33, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x33 };
+
+static unsigned char setup_call_481[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x40,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x40, 0xB4 };
+
+static unsigned char setup_call_482[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_call_483[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x33, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x33 };
+
+static unsigned char setup_call_491[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x80,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x80, 0xB4 };
+
+static unsigned char setup_call_492[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_call_493[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x33, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x33 };
+
+static unsigned char setup_call_4101[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x31, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x31,
+                                               0xD0, 0x04, 0x00, 0x0E, 0x00,
+                                               0xB4, 0xD0, 0x04, 0x00, 0x06,
+                                               0x00, 0x4B };
+
+static unsigned char setup_call_4102[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0E, 0x43, 0x4F, 0x4E,
+                                               0x46, 0x49, 0x52, 0x4D, 0x41,
+                                               0x54, 0x49, 0x4F, 0x4E, 0x20,
+                                               0x32, 0x86, 0x09, 0x91, 0x10,
+                                               0x32, 0x04, 0x21, 0x43, 0x65,
+                                               0x1C, 0x2C, 0x85, 0x06, 0x43,
+                                               0x41, 0x4C, 0x4C, 0x20, 0x32 };
+
+static unsigned char setup_call_511[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x86, 0x07, 0x91,
+                                               0x10, 0x32, 0x04, 0x21, 0x43,
+                                               0x65 };
+
+static unsigned char setup_call_521[] = { 0xD0, 0x4C, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x1B, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0x00, 0x31, 0x86,
+                                               0x07, 0x91, 0x10, 0x32, 0x04,
+                                               0x21, 0x43, 0x65, 0x85, 0x1B,
+                                               0x80, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15,
+                                               0x00, 0x32 };
+
+static unsigned char setup_call_611[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x80, 0x4E, 0x0D,
+                                               0x5F, 0xD9, 0x86, 0x07, 0x91,
+                                               0x10, 0x32, 0x04, 0x21, 0x43,
+                                               0x65 };
+
+static unsigned char setup_call_621[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x80, 0x78, 0x6E,
+                                               0x5B, 0x9A, 0x86, 0x07, 0x91,
+                                               0x10, 0x32, 0x04, 0x21, 0x43,
+                                               0x65, 0x85, 0x07, 0x80, 0x62,
+                                               0x53, 0x75, 0x35, 0x8B, 0xDD };
+
+static unsigned char setup_call_711[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x03, 0x80, 0x30, 0xEB,
+                                               0x86, 0x07, 0x91, 0x10, 0x32,
+                                               0x04, 0x21, 0x43, 0x65 };
+
+static unsigned char setup_call_721[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x10,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x80, 0x30, 0xEB,
+                                               0x00, 0x31, 0x86, 0x07, 0x91,
+                                               0x10, 0x32, 0x04, 0x21, 0x43,
+                                               0x65, 0x85, 0x05, 0x80, 0x30,
+                                               0xEB, 0x00, 0x32 };
+
+static struct setup_call_test setup_call_data_111 = {
+       .pdu = setup_call_111,
+       .pdu_len = sizeof(setup_call_111),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "Not busy",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       }
+};
+
+static struct setup_call_test setup_call_data_141 = {
+       .pdu = setup_call_141,
+       .pdu_len = sizeof(setup_call_141),
+       .qualifier = 0x02,
+       .alpha_id_usr_cfm = "On hold",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       }
+};
+
+static struct setup_call_test setup_call_data_151 = {
+       .pdu = setup_call_151,
+       .pdu_len = sizeof(setup_call_151),
+       .qualifier = 0x04,
+       .alpha_id_usr_cfm = "Disconnect",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       }
+};
+
+static struct setup_call_test setup_call_data_181 = {
+       .pdu = setup_call_181,
+       .pdu_len = sizeof(setup_call_181),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "Capability config",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .ccp = {
+               .len = 0x02,
+               .ccp = { 0x01, 0xA0 }
+       }
+};
+
+static struct setup_call_test setup_call_data_191 = {
+       .pdu = setup_call_191,
+       .pdu_len = sizeof(setup_call_191),
+       .qualifier = 0x01,
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "01234567890123456789012345678901"
+       }
+};
+
+static struct setup_call_test setup_call_data_1101 = {
+       .pdu = setup_call_1101,
+       .pdu_len = sizeof(setup_call_1101),
+       .qualifier = 0x01,
+       .alpha_id_usr_cfm = "Three types are defined: - set up a call, but "
+                       "only if not currently busy on another call; - set "
+                       "up a call, putting all other calls (if any) on hold; "
+                       "- set up a call, disconnecting all other calls (if "
+                       "any) first. For each of these types, ",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "01"
+       }
+};
+
+static struct setup_call_test setup_call_data_1111 = {
+       .pdu = setup_call_1111,
+       .pdu_len = sizeof(setup_call_1111),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "Called party",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .subaddr = {
+               .len = 0x07,
+               .subaddr = { 0x80, 0x50, 0x95, 0x95, 0x95, 0x95, 0x95 }
+       }
+};
+
+static struct setup_call_test setup_call_data_1121 = {
+       .pdu = setup_call_1121,
+       .pdu_len = sizeof(setup_call_1121),
+       .qualifier = 0x01,
+       .alpha_id_usr_cfm = "Duration",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .duration = {
+               .unit = STK_DURATION_TYPE_SECONDS,
+               .interval = 10,
+       }
+};
+
+static struct setup_call_test setup_call_data_211 = {
+       .pdu = setup_call_211,
+       .pdu_len = sizeof(setup_call_211),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL"
+};
+
+static struct setup_call_test setup_call_data_311 = {
+       .pdu = setup_call_311,
+       .pdu_len = sizeof(setup_call_311),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "Set up call Icon 3.1.1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .icon_id_usr_cfm = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct setup_call_test setup_call_data_321 = {
+       .pdu = setup_call_321,
+       .pdu_len = sizeof(setup_call_321),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "Set up call Icon 3.2.1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .icon_id_usr_cfm = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct setup_call_test setup_call_data_331 = {
+       .pdu = setup_call_331,
+       .pdu_len = sizeof(setup_call_331),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "Set up call Icon 3.3.1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .icon_id_usr_cfm = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct setup_call_test setup_call_data_341 = {
+       .pdu = setup_call_341,
+       .pdu_len = sizeof(setup_call_341),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "Set up call Icon 3.4.1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .icon_id_usr_cfm = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       },
+       .alpha_id_call_setup = "Set up call Icon 3.4.2",
+       .icon_id_call_setup = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct setup_call_test setup_call_data_411 = {
+       .pdu = setup_call_411,
+       .pdu_len = sizeof(setup_call_411),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_412 = {
+       .pdu = setup_call_412,
+       .pdu_len = sizeof(setup_call_412),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2"
+};
+
+static struct setup_call_test setup_call_data_421 = {
+       .pdu = setup_call_421,
+       .pdu_len = sizeof(setup_call_421),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x01, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x01, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_422 = {
+       .pdu = setup_call_422,
+       .pdu_len = sizeof(setup_call_422),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2"
+};
+
+static struct setup_call_test setup_call_data_431 = {
+       .pdu = setup_call_431,
+       .pdu_len = sizeof(setup_call_431),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x02, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x02, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_432 = {
+       .pdu = setup_call_432,
+       .pdu_len = sizeof(setup_call_432),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2"
+};
+
+static struct setup_call_test setup_call_data_441 = {
+       .pdu = setup_call_441,
+       .pdu_len = sizeof(setup_call_441),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x04, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x04, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_442 = {
+       .pdu = setup_call_442,
+       .pdu_len = sizeof(setup_call_442),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_443 = {
+       .pdu = setup_call_443,
+       .pdu_len = sizeof(setup_call_443),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 3",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 3"
+};
+
+static struct setup_call_test setup_call_data_451 = {
+       .pdu = setup_call_451,
+       .pdu_len = sizeof(setup_call_451),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x08, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x08, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_452 = {
+       .pdu = setup_call_452,
+       .pdu_len = sizeof(setup_call_452),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_453 = {
+       .pdu = setup_call_453,
+       .pdu_len = sizeof(setup_call_453),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 3",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 3"
+};
+
+static struct setup_call_test setup_call_data_461 = {
+       .pdu = setup_call_461,
+       .pdu_len = sizeof(setup_call_461),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x10, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x10, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_462 = {
+       .pdu = setup_call_462,
+       .pdu_len = sizeof(setup_call_462),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_463 = {
+       .pdu = setup_call_463,
+       .pdu_len = sizeof(setup_call_463),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 3",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 3"
+};
+
+static struct setup_call_test setup_call_data_471 = {
+       .pdu = setup_call_471,
+       .pdu_len = sizeof(setup_call_471),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x20, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x20, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_472 = {
+       .pdu = setup_call_472,
+       .pdu_len = sizeof(setup_call_472),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_473 = {
+       .pdu = setup_call_473,
+       .pdu_len = sizeof(setup_call_473),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 3",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 3"
+};
+
+static struct setup_call_test setup_call_data_481 = {
+       .pdu = setup_call_481,
+       .pdu_len = sizeof(setup_call_481),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x40, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x40, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_482 = {
+       .pdu = setup_call_482,
+       .pdu_len = sizeof(setup_call_482),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_483 = {
+       .pdu = setup_call_483,
+       .pdu_len = sizeof(setup_call_483),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 3",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 3"
+};
+
+static struct setup_call_test setup_call_data_491 = {
+       .pdu = setup_call_491,
+       .pdu_len = sizeof(setup_call_491),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x80, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x80, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_492 = {
+       .pdu = setup_call_492,
+       .pdu_len = sizeof(setup_call_492),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0xB4 }
+       }
+};
+
+static struct setup_call_test setup_call_data_493 = {
+       .pdu = setup_call_493,
+       .pdu_len = sizeof(setup_call_493),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 3",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 3"
+};
+
+static struct setup_call_test setup_call_data_4101 = {
+       .pdu = setup_call_4101,
+       .pdu_len = sizeof(setup_call_4101),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 1",
+       .text_attr_usr_cfm = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+       .text_attr_call_setup = {
+               .len = 4,
+               .attributes = { 0x00, 0x06, 0x00, 0x4B }
+       }
+};
+
+static struct setup_call_test setup_call_data_4102 = {
+       .pdu = setup_call_4102,
+       .pdu_len = sizeof(setup_call_4102),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "CONFIRMATION 2",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456c1c2"
+       },
+       .alpha_id_call_setup = "CALL 2"
+};
+
+static struct setup_call_test setup_call_data_511 = {
+       .pdu = setup_call_511,
+       .pdu_len = sizeof(setup_call_511),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "ЗДРАВСТВУЙТЕ",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456"
+       }
+};
+
+static struct setup_call_test setup_call_data_521 = {
+       .pdu = setup_call_521,
+       .pdu_len = sizeof(setup_call_521),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "ЗДРАВСТВУЙТЕ1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456"
+       },
+       .alpha_id_call_setup = "ЗДРАВСТВУЙТЕ2"
+};
+
+static struct setup_call_test setup_call_data_611 = {
+       .pdu = setup_call_611,
+       .pdu_len = sizeof(setup_call_611),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "不忙",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456"
+       }
+};
+
+static struct setup_call_test setup_call_data_621 = {
+       .pdu = setup_call_621,
+       .pdu_len = sizeof(setup_call_621),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "确定",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456"
+       },
+       .alpha_id_call_setup = "打电话"
+};
+
+static struct setup_call_test setup_call_data_711 = {
+       .pdu = setup_call_711,
+       .pdu_len = sizeof(setup_call_711),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "ル",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456"
+       }
+};
+
+static struct setup_call_test setup_call_data_721 = {
+       .pdu = setup_call_721,
+       .pdu_len = sizeof(setup_call_721),
+       .qualifier = 0x00,
+       .alpha_id_usr_cfm = "ル1",
+       .addr = {
+               .ton_npi = 0x91,
+               .number = "012340123456"
+       },
+       .alpha_id_call_setup = "ル2"
+};
+
+static void test_setup_call(gconstpointer data)
+{
+       const struct setup_call_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SETUP_CALL);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK);
+
+       check_alpha_id(command->setup_call.alpha_id_usr_cfm,
+                                       test->alpha_id_usr_cfm);
+       check_address(&command->setup_call.addr, &test->addr);
+       check_ccp(&command->setup_call.ccp, &test->ccp);
+       check_subaddress(&command->setup_call.subaddr, &test->subaddr);
+       check_duration(&command->setup_call.duration, &test->duration);
+       check_icon_id(&command->setup_call.icon_id_usr_cfm,
+                                       &test->icon_id_usr_cfm);
+       check_alpha_id(command->setup_call.alpha_id_call_setup,
+                                       test->alpha_id_call_setup);
+       check_icon_id(&command->setup_call.icon_id_call_setup,
+                                       &test->icon_id_call_setup);
+       check_text_attr(&command->setup_call.text_attr_usr_cfm,
+                                       &test->text_attr_usr_cfm);
+       check_text_attr(&command->setup_call.text_attr_call_setup,
+                                       &test->text_attr_call_setup);
+       check_frame_id(&command->setup_call.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct refresh_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       struct stk_file file_list[MAX_ITEM];
+       struct stk_aid aid;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char refresh_121[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x01,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x92, 0x05, 0x01, 0x3F, 0x00,
+                                               0x2F, 0xE2 };
+
+static unsigned char refresh_151[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x01,
+                                               0x04, 0x82, 0x02, 0x81, 0x82 };
+
+static struct refresh_test refresh_data_121 = {
+       .pdu = refresh_121,
+       .pdu_len = sizeof(refresh_121),
+       .qualifier = 0x01,
+       .file_list = {{
+               .len = 4,
+               .file = { 0x3F, 0x00, 0x2F, 0xE2 }
+       }}
+};
+
+static struct refresh_test refresh_data_151 = {
+       .pdu = refresh_151,
+       .pdu_len = sizeof(refresh_151),
+       .qualifier = 0x04
+};
+
+/* Defined in TS 102.384 Section 27.22.4.7 */
+static void test_refresh(gconstpointer data)
+{
+       const struct refresh_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_REFRESH);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_file_list(command->refresh.file_list, test->file_list);
+       check_aid(&command->refresh.aid, &test->aid);
+       check_alpha_id(command->refresh.alpha_id, test->alpha_id);
+       check_icon_id(&command->refresh.icon_id, &test->icon_id);
+       check_text_attr(&command->refresh.text_attr, &test->text_attr);
+       check_frame_id(&command->refresh.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct polling_off_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+};
+
+static unsigned char polling_off_112[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x04,
+                                               0x00, 0x82, 0x02, 0x81, 0x82 };
+
+static struct polling_off_test polling_off_data_112 = {
+       .pdu = polling_off_112,
+       .pdu_len = sizeof(polling_off_112),
+       .qualifier = 0x00,
+};
+
+static void test_polling_off(gconstpointer data)
+{
+       const struct polling_off_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_POLLING_OFF);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       stk_command_free(command);
+}
+
+struct provide_local_info_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+};
+
+static unsigned char provide_local_info_121[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                               0x26, 0x01, 0x82, 0x02, 0x81,
+                                               0x82 };
+
+static unsigned char provide_local_info_141[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                               0x26, 0x03, 0x82, 0x02, 0x81,
+                                               0x82 };
+
+static unsigned char provide_local_info_151[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                               0x26, 0x04, 0x82, 0x02, 0x81,
+                                               0x82 };
+
+static unsigned char provide_local_info_181[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                               0x26, 0x07, 0x82, 0x02, 0x81,
+                                               0x82 };
+
+static unsigned char provide_local_info_191[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                               0x26, 0x08, 0x82, 0x02, 0x81,
+                                               0x82 };
+
+static unsigned char provide_local_info_1111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                               0x26, 0x0A, 0x82, 0x02, 0x81,
+                                               0x82 };
+
+static struct provide_local_info_test provide_local_info_data_121 = {
+       .pdu = provide_local_info_121,
+       .pdu_len = sizeof(provide_local_info_121),
+       .qualifier = 0x01
+};
+
+static struct provide_local_info_test provide_local_info_data_141 = {
+       .pdu = provide_local_info_141,
+       .pdu_len = sizeof(provide_local_info_141),
+       .qualifier = 0x03
+};
+
+static struct provide_local_info_test provide_local_info_data_151 = {
+       .pdu = provide_local_info_151,
+       .pdu_len = sizeof(provide_local_info_151),
+       .qualifier = 0x04
+};
+
+static struct provide_local_info_test provide_local_info_data_181 = {
+       .pdu = provide_local_info_181,
+       .pdu_len = sizeof(provide_local_info_181),
+       .qualifier = 0x07
+};
+
+static struct provide_local_info_test provide_local_info_data_191 = {
+       .pdu = provide_local_info_191,
+       .pdu_len = sizeof(provide_local_info_191),
+       .qualifier = 0x08
+};
+
+static struct provide_local_info_test provide_local_info_data_1111 = {
+       .pdu = provide_local_info_1111,
+       .pdu_len = sizeof(provide_local_info_1111),
+       .qualifier = 0x0A
+};
+
+static void test_provide_local_info(gconstpointer data)
+{
+       const struct provide_local_info_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       stk_command_free(command);
+}
+
+struct setup_event_list_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       struct stk_event_list event_list;
+};
+
+static unsigned char setup_event_list_111[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01,
+                                               0x05, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x99, 0x01, 0x04 };
+
+static unsigned char setup_event_list_121[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01,
+                                               0x05, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x99, 0x02, 0x05, 0x07 };
+
+static unsigned char setup_event_list_122[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01,
+                                               0x05, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x99, 0x01, 0x07 };
+
+static unsigned char setup_event_list_131[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01,
+                                               0x05, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x99, 0x01, 0x07 };
+
+static unsigned char setup_event_list_132[] = { 0xD0, 0x0B, 0x81, 0x03, 0x01,
+                                               0x05, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x99, 0x00 };
+
+static unsigned char setup_event_list_141[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01,
+                                               0x05, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x99, 0x01, 0x07 };
+
+static struct setup_event_list_test setup_event_list_data_111 = {
+       .pdu = setup_event_list_111,
+       .pdu_len = sizeof(setup_event_list_111),
+       .qualifier = 0x00,
+       .event_list = {
+               .len = 1,
+               .list = { STK_EVENT_TYPE_USER_ACTIVITY }
+       }
+};
+
+static struct setup_event_list_test setup_event_list_data_121 = {
+       .pdu = setup_event_list_121,
+       .pdu_len = sizeof(setup_event_list_121),
+       .qualifier = 0x00,
+       .event_list = {
+               .len = 2,
+               .list = { STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE,
+                               STK_EVENT_TYPE_LANGUAGE_SELECTION }
+       }
+};
+
+static struct setup_event_list_test setup_event_list_data_122 = {
+       .pdu = setup_event_list_122,
+       .pdu_len = sizeof(setup_event_list_122),
+       .qualifier = 0x00,
+       .event_list = {
+               .len = 1,
+               .list = { STK_EVENT_TYPE_LANGUAGE_SELECTION }
+       }
+};
+
+static struct setup_event_list_test setup_event_list_data_131 = {
+       .pdu = setup_event_list_131,
+       .pdu_len = sizeof(setup_event_list_131),
+       .qualifier = 0x00,
+       .event_list = {
+               .len = 1,
+               .list = { STK_EVENT_TYPE_LANGUAGE_SELECTION }
+       }
+};
+
+static struct setup_event_list_test setup_event_list_data_132 = {
+       .pdu = setup_event_list_132,
+       .pdu_len = sizeof(setup_event_list_132),
+       .qualifier = 0x00
+};
+
+static struct setup_event_list_test setup_event_list_data_141 = {
+       .pdu = setup_event_list_141,
+       .pdu_len = sizeof(setup_event_list_141),
+       .qualifier = 0x00,
+       .event_list = {
+               .len = 1,
+               .list = { STK_EVENT_TYPE_LANGUAGE_SELECTION }
+       }
+};
+
+static void test_setup_event_list(gconstpointer data)
+{
+       const struct setup_event_list_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SETUP_EVENT_LIST);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_event_list(&command->setup_event_list.event_list,
+                                               &test->event_list);
+
+       stk_command_free(command);
+}
+
+struct perform_card_apdu_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       unsigned char dst;
+       struct stk_c_apdu c_apdu;
+};
+
+static unsigned char perform_card_apdu_111[] = { 0xD0, 0x12, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x07, 0xA0, 0xA4,
+                                               0x00, 0x00, 0x02, 0x3F, 0x00 };
+
+static unsigned char perform_card_apdu_112[] = { 0xD0, 0x10, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x05, 0xA0, 0xC0,
+                                               0x00, 0x00, 0x1B };
+
+static unsigned char perform_card_apdu_121[] = { 0xD0, 0x12, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x07, 0xA0, 0xA4,
+                                               0x00, 0x00, 0x02, 0x7F, 0x20 };
+
+static unsigned char perform_card_apdu_122[] = { 0xD0, 0x12, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x07, 0xA0, 0xA4,
+                                               0x00, 0x00, 0x02, 0x6F, 0x30 };
+
+static unsigned char perform_card_apdu_123[] = { 0xD0, 0x28, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x1D, 0xA0, 0xD6,
+                                               0x00, 0x00, 0x18, 0x00, 0x01,
+                                               0x02, 0x03, 0x04, 0x05, 0x06,
+                                               0x07, 0x08, 0x09, 0x0A, 0x0B,
+                                               0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+                                               0x11, 0x12, 0x13, 0x14, 0x15,
+                                               0x16, 0x17 };
+
+static unsigned char perform_card_apdu_124[] = { 0xD0, 0x10, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x05, 0xA0, 0xB0,
+                                               0x00, 0x00, 0x18 };
+
+static unsigned char perform_card_apdu_125[] = { 0xD0, 0x28, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x1D, 0xA0, 0xD6,
+                                               0x00, 0x00, 0x18, 0xFF, 0xFF,
+                                               0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                                               0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                                               0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                                               0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                                               0xFF, 0xFF };
+
+static unsigned char perform_card_apdu_151[] = { 0xD0, 0x12, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x17, 0xA2, 0x07, 0xA0, 0xA4,
+                                               0x00, 0x00, 0x02, 0x3F, 0x00 };
+
+static unsigned char perform_card_apdu_211[] = { 0xD0, 0x12, 0x81, 0x03, 0x01,
+                                               0x30, 0x00, 0x82, 0x02, 0x81,
+                                               0x11, 0xA2, 0x07, 0xA0, 0xA4,
+                                               0x00, 0x00, 0x02, 0x3F, 0x00 };
+
+static struct perform_card_apdu_test perform_card_apdu_data_111 = {
+       .pdu = perform_card_apdu_111,
+       .pdu_len = sizeof(perform_card_apdu_111),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_SELECT,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .lc = 0x02,
+               .data = { 0x3F, 0x00 }
+       }
+};
+
+static struct perform_card_apdu_test perform_card_apdu_data_112 = {
+       .pdu = perform_card_apdu_112,
+       .pdu_len = sizeof(perform_card_apdu_112),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_GET_RESPONSE,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .has_le = 1,
+               .le = 0x1B
+       }
+};
+
+static struct perform_card_apdu_test perform_card_apdu_data_121 = {
+       .pdu = perform_card_apdu_121,
+       .pdu_len = sizeof(perform_card_apdu_121),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_SELECT,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .lc = 0x02,
+               .data = { 0x7F, 0x20 }
+       }
+};
+
+static struct perform_card_apdu_test perform_card_apdu_data_122 = {
+       .pdu = perform_card_apdu_122,
+       .pdu_len = sizeof(perform_card_apdu_122),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_SELECT,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .lc = 0x02,
+               .data = { 0x6F, 0x30 }
+       }
+};
+
+/* Byte 14 of Data is not correct in spec. */
+static struct perform_card_apdu_test perform_card_apdu_data_123 = {
+       .pdu = perform_card_apdu_123,
+       .pdu_len = sizeof(perform_card_apdu_123),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_UPDATE_BINARY_D6,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .lc = 0x18,
+               .data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                               0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+                               0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }
+       }
+};
+
+static struct perform_card_apdu_test perform_card_apdu_data_124 = {
+       .pdu = perform_card_apdu_124,
+       .pdu_len = sizeof(perform_card_apdu_124),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_READ_BINARY_B0,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .has_le = 1,
+               .le = 0x18
+       }
+};
+
+static struct perform_card_apdu_test perform_card_apdu_data_125 = {
+       .pdu = perform_card_apdu_125,
+       .pdu_len = sizeof(perform_card_apdu_125),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_UPDATE_BINARY_D6,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .lc = 0x18,
+               .data = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+       }
+};
+
+static struct perform_card_apdu_test perform_card_apdu_data_151 = {
+       .pdu = perform_card_apdu_151,
+       .pdu_len = sizeof(perform_card_apdu_151),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_7,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_SELECT,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .lc = 0x02,
+               .data = { 0x3F, 0x00 }
+       }
+};
+
+static struct perform_card_apdu_test perform_card_apdu_data_211 = {
+       .pdu = perform_card_apdu_211,
+       .pdu_len = sizeof(perform_card_apdu_211),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1,
+       .c_apdu = {
+               .cla = 0xA0,
+               .ins = STK_INS_SELECT,
+               .p1 = 0x00,
+               .p2 = 0x00,
+               .lc = 0x02,
+               .data = { 0x3F, 0x00 }
+       }
+};
+
+static void test_perform_card_apdu(gconstpointer data)
+{
+       const struct perform_card_apdu_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_PERFORM_CARD_APDU);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == test->dst);
+
+       check_c_apdu(&command->perform_card_apdu.c_apdu, &test->c_apdu);
+
+       stk_command_free(command);
+}
+
+struct get_reader_status_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+};
+
+static unsigned char get_reader_status_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                               0x33, 0x00, 0x82, 0x02, 0x81,
+                                               0x82 };
+
+static struct get_reader_status_test get_reader_status_data_111 = {
+       .pdu = get_reader_status_111,
+       .pdu_len = sizeof(get_reader_status_111),
+       .qualifier = STK_QUALIFIER_TYPE_CARD_READER_STATUS,
+};
+
+static void test_get_reader_status(gconstpointer data)
+{
+       const struct get_reader_status_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_GET_READER_STATUS);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+
+       if (command->qualifier == STK_QUALIFIER_TYPE_CARD_READER_STATUS)
+               g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+       else
+               g_assert(command->dst <
+                               STK_DEVICE_IDENTITY_TYPE_CARD_READER_0 ||
+                       command->dst >
+                               STK_DEVICE_IDENTITY_TYPE_CARD_READER_7);
+
+       stk_command_free(command);
+}
+
+struct timer_mgmt_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       unsigned char timer_id;
+       struct stk_timer_value timer_value;
+};
+
+static unsigned char timer_mgmt_111[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01, 0xA5, 0x03,
+                                               0x00, 0x50, 0x00 };
+
+static unsigned char timer_mgmt_112[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01 };
+
+static unsigned char timer_mgmt_113[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01, 0xA5, 0x03,
+                                               0x00, 0x10, 0x03 };
+
+static unsigned char timer_mgmt_114[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01 };
+
+static unsigned char timer_mgmt_121[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x02, 0xA5, 0x03,
+                                               0x32, 0x95, 0x95 };
+
+static unsigned char timer_mgmt_122[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x02 };
+
+static unsigned char timer_mgmt_123[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x02, 0xA5, 0x03,
+                                               0x00, 0x10, 0x01 };
+
+static unsigned char timer_mgmt_124[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x02 };
+
+static unsigned char timer_mgmt_131[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x08, 0xA5, 0x03,
+                                               0x00, 0x02, 0x00 };
+
+static unsigned char timer_mgmt_132[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x08 };
+
+static unsigned char timer_mgmt_133[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x08, 0xA5, 0x03,
+                                               0x10, 0x00, 0x00 };
+
+static unsigned char timer_mgmt_134[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x08 };
+
+static unsigned char timer_mgmt_141[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01 };
+
+static unsigned char timer_mgmt_142[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x02 };
+
+static unsigned char timer_mgmt_143[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x03 };
+
+static unsigned char timer_mgmt_144[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x04 };
+
+static unsigned char timer_mgmt_145[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x05 };
+
+static unsigned char timer_mgmt_146[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x06 };
+
+static unsigned char timer_mgmt_147[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x07 };
+
+static unsigned char timer_mgmt_148[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x02, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x08 };
+
+static unsigned char timer_mgmt_151[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01 };
+
+static unsigned char timer_mgmt_152[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x02 };
+
+static unsigned char timer_mgmt_153[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x03 };
+
+static unsigned char timer_mgmt_154[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x04 };
+
+static unsigned char timer_mgmt_155[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x05 };
+
+static unsigned char timer_mgmt_156[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x06 };
+
+static unsigned char timer_mgmt_157[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x07 };
+
+static unsigned char timer_mgmt_158[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x08 };
+
+static unsigned char timer_mgmt_161[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_162[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x02, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_163[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x03, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_164[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x04, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_165[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x05, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_166[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x06, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_167[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x07, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_168[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x08, 0xA5, 0x03,
+                                               0x00, 0x00, 0x50 };
+
+static unsigned char timer_mgmt_211[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01, 0xA5, 0x03,
+                                               0x00, 0x00, 0x01 };
+
+static unsigned char timer_mgmt_221[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27,
+                                               0x00, 0x82, 0x02, 0x81, 0x82,
+                                               0xA4, 0x01, 0x01, 0xA5, 0x03,
+                                               0x00, 0x00, 0x03 };
+
+static struct timer_mgmt_test timer_mgmt_data_111 = {
+       .pdu = timer_mgmt_111,
+       .pdu_len = sizeof(timer_mgmt_111),
+       .qualifier = 0x00,
+       .timer_id = 1,
+       .timer_value = {
+               .minute = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_112 = {
+       .pdu = timer_mgmt_112,
+       .pdu_len = sizeof(timer_mgmt_112),
+       .qualifier = 0x02,
+       .timer_id = 1
+};
+
+static struct timer_mgmt_test timer_mgmt_data_113 = {
+       .pdu = timer_mgmt_113,
+       .pdu_len = sizeof(timer_mgmt_113),
+       .qualifier = 0x00,
+       .timer_id = 1,
+       .timer_value = {
+               .minute = 1,
+               .second = 30
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_114 = {
+       .pdu = timer_mgmt_114,
+       .pdu_len = sizeof(timer_mgmt_114),
+       .qualifier = 0x01,
+       .timer_id = 1
+};
+
+static struct timer_mgmt_test timer_mgmt_data_121 = {
+       .pdu = timer_mgmt_121,
+       .pdu_len = sizeof(timer_mgmt_121),
+       .qualifier = 0x00,
+       .timer_id = 2,
+       .timer_value = {
+               .hour = 23,
+               .minute = 59,
+               .second = 59
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_122 = {
+       .pdu = timer_mgmt_122,
+       .pdu_len = sizeof(timer_mgmt_122),
+       .qualifier = 0x02,
+       .timer_id = 2
+};
+
+static struct timer_mgmt_test timer_mgmt_data_123 = {
+       .pdu = timer_mgmt_123,
+       .pdu_len = sizeof(timer_mgmt_123),
+       .qualifier = 0x00,
+       .timer_id = 2,
+       .timer_value = {
+               .minute = 1,
+               .second = 10
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_124 = {
+       .pdu = timer_mgmt_124,
+       .pdu_len = sizeof(timer_mgmt_124),
+       .qualifier = 0x01,
+       .timer_id = 2
+};
+
+static struct timer_mgmt_test timer_mgmt_data_131 = {
+       .pdu = timer_mgmt_131,
+       .pdu_len = sizeof(timer_mgmt_131),
+       .qualifier = 0x00,
+       .timer_id = 8,
+       .timer_value = {
+               .minute = 20
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_132 = {
+       .pdu = timer_mgmt_132,
+       .pdu_len = sizeof(timer_mgmt_132),
+       .qualifier = 0x02,
+       .timer_id = 8
+};
+
+static struct timer_mgmt_test timer_mgmt_data_133 = {
+       .pdu = timer_mgmt_133,
+       .pdu_len = sizeof(timer_mgmt_133),
+       .qualifier = 0x00,
+       .timer_id = 8,
+       .timer_value = {
+               .hour = 1
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_134 = {
+       .pdu = timer_mgmt_134,
+       .pdu_len = sizeof(timer_mgmt_134),
+       .qualifier = 0x01,
+       .timer_id = 8
+};
+
+static struct timer_mgmt_test timer_mgmt_data_141 = {
+       .pdu = timer_mgmt_141,
+       .pdu_len = sizeof(timer_mgmt_141),
+       .qualifier = 0x02,
+       .timer_id = 1
+};
+
+static struct timer_mgmt_test timer_mgmt_data_142 = {
+       .pdu = timer_mgmt_142,
+       .pdu_len = sizeof(timer_mgmt_142),
+       .qualifier = 0x02,
+       .timer_id = 2
+};
+
+static struct timer_mgmt_test timer_mgmt_data_143 = {
+       .pdu = timer_mgmt_143,
+       .pdu_len = sizeof(timer_mgmt_143),
+       .qualifier = 0x02,
+       .timer_id = 3
+};
+
+static struct timer_mgmt_test timer_mgmt_data_144 = {
+       .pdu = timer_mgmt_144,
+       .pdu_len = sizeof(timer_mgmt_144),
+       .qualifier = 0x02,
+       .timer_id = 4
+};
+
+static struct timer_mgmt_test timer_mgmt_data_145 = {
+       .pdu = timer_mgmt_145,
+       .pdu_len = sizeof(timer_mgmt_145),
+       .qualifier = 0x02,
+       .timer_id = 5
+};
+
+static struct timer_mgmt_test timer_mgmt_data_146 = {
+       .pdu = timer_mgmt_146,
+       .pdu_len = sizeof(timer_mgmt_146),
+       .qualifier = 0x02,
+       .timer_id = 6
+};
+
+static struct timer_mgmt_test timer_mgmt_data_147 = {
+       .pdu = timer_mgmt_147,
+       .pdu_len = sizeof(timer_mgmt_147),
+       .qualifier = 0x02,
+       .timer_id = 7
+};
+
+static struct timer_mgmt_test timer_mgmt_data_148 = {
+       .pdu = timer_mgmt_148,
+       .pdu_len = sizeof(timer_mgmt_148),
+       .qualifier = 0x02,
+       .timer_id = 8
+};
+
+static struct timer_mgmt_test timer_mgmt_data_151 = {
+       .pdu = timer_mgmt_151,
+       .pdu_len = sizeof(timer_mgmt_151),
+       .qualifier = 0x01,
+       .timer_id = 1
+};
+
+static struct timer_mgmt_test timer_mgmt_data_152 = {
+       .pdu = timer_mgmt_152,
+       .pdu_len = sizeof(timer_mgmt_152),
+       .qualifier = 0x01,
+       .timer_id = 2
+};
+
+static struct timer_mgmt_test timer_mgmt_data_153 = {
+       .pdu = timer_mgmt_153,
+       .pdu_len = sizeof(timer_mgmt_153),
+       .qualifier = 0x01,
+       .timer_id = 3
+};
+
+static struct timer_mgmt_test timer_mgmt_data_154 = {
+       .pdu = timer_mgmt_154,
+       .pdu_len = sizeof(timer_mgmt_154),
+       .qualifier = 0x01,
+       .timer_id = 4
+};
+
+static struct timer_mgmt_test timer_mgmt_data_155 = {
+       .pdu = timer_mgmt_155,
+       .pdu_len = sizeof(timer_mgmt_155),
+       .qualifier = 0x01,
+       .timer_id = 5
+};
+
+static struct timer_mgmt_test timer_mgmt_data_156 = {
+       .pdu = timer_mgmt_156,
+       .pdu_len = sizeof(timer_mgmt_156),
+       .qualifier = 0x01,
+       .timer_id = 6
+};
+
+static struct timer_mgmt_test timer_mgmt_data_157 = {
+       .pdu = timer_mgmt_157,
+       .pdu_len = sizeof(timer_mgmt_157),
+       .qualifier = 0x01,
+       .timer_id = 7
+};
+
+static struct timer_mgmt_test timer_mgmt_data_158 = {
+       .pdu = timer_mgmt_158,
+       .pdu_len = sizeof(timer_mgmt_158),
+       .qualifier = 0x01,
+       .timer_id = 8
+};
+
+static struct timer_mgmt_test timer_mgmt_data_161 = {
+       .pdu = timer_mgmt_161,
+       .pdu_len = sizeof(timer_mgmt_161),
+       .qualifier = 0x00,
+       .timer_id = 1,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_162 = {
+       .pdu = timer_mgmt_162,
+       .pdu_len = sizeof(timer_mgmt_162),
+       .qualifier = 0x00,
+       .timer_id = 2,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_163 = {
+       .pdu = timer_mgmt_163,
+       .pdu_len = sizeof(timer_mgmt_163),
+       .qualifier = 0x00,
+       .timer_id = 3,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_164 = {
+       .pdu = timer_mgmt_164,
+       .pdu_len = sizeof(timer_mgmt_164),
+       .qualifier = 0x00,
+       .timer_id = 4,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_165 = {
+       .pdu = timer_mgmt_165,
+       .pdu_len = sizeof(timer_mgmt_165),
+       .qualifier = 0x00,
+       .timer_id = 5,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_166 = {
+       .pdu = timer_mgmt_166,
+       .pdu_len = sizeof(timer_mgmt_166),
+       .qualifier = 0x00,
+       .timer_id = 6,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_167 = {
+       .pdu = timer_mgmt_167,
+       .pdu_len = sizeof(timer_mgmt_167),
+       .qualifier = 0x00,
+       .timer_id = 7,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_168 = {
+       .pdu = timer_mgmt_168,
+       .pdu_len = sizeof(timer_mgmt_168),
+       .qualifier = 0x00,
+       .timer_id = 8,
+       .timer_value = {
+               .second = 5
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_211 = {
+       .pdu = timer_mgmt_211,
+       .pdu_len = sizeof(timer_mgmt_211),
+       .qualifier = 0x00,
+       .timer_id = 1,
+       .timer_value = {
+               .second = 10
+       }
+};
+
+static struct timer_mgmt_test timer_mgmt_data_221 = {
+       .pdu = timer_mgmt_221,
+       .pdu_len = sizeof(timer_mgmt_221),
+       .qualifier = 0x00,
+       .timer_id = 1,
+       .timer_value = {
+               .second = 30
+       }
+};
+
+static void test_timer_mgmt(gconstpointer data)
+{
+       const struct timer_mgmt_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_TIMER_MANAGEMENT);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_timer_id(command->timer_mgmt.timer_id, test->timer_id);
+       check_timer_value(&command->timer_mgmt.timer_value, &test->timer_value);
+
+       stk_command_free(command);
+}
+
+struct setup_idle_mode_text_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *text;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       char *html;
+       enum stk_command_parse_result status;
+};
+
+static unsigned char setup_idle_mode_text_111[] = { 0xD0, 0x1A, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x0F, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74 };
+
+static unsigned char setup_idle_mode_text_121[] = { 0xD0, 0x18, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x0D, 0x04,
+                                               0x54, 0x6F, 0x6F, 0x6C, 0x6B,
+                                               0x69, 0x74, 0x20, 0x54, 0x65,
+                                               0x73, 0x74 };
+
+static unsigned char setup_idle_mode_text_131[] = { 0xD0, 0x0B, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x00 };
+
+static unsigned char setup_idle_mode_text_171[] = { 0xD0, 0x81, 0xFD, 0x81,
+                                               0x03, 0x01, 0x28, 0x00, 0x82,
+                                               0x02, 0x81, 0x82, 0x8D, 0x81,
+                                               0xF1, 0x00, 0x54, 0x74, 0x19,
+                                               0x34, 0x4D, 0x36, 0x41, 0x73,
+                                               0x74, 0x98, 0xCD, 0x06, 0xCD,
+                                               0xEB, 0x70, 0x38, 0x3B, 0x0F,
+                                               0x0A, 0x83, 0xE8, 0x65, 0x3C,
+                                               0x1D, 0x34, 0xA7, 0xCB, 0xD3,
+                                               0xEE, 0x33, 0x0B, 0x74, 0x47,
+                                               0xA7, 0xC7, 0x68, 0xD0, 0x1C,
+                                               0x1D, 0x66, 0xB3, 0x41, 0xE2,
+                                               0x32, 0x88, 0x9C, 0x9E, 0xC3,
+                                               0xD9, 0xE1, 0x7C, 0x99, 0x0C,
+                                               0x12, 0xE7, 0x41, 0x74, 0x74,
+                                               0x19, 0xD4, 0x2C, 0x82, 0xC2,
+                                               0x73, 0x50, 0xD8, 0x0D, 0x4A,
+                                               0x93, 0xD9, 0x65, 0x50, 0xFB,
+                                               0x4D, 0x2E, 0x83, 0xE8, 0x65,
+                                               0x3C, 0x1D, 0x94, 0x36, 0x83,
+                                               0xE8, 0xE8, 0x32, 0xA8, 0x59,
+                                               0x04, 0xA5, 0xE7, 0xA0, 0xB0,
+                                               0x98, 0x5D, 0x06, 0xD1, 0xDF,
+                                               0x20, 0xF2, 0x1B, 0x94, 0xA6,
+                                               0xBB, 0xA8, 0xE8, 0x32, 0x08,
+                                               0x2E, 0x2F, 0xCF, 0xCB, 0x6E,
+                                               0x7A, 0x98, 0x9E, 0x7E, 0xBB,
+                                               0x41, 0x73, 0x7A, 0x9E, 0x5D,
+                                               0x06, 0xA5, 0xE7, 0x20, 0x76,
+                                               0xD9, 0x4C, 0x07, 0x85, 0xE7,
+                                               0xA0, 0xB0, 0x1B, 0x94, 0x6E,
+                                               0xC3, 0xD9, 0xE5, 0x76, 0xD9,
+                                               0x4D, 0x0F, 0xD3, 0xD3, 0x6F,
+                                               0x37, 0x88, 0x5C, 0x1E, 0xA7,
+                                               0xE7, 0xE9, 0xB7, 0x1B, 0x44,
+                                               0x7F, 0x83, 0xE8, 0xE8, 0x32,
+                                               0xA8, 0x59, 0x04, 0xB5, 0xC3,
+                                               0xEE, 0xBA, 0x39, 0x3C, 0xA6,
+                                               0xD7, 0xE5, 0x65, 0xB9, 0x0B,
+                                               0x44, 0x45, 0x97, 0x41, 0x69,
+                                               0x32, 0xBB, 0x0C, 0x6A, 0xBF,
+                                               0xC9, 0x65, 0x10, 0xBD, 0x8C,
+                                               0xA7, 0x83, 0xE6, 0xE8, 0x30,
+                                               0x9B, 0x0D, 0x12, 0x97, 0x41,
+                                               0xE4, 0xF4, 0x1C, 0xCE, 0x0E,
+                                               0xE7, 0xCB, 0x64, 0x50, 0xDA,
+                                               0x0D, 0x0A, 0x83, 0xDA, 0x61,
+                                               0xB7, 0xBB, 0x2C, 0x07, 0xD1,
+                                               0xD1, 0x61, 0x3A, 0xA8, 0xEC,
+                                               0x9E, 0xD7, 0xE5, 0xE5, 0x39,
+                                               0x88, 0x8E, 0x0E, 0xD3, 0x41,
+                                               0xEE, 0x32 };
+
+static unsigned char setup_idle_mode_text_211[] = { 0xD0, 0x19, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x0A, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x74, 0x65, 0x78, 0x74, 0x9E,
+                                               0x02, 0x00, 0x01 };
+
+static unsigned char setup_idle_mode_text_221[] = { 0xD0, 0x19, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x0A, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x74, 0x65, 0x78, 0x74, 0x9E,
+                                               0x02, 0x01, 0x01 };
+
+static unsigned char setup_idle_mode_text_231[] = { 0xD0, 0x19, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x0A, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x74, 0x65, 0x78, 0x74, 0x9E,
+                                               0x02, 0x00, 0x02 };
+
+static unsigned char setup_idle_mode_text_241[] = { 0xD0, 0x0F, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x00, 0x9E,
+                                               0x02, 0x01, 0x01 };
+
+static unsigned char setup_idle_mode_text_311[] = { 0xD0, 0x24, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x19, 0x08,
+                                               0x04, 0x17, 0x04, 0x14, 0x04,
+                                               0x20, 0x04, 0x10, 0x04, 0x12,
+                                               0x04, 0x21, 0x04, 0x22, 0x04,
+                                               0x12, 0x04, 0x23, 0x04, 0x19,
+                                               0x04, 0x22, 0x04, 0x15 };
+
+static unsigned char setup_idle_mode_text_411[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_412[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32 };
+
+static unsigned char setup_idle_mode_text_421[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x01, 0xB4 };
+
+static unsigned char setup_idle_mode_text_422[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32 };
+
+static unsigned char setup_idle_mode_text_431[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x02, 0xB4 };
+
+static unsigned char setup_idle_mode_text_432[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32 };
+
+static unsigned char setup_idle_mode_text_441[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x04, 0xB4 };
+
+static unsigned char setup_idle_mode_text_442[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_443[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x33 };
+
+static unsigned char setup_idle_mode_text_451[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x08, 0xB4 };
+
+static unsigned char setup_idle_mode_text_452[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_453[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x33 };
+
+static unsigned char setup_idle_mode_text_461[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x10, 0xB4 };
+
+static unsigned char setup_idle_mode_text_462[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_463[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x33 };
+
+static unsigned char setup_idle_mode_text_471[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x20, 0xB4 };
+
+static unsigned char setup_idle_mode_text_472[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_473[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x33 };
+
+static unsigned char setup_idle_mode_text_481[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x40, 0xB4 };
+
+static unsigned char setup_idle_mode_text_482[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_483[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x33 };
+
+static unsigned char setup_idle_mode_text_491[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x80, 0xB4 };
+
+static unsigned char setup_idle_mode_text_492[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_493[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x33 };
+
+static unsigned char setup_idle_mode_text_4101[] = { 0xD0, 0x22, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x31, 0xD0, 0x04, 0x00, 0x10,
+                                               0x00, 0xB4 };
+
+static unsigned char setup_idle_mode_text_4102[] = { 0xD0, 0x1C, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x11, 0x04,
+                                               0x49, 0x64, 0x6C, 0x65, 0x20,
+                                               0x4D, 0x6F, 0x64, 0x65, 0x20,
+                                               0x54, 0x65, 0x78, 0x74, 0x20,
+                                               0x32 };
+
+static unsigned char setup_idle_mode_text_511[] = { 0xD0, 0x10, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x05, 0x08,
+                                               0x4F, 0x60, 0x59, 0x7D };
+
+static unsigned char setup_idle_mode_text_611[] = { 0xD0, 0x14, 0x81, 0x03,
+                                               0x01, 0x28, 0x00, 0x82, 0x02,
+                                               0x81, 0x82, 0x8D, 0x09, 0x08,
+                                               0x00, 0x38, 0x00, 0x30, 0x30,
+                                               0xEB, 0x00, 0x30 };
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_111 = {
+       .pdu = setup_idle_mode_text_111,
+       .pdu_len = sizeof(setup_idle_mode_text_111),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_121 = {
+       .pdu = setup_idle_mode_text_121,
+       .pdu_len = sizeof(setup_idle_mode_text_121),
+       .qualifier = 0x00,
+       .text = "Toolkit Test"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_131 = {
+       .pdu = setup_idle_mode_text_131,
+       .pdu_len = sizeof(setup_idle_mode_text_131),
+       .qualifier = 0x00,
+       .text = ""
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_171 = {
+       .pdu = setup_idle_mode_text_171,
+       .pdu_len = sizeof(setup_idle_mode_text_171),
+       .qualifier = 0x00,
+       .text = "The SIM shall supply a text string, which shall be displayed "
+               "by the ME as an idle mode text if the ME is able to do it."
+               "The presentation style is left as an implementation decision "
+               "to the ME manufacturer. The idle mode text shall be displayed "
+               "in a manner that ensures that ne"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_211 = {
+       .pdu = setup_idle_mode_text_211,
+       .pdu_len = sizeof(setup_idle_mode_text_211),
+       .qualifier = 0x00,
+       .text = "Idle text",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_221 = {
+       .pdu = setup_idle_mode_text_221,
+       .pdu_len = sizeof(setup_idle_mode_text_221),
+       .qualifier = 0x00,
+       .text = "Idle text",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_231 = {
+       .pdu = setup_idle_mode_text_231,
+       .pdu_len = sizeof(setup_idle_mode_text_231),
+       .qualifier = 0x00,
+       .text = "Idle text",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_241 = {
+       .pdu = setup_idle_mode_text_241,
+       .pdu_len = sizeof(setup_idle_mode_text_241),
+       .qualifier = 0x00,
+       .text = "",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       },
+       .status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_311 = {
+       .pdu = setup_idle_mode_text_311,
+       .pdu_len = sizeof(setup_idle_mode_text_311),
+       .qualifier = 0x00,
+       .text = "ЗДРАВСТВУЙТЕ"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_411 = {
+       .pdu = setup_idle_mode_text_411,
+       .pdu_len = sizeof(setup_idle_mode_text_411),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 1</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_412 = {
+       .pdu = setup_idle_mode_text_412,
+       .pdu_len = sizeof(setup_idle_mode_text_412),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_421 = {
+       .pdu = setup_idle_mode_text_421,
+       .pdu_len = sizeof(setup_idle_mode_text_421),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 }
+       },
+       .html = "<div style=\"text-align: center;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 1</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_422 = {
+       .pdu = setup_idle_mode_text_422,
+       .pdu_len = sizeof(setup_idle_mode_text_422),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_431 = {
+       .pdu = setup_idle_mode_text_431,
+       .pdu_len = sizeof(setup_idle_mode_text_431),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 }
+       },
+       .html = "<div style=\"text-align: right;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 1</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_432 = {
+       .pdu = setup_idle_mode_text_432,
+       .pdu_len = sizeof(setup_idle_mode_text_432),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_441 = {
+       .pdu = setup_idle_mode_text_441,
+       .pdu_len = sizeof(setup_idle_mode_text_441),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "big;color: #347235;background-color: #FFFF00;\">"
+               "Idle Mode Text 1</span></div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_442 = {
+       .pdu = setup_idle_mode_text_442,
+       .pdu_len = sizeof(setup_idle_mode_text_442),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 2</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_443 = {
+       .pdu = setup_idle_mode_text_443,
+       .pdu_len = sizeof(setup_idle_mode_text_443),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 3"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_451 = {
+       .pdu = setup_idle_mode_text_451,
+       .pdu_len = sizeof(setup_idle_mode_text_451),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-size: "
+               "small;color: #347235;background-color: #FFFF00;\">"
+               "Idle Mode Text 1</span></div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_452 = {
+       .pdu = setup_idle_mode_text_452,
+       .pdu_len = sizeof(setup_idle_mode_text_452),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 2</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_453 = {
+       .pdu = setup_idle_mode_text_453,
+       .pdu_len = sizeof(setup_idle_mode_text_453),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 3"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_461 = {
+       .pdu = setup_idle_mode_text_461,
+       .pdu_len = sizeof(setup_idle_mode_text_461),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x10, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-weight: "
+               "bold;color: #347235;background-color: #FFFF00;\">"
+               "Idle Mode Text 1</span></div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_462 = {
+       .pdu = setup_idle_mode_text_462,
+       .pdu_len = sizeof(setup_idle_mode_text_462),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 2</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_463 = {
+       .pdu = setup_idle_mode_text_463,
+       .pdu_len = sizeof(setup_idle_mode_text_463),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 3"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_471 = {
+       .pdu = setup_idle_mode_text_471,
+       .pdu_len = sizeof(setup_idle_mode_text_471),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x20, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"font-style: "
+               "italic;color: #347235;background-color: #FFFF00;\">"
+               "Idle Mode Text 1</span></div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_472 = {
+       .pdu = setup_idle_mode_text_472,
+       .pdu_len = sizeof(setup_idle_mode_text_472),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 2</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_473 = {
+       .pdu = setup_idle_mode_text_473,
+       .pdu_len = sizeof(setup_idle_mode_text_473),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 3"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_481 = {
+       .pdu = setup_idle_mode_text_481,
+       .pdu_len = sizeof(setup_idle_mode_text_481),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: underline;color: #347235;"
+               "background-color: #FFFF00;\">Idle Mode Text 1</span></div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_482 = {
+       .pdu = setup_idle_mode_text_482,
+       .pdu_len = sizeof(setup_idle_mode_text_482),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 2</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_483 = {
+       .pdu = setup_idle_mode_text_483,
+       .pdu_len = sizeof(setup_idle_mode_text_483),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 3"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_491 = {
+       .pdu = setup_idle_mode_text_491,
+       .pdu_len = sizeof(setup_idle_mode_text_491),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span "
+               "style=\"text-decoration: line-through;color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 1</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_492 = {
+       .pdu = setup_idle_mode_text_492,
+       .pdu_len = sizeof(setup_idle_mode_text_492),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 2</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_493 = {
+       .pdu = setup_idle_mode_text_493,
+       .pdu_len = sizeof(setup_idle_mode_text_493),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 3"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_4101 = {
+       .pdu = setup_idle_mode_text_4101,
+       .pdu_len = sizeof(setup_idle_mode_text_4101),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       },
+       .html = "<div style=\"text-align: left;\"><span style=\"color: "
+               "#347235;background-color: #FFFF00;\">Idle Mode Text 1</span>"
+               "</div>",
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_4102 = {
+       .pdu = setup_idle_mode_text_4102,
+       .pdu_len = sizeof(setup_idle_mode_text_4102),
+       .qualifier = 0x00,
+       .text = "Idle Mode Text 2"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_511 = {
+       .pdu = setup_idle_mode_text_511,
+       .pdu_len = sizeof(setup_idle_mode_text_511),
+       .qualifier = 0x00,
+       .text = "你好"
+};
+
+static struct setup_idle_mode_text_test setup_idle_mode_text_data_611 = {
+       .pdu = setup_idle_mode_text_611,
+       .pdu_len = sizeof(setup_idle_mode_text_611),
+       .qualifier = 0x00,
+       .text = "80ル0"
+};
+
+static void test_setup_idle_mode_text(gconstpointer data)
+{
+       const struct setup_idle_mode_text_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == test->status);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_text(command->setup_idle_mode_text.text, test->text);
+       check_icon_id(&command->setup_idle_mode_text.icon_id, &test->icon_id);
+       check_text_attr(&command->setup_idle_mode_text.text_attr,
+                                                       &test->text_attr);
+       check_text_attr_html(&command->setup_idle_mode_text.text_attr,
+                               command->setup_idle_mode_text.text, test->html);
+       check_frame_id(&command->setup_idle_mode_text.frame_id,
+                                                       &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct run_at_command_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       char *at_command;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       enum stk_command_parse_result status;
+};
+
+static unsigned char run_at_command_111[] = { 0xD0, 0x12, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0xA8, 0x07, 0x41, 0x54,
+                                               0x2B, 0x43, 0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_121[] = { 0xD0, 0x14, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x00, 0xA8, 0x07,
+                                               0x41, 0x54, 0x2B, 0x43, 0x47,
+                                               0x4D, 0x49 };
+
+static unsigned char run_at_command_131[] = { 0xD0, 0x22, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0E, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0xA8, 0x07, 0x41,
+                                               0x54, 0x2B, 0x43, 0x47, 0x4D,
+                                               0x49 };
+
+static unsigned char run_at_command_211[] = { 0xD0, 0x22, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0A, 0x42, 0x61,
+                                               0x73, 0x69, 0x63, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0xA8, 0x07,
+                                               0x41, 0x54, 0x2B, 0x43, 0x47,
+                                               0x4D, 0x49, 0x9E, 0x02, 0x00,
+                                               0x01 };
+
+/* The 12th byte should be 0x85, instead of 0xA8 */
+static unsigned char run_at_command_221[] = { 0xD0, 0x23, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0B, 0x43, 0x6F,
+                                               0x6C, 0x6F, 0x75, 0x72, 0x20,
+                                               0x49, 0x63, 0x6F, 0x6E, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0x9E, 0x02,
+                                               0x00, 0x02 };
+
+static unsigned char run_at_command_231[] = { 0xD0, 0x22, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0A, 0x42, 0x61,
+                                               0x73, 0x69, 0x63, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0xA8, 0x07,
+                                               0x41, 0x54, 0x2B, 0x43, 0x47,
+                                               0x4D, 0x49, 0x9E, 0x02, 0x01,
+                                               0x01 };
+
+static unsigned char run_at_command_241[] = { 0xD0, 0x23, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x0B, 0x43, 0x6F,
+                                               0x6C, 0x6F, 0x75, 0x72, 0x20,
+                                               0x49, 0x63, 0x6F, 0x6E, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0x9E, 0x02,
+                                               0x01, 0x02 };
+
+static unsigned char run_at_command_251[] = { 0xD0, 0x16, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0xA8, 0x07, 0x41, 0x54,
+                                               0x2B, 0x43, 0x47, 0x4D, 0x49,
+                                               0x9E, 0x02, 0x01, 0x01 };
+
+static unsigned char run_at_command_311[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_312[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_321[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x01, 0xB4 };
+
+static unsigned char run_at_command_322[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_331[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x02, 0xB4 };
+
+static unsigned char run_at_command_332[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_341[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x04, 0xB4 };
+
+static unsigned char run_at_command_342[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_343[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x33, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_351[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x08, 0xB4 };
+
+static unsigned char run_at_command_352[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_353[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x33, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_361[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x10, 0xB4 };
+
+static unsigned char run_at_command_362[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_363[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x33, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_371[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x20, 0xB4 };
+
+static unsigned char run_at_command_372[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_373[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x33, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_381[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x40, 0xB4 };
+
+static unsigned char run_at_command_382[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_383[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x33, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_391[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x80, 0xB4 };
+
+static unsigned char run_at_command_392[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_393[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x33, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+static unsigned char run_at_command_3101[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x31, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49, 0xD0, 0x04,
+                                               0x00, 0x10, 0x00, 0xB4 };
+
+static unsigned char run_at_command_3102[] = { 0xD0, 0x24, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x10, 0x52, 0x75,
+                                               0x6E, 0x20, 0x41, 0x54, 0x20,
+                                               0x43, 0x6F, 0x6D, 0x6D, 0x61,
+                                               0x6E, 0x64, 0x20, 0x32, 0xA8,
+                                               0x07, 0x41, 0x54, 0x2B, 0x43,
+                                               0x47, 0x4D, 0x49 };
+
+/* The 2nd byte (total size) should be 0x2D, instead of 0x21 */
+static unsigned char run_at_command_411[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x19, 0x80, 0x04,
+                                               0x17, 0x04, 0x14, 0x04, 0x20,
+                                               0x04, 0x10, 0x04, 0x12, 0x04,
+                                               0x21, 0x04, 0x22, 0x04, 0x12,
+                                               0x04, 0x23, 0x04, 0x19, 0x04,
+                                               0x22, 0x04, 0x15, 0xA8, 0x07,
+                                               0x41, 0x54, 0x2B, 0x43, 0x47,
+                                               0x4D, 0x49 };
+
+static unsigned char run_at_command_511[] = { 0xD0, 0x19, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x05, 0x80, 0x4F,
+                                               0x60, 0x59, 0x7D, 0xA8, 0x07,
+                                               0x41, 0x54, 0x2B, 0x43, 0x47,
+                                               0x4D, 0x49 };
+
+static unsigned char run_at_command_611[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01,
+                                               0x34, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x85, 0x07, 0x80, 0x00,
+                                               0x38, 0x00, 0x30, 0x30, 0xEB,
+                                               0xA8, 0x07, 0x41, 0x54, 0x2B,
+                                               0x43, 0x47, 0x4D, 0x49 };
+
+static struct run_at_command_test run_at_command_data_111 = {
+       .pdu = run_at_command_111,
+       .pdu_len = sizeof(run_at_command_111),
+       .qualifier = 0x00,
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_121 = {
+       .pdu = run_at_command_121,
+       .pdu_len = sizeof(run_at_command_121),
+       .qualifier = 0x00,
+       .alpha_id = "",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_131 = {
+       .pdu = run_at_command_131,
+       .pdu_len = sizeof(run_at_command_131),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_211 = {
+       .pdu = run_at_command_211,
+       .pdu_len = sizeof(run_at_command_211),
+       .qualifier = 0x00,
+       .alpha_id = "Basic Icon",
+       .at_command = "AT+CGMI",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct run_at_command_test run_at_command_data_221 = {
+       .pdu = run_at_command_221,
+       .pdu_len = sizeof(run_at_command_221),
+       .qualifier = 0x00,
+       .alpha_id = "Colour Icon",
+       .at_command = "AT+CGMI",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct run_at_command_test run_at_command_data_231 = {
+       .pdu = run_at_command_231,
+       .pdu_len = sizeof(run_at_command_231),
+       .qualifier = 0x00,
+       .alpha_id = "Basic Icon",
+       .at_command = "AT+CGMI",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+/* The qualifier of icon_id should be non self-explanatory */
+static struct run_at_command_test run_at_command_data_241 = {
+       .pdu = run_at_command_241,
+       .pdu_len = sizeof(run_at_command_241),
+       .qualifier = 0x00,
+       .alpha_id = "Colour Icon",
+       .at_command = "AT+CGMI",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct run_at_command_test run_at_command_data_251 = {
+       .pdu = run_at_command_251,
+       .pdu_len = sizeof(run_at_command_251),
+       .qualifier = 0x00,
+       .at_command = "AT+CGMI",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       },
+       .status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD
+};
+
+static struct run_at_command_test run_at_command_data_311 = {
+       .pdu = run_at_command_311,
+       .pdu_len = sizeof(run_at_command_311),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_312 = {
+       .pdu = run_at_command_312,
+       .pdu_len = sizeof(run_at_command_312),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_321 = {
+       .pdu = run_at_command_321,
+       .pdu_len = sizeof(run_at_command_321),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x01, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_322 = {
+       .pdu = run_at_command_322,
+       .pdu_len = sizeof(run_at_command_322),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_331 = {
+       .pdu = run_at_command_331,
+       .pdu_len = sizeof(run_at_command_331),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x02, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_332 = {
+       .pdu = run_at_command_332,
+       .pdu_len = sizeof(run_at_command_332),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_341 = {
+       .pdu = run_at_command_341,
+       .pdu_len = sizeof(run_at_command_341),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x04, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_342 = {
+       .pdu = run_at_command_342,
+       .pdu_len = sizeof(run_at_command_342),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_343 = {
+       .pdu = run_at_command_343,
+       .pdu_len = sizeof(run_at_command_343),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 3",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_351 = {
+       .pdu = run_at_command_351,
+       .pdu_len = sizeof(run_at_command_351),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x08, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_352 = {
+       .pdu = run_at_command_352,
+       .pdu_len = sizeof(run_at_command_352),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_353 = {
+       .pdu = run_at_command_353,
+       .pdu_len = sizeof(run_at_command_353),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 3",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_361 = {
+       .pdu = run_at_command_361,
+       .pdu_len = sizeof(run_at_command_361),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x10, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_362 = {
+       .pdu = run_at_command_362,
+       .pdu_len = sizeof(run_at_command_362),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_363 = {
+       .pdu = run_at_command_363,
+       .pdu_len = sizeof(run_at_command_363),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 3",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_371 = {
+       .pdu = run_at_command_371,
+       .pdu_len = sizeof(run_at_command_371),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x20, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_372 = {
+       .pdu = run_at_command_372,
+       .pdu_len = sizeof(run_at_command_372),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_373 = {
+       .pdu = run_at_command_373,
+       .pdu_len = sizeof(run_at_command_373),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 3",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_381 = {
+       .pdu = run_at_command_381,
+       .pdu_len = sizeof(run_at_command_381),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x40, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_382 = {
+       .pdu = run_at_command_382,
+       .pdu_len = sizeof(run_at_command_382),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_383 = {
+       .pdu = run_at_command_383,
+       .pdu_len = sizeof(run_at_command_383),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 3",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_391 = {
+       .pdu = run_at_command_391,
+       .pdu_len = sizeof(run_at_command_391),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x80, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_392 = {
+       .pdu = run_at_command_392,
+       .pdu_len = sizeof(run_at_command_392),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_393 = {
+       .pdu = run_at_command_393,
+       .pdu_len = sizeof(run_at_command_393),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 3",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_3101 = {
+       .pdu = run_at_command_3101,
+       .pdu_len = sizeof(run_at_command_3101),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 1",
+       .at_command = "AT+CGMI",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x10, 0x00, 0xB4 }
+       }
+};
+
+static struct run_at_command_test run_at_command_data_3102 = {
+       .pdu = run_at_command_3102,
+       .pdu_len = sizeof(run_at_command_3102),
+       .qualifier = 0x00,
+       .alpha_id = "Run AT Command 2",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_411 = {
+       .pdu = run_at_command_411,
+       .pdu_len = sizeof(run_at_command_411),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_511 = {
+       .pdu = run_at_command_511,
+       .pdu_len = sizeof(run_at_command_511),
+       .qualifier = 0x00,
+       .alpha_id = "你好",
+       .at_command = "AT+CGMI"
+};
+
+static struct run_at_command_test run_at_command_data_611 = {
+       .pdu = run_at_command_611,
+       .pdu_len = sizeof(run_at_command_611),
+       .qualifier = 0x00,
+       .alpha_id = "80ル",
+       .at_command = "AT+CGMI"
+};
+
+static void test_run_at_command(gconstpointer data)
+{
+       const struct run_at_command_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == test->status);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_RUN_AT_COMMAND);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_alpha_id(command->run_at_command.alpha_id, test->alpha_id);
+       check_at_command(command->run_at_command.at_command, test->at_command);
+       check_icon_id(&command->run_at_command.icon_id, &test->icon_id);
+       check_text_attr(&command->run_at_command.text_attr, &test->text_attr);
+       check_frame_id(&command->run_at_command.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct send_dtmf_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       char *dtmf;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char send_dtmf_111[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0xAC, 0x02, 0xC1, 0xF2 };
+
+static unsigned char send_dtmf_121[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x09, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0xAC, 0x05, 0x21, 0x43,
+                                               0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_131[] = { 0xD0, 0x13, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x00, 0xAC, 0x06, 0xC1,
+                                               0xCC, 0xCC, 0xCC, 0xCC, 0x2C };
+
+static unsigned char send_dtmf_211[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0A, 0x42, 0x61, 0x73,
+                                               0x69, 0x63, 0x20, 0x49, 0x63,
+                                               0x6F, 0x6E, 0xAC, 0x02, 0xC1,
+                                               0xF2, 0x9E, 0x02, 0x00, 0x01 };
+
+static unsigned char send_dtmf_221[] = { 0xD0, 0x1E, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x43, 0x6F, 0x6C,
+                                               0x6F, 0x75, 0x72, 0x20, 0x49,
+                                               0x63, 0x6F, 0x6E, 0xAC, 0x02,
+                                               0xC1, 0xF2, 0x9E, 0x02, 0x00,
+                                               0x02 };
+
+static unsigned char send_dtmf_231[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x09, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0xAC, 0x02, 0xC1, 0xF2,
+                                               0x9E, 0x02, 0x01, 0x01 };
+
+static unsigned char send_dtmf_311[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x19, 0x80, 0x04, 0x17,
+                                               0x04, 0x14, 0x04, 0x20, 0x04,
+                                               0x10, 0x04, 0x12, 0x04, 0x21,
+                                               0x04, 0x22, 0x04, 0x12, 0x04,
+                                               0x23, 0x04, 0x19, 0x04, 0x22,
+                                               0x04, 0x15, 0xAC, 0x02, 0xC1,
+                                               0xF2 };
+
+static unsigned char send_dtmf_411[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_412[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_421[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x01,
+                                               0xB4 };
+
+static unsigned char send_dtmf_422[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_431[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0xB0, 0x02,
+                                               0xB4 };
+
+static unsigned char send_dtmf_432[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_441[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x04,
+                                               0xB4 };
+
+static unsigned char send_dtmf_442[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_443[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x33, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_451[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x08,
+                                               0xB4 };
+
+static unsigned char send_dtmf_452[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_453[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x33, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+/* The last 0x00 in spec should be removed. */
+static unsigned char send_dtmf_461[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x10,
+                                               0xB4 };
+
+static unsigned char send_dtmf_462[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_463[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x33, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_471[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x20,
+                                               0xB4 };
+
+static unsigned char send_dtmf_472[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_473[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x33, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_481[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x40,
+                                               0xB4 };
+
+static unsigned char send_dtmf_482[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_483[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x33, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+/* The second to the last should be 0x80 */
+static unsigned char send_dtmf_491[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x80,
+                                               0xB4 };
+
+static unsigned char send_dtmf_492[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_493[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x33, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_4101[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x31, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09,
+                                               0xD0, 0x04, 0x00, 0x0B, 0x00,
+                                               0xB4 };
+
+static unsigned char send_dtmf_4102[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x0B, 0x53, 0x65, 0x6E,
+                                               0x64, 0x20, 0x44, 0x54, 0x4D,
+                                               0x46, 0x20, 0x32, 0xAC, 0x05,
+                                               0x21, 0x43, 0x65, 0x87, 0x09 };
+
+static unsigned char send_dtmf_511[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x05, 0x80, 0x4F, 0x60,
+                                               0x59, 0x7D, 0xAC, 0x02, 0xC1,
+                                               0xF2 };
+
+static unsigned char send_dtmf_611[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x14,
+                                               0x00, 0x82, 0x02, 0x81, 0x83,
+                                               0x85, 0x03, 0x80, 0x30, 0xEB,
+                                               0xAC, 0x02, 0xC1, 0xF2 };
+
+static struct send_dtmf_test send_dtmf_data_111 = {
+       .pdu = send_dtmf_111,
+       .pdu_len = sizeof(send_dtmf_111),
+       .qualifier = 0x00,
+       .dtmf = "1c2"
+};
+
+static struct send_dtmf_test send_dtmf_data_121 = {
+       .pdu = send_dtmf_121,
+       .pdu_len = sizeof(send_dtmf_121),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_131 = {
+       .pdu = send_dtmf_131,
+       .pdu_len = sizeof(send_dtmf_131),
+       .qualifier = 0x00,
+       .alpha_id = "",
+       .dtmf = "1cccccccccc2"
+};
+
+static struct send_dtmf_test send_dtmf_data_211 = {
+       .pdu = send_dtmf_211,
+       .pdu_len = sizeof(send_dtmf_211),
+       .qualifier = 0x00,
+       .alpha_id = "Basic Icon",
+       .dtmf = "1c2",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_221 = {
+       .pdu = send_dtmf_221,
+       .pdu_len = sizeof(send_dtmf_221),
+       .qualifier = 0x00,
+       .alpha_id = "Colour Icon",
+       .dtmf = "1c2",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x02
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_231 = {
+       .pdu = send_dtmf_231,
+       .pdu_len = sizeof(send_dtmf_231),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF",
+       .dtmf = "1c2",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_311 = {
+       .pdu = send_dtmf_311,
+       .pdu_len = sizeof(send_dtmf_311),
+       .qualifier = 0x00,
+       .alpha_id = "ЗДРАВСТВУЙТЕ",
+       .dtmf = "1c2"
+};
+
+static struct send_dtmf_test send_dtmf_data_411 = {
+       .pdu = send_dtmf_411,
+       .pdu_len = sizeof(send_dtmf_411),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_412 = {
+       .pdu = send_dtmf_412,
+       .pdu_len = sizeof(send_dtmf_412),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_421 = {
+       .pdu = send_dtmf_421,
+       .pdu_len = sizeof(send_dtmf_421),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x01, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_422 = {
+       .pdu = send_dtmf_422,
+       .pdu_len = sizeof(send_dtmf_422),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_431 = {
+       .pdu = send_dtmf_431,
+       .pdu_len = sizeof(send_dtmf_431),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0xB0, 0x02, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_432 = {
+       .pdu = send_dtmf_432,
+       .pdu_len = sizeof(send_dtmf_432),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_441 = {
+       .pdu = send_dtmf_441,
+       .pdu_len = sizeof(send_dtmf_441),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x04, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_442 = {
+       .pdu = send_dtmf_442,
+       .pdu_len = sizeof(send_dtmf_442),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_443 = {
+       .pdu = send_dtmf_443,
+       .pdu_len = sizeof(send_dtmf_443),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 3",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_451 = {
+       .pdu = send_dtmf_451,
+       .pdu_len = sizeof(send_dtmf_451),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x08, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_452 = {
+       .pdu = send_dtmf_452,
+       .pdu_len = sizeof(send_dtmf_452),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_453 = {
+       .pdu = send_dtmf_453,
+       .pdu_len = sizeof(send_dtmf_453),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 3",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_461 = {
+       .pdu = send_dtmf_461,
+       .pdu_len = sizeof(send_dtmf_461),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x10, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_462 = {
+       .pdu = send_dtmf_462,
+       .pdu_len = sizeof(send_dtmf_462),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_463 = {
+       .pdu = send_dtmf_463,
+       .pdu_len = sizeof(send_dtmf_463),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 3",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_471 = {
+       .pdu = send_dtmf_471,
+       .pdu_len = sizeof(send_dtmf_471),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x20, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_472 = {
+       .pdu = send_dtmf_472,
+       .pdu_len = sizeof(send_dtmf_472),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_473 = {
+       .pdu = send_dtmf_473,
+       .pdu_len = sizeof(send_dtmf_473),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 3",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_481 = {
+       .pdu = send_dtmf_481,
+       .pdu_len = sizeof(send_dtmf_481),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x40, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_482 = {
+       .pdu = send_dtmf_482,
+       .pdu_len = sizeof(send_dtmf_482),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_483 = {
+       .pdu = send_dtmf_483,
+       .pdu_len = sizeof(send_dtmf_483),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 3",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_491 = {
+       .pdu = send_dtmf_491,
+       .pdu_len = sizeof(send_dtmf_491),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x80, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_492 = {
+       .pdu = send_dtmf_492,
+       .pdu_len = sizeof(send_dtmf_492),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_493 = {
+       .pdu = send_dtmf_493,
+       .pdu_len = sizeof(send_dtmf_493),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 3",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_4101 = {
+       .pdu = send_dtmf_4101,
+       .pdu_len = sizeof(send_dtmf_4101),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 1",
+       .dtmf = "1234567890",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       }
+};
+
+static struct send_dtmf_test send_dtmf_data_4102 = {
+       .pdu = send_dtmf_4102,
+       .pdu_len = sizeof(send_dtmf_4102),
+       .qualifier = 0x00,
+       .alpha_id = "Send DTMF 2",
+       .dtmf = "1234567890"
+};
+
+static struct send_dtmf_test send_dtmf_data_511 = {
+       .pdu = send_dtmf_511,
+       .pdu_len = sizeof(send_dtmf_511),
+       .qualifier = 0x00,
+       .alpha_id = "你好",
+       .dtmf = "1c2"
+};
+
+static struct send_dtmf_test send_dtmf_data_611 = {
+       .pdu = send_dtmf_611,
+       .pdu_len = sizeof(send_dtmf_611),
+       .qualifier = 0x00,
+       .alpha_id = "ル",
+       .dtmf = "1c2"
+};
+
+static void test_send_dtmf(gconstpointer data)
+{
+       const struct send_dtmf_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SEND_DTMF);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK);
+
+       check_alpha_id(command->send_dtmf.alpha_id, test->alpha_id);
+       check_dtmf_string(command->send_dtmf.dtmf, test->dtmf);
+       check_icon_id(&command->send_dtmf.icon_id, &test->icon_id);
+       check_text_attr(&command->send_dtmf.text_attr, &test->text_attr);
+       check_frame_id(&command->send_dtmf.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct language_notification_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char language[3];
+};
+
+static unsigned char language_notification_111[] = { 0xD0, 0x0D, 0x81, 0x03,
+                                               0x01, 0x35, 0x01, 0x82, 0x02,
+                                               0x81, 0x82, 0xAD, 0x02, 0x73,
+                                               0x65 };
+
+static unsigned char language_notification_121[] = { 0xD0, 0x09, 0x81, 0x03,
+                                               0x01, 0x35, 0x00, 0x82, 0x02,
+                                               0x81, 0x82 };
+
+static struct language_notification_test language_notification_data_111 = {
+       .pdu = language_notification_111,
+       .pdu_len = sizeof(language_notification_111),
+       .qualifier = 0x01,
+       .language = "se"
+};
+
+static struct language_notification_test language_notification_data_121 = {
+       .pdu = language_notification_121,
+       .pdu_len = sizeof(language_notification_121),
+       .qualifier = 0x00
+};
+
+static void test_language_notification(gconstpointer data)
+{
+       const struct language_notification_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_language(command->language_notification.language, test->language);
+
+       stk_command_free(command);
+}
+
+struct launch_browser_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       unsigned char browser_id;
+       char *url;
+       struct stk_common_byte_array bearer;
+       struct stk_file prov_file_refs[MAX_ITEM];
+       char *text_gateway_proxy_id;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+       struct stk_common_byte_array network_name;
+       char *text_usr;
+       char *text_passwd;
+};
+
+static unsigned char launch_browser_111[] = { 0xD0, 0x18, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0B,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C };
+
+static unsigned char launch_browser_121[] = { 0xD0, 0x1F, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x12, 0x68, 0x74,
+                                               0x74, 0x70, 0x3A, 0x2F, 0x2F,
+                                               0x78, 0x78, 0x78, 0x2E, 0x79,
+                                               0x79, 0x79, 0x2E, 0x7A, 0x7A,
+                                               0x7A, 0x05, 0x00 };
+
+static unsigned char launch_browser_131[] = { 0xD0, 0x0E, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x30, 0x01, 0x00, 0x31,
+                                               0x00 };
+
+static unsigned char launch_browser_141[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x32, 0x01,
+                                               0x03, 0x0D, 0x10, 0x04, 0x61,
+                                               0x62, 0x63, 0x2E, 0x64, 0x65,
+                                               0x66, 0x2E, 0x67, 0x68, 0x69,
+                                               0x2E, 0x6A, 0x6B, 0x6C };
+
+static unsigned char launch_browser_211[] = { 0xD0, 0x18, 0x81, 0x03, 0x01,
+                                               0x15, 0x02, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0B,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C };
+
+static unsigned char launch_browser_221[] = { 0xD0, 0x18, 0x81, 0x03, 0x01,
+                                               0x15, 0x03, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0B,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C };
+
+static unsigned char launch_browser_231[] = { 0xD0, 0x0B, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00 };
+
+static unsigned char launch_browser_311[] = { 0xD0, 0x26, 0x81, 0x03, 0x01,
+                                               0x15, 0x02, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x19,
+                                               0x80, 0x04, 0x17, 0x04, 0x14,
+                                               0x04, 0x20, 0x04, 0x10, 0x04,
+                                               0x12, 0x04, 0x21, 0x04, 0x22,
+                                               0x04, 0x12, 0x04, 0x23, 0x04,
+                                               0x19, 0x04, 0x22, 0x04, 0x15 };
+
+static unsigned char launch_browser_411[] = { 0xD0, 0x21, 0x81, 0x03, 0x01,
+                                               0x15, 0x02, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x10,
+                                               0x4E, 0x6F, 0x74, 0x20, 0x73,
+                                               0x65, 0x6C, 0x66, 0x20, 0x65,
+                                               0x78, 0x70, 0x6C, 0x61, 0x6E,
+                                               0x2E, 0x1E, 0x02, 0x01, 0x01 };
+
+static unsigned char launch_browser_421[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01,
+                                               0x15, 0x02, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0C,
+                                               0x53, 0x65, 0x6C, 0x66, 0x20,
+                                               0x65, 0x78, 0x70, 0x6C, 0x61,
+                                               0x6E, 0x2E, 0x1E, 0x02, 0x00,
+                                               0x01 };
+
+static unsigned char launch_browser_511[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_512[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32 };
+
+static unsigned char launch_browser_521[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x01, 0xB4 };
+
+static unsigned char launch_browser_522[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32 };
+
+static unsigned char launch_browser_531[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x02, 0xB4 };
+
+static unsigned char launch_browser_532[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32 };
+
+static unsigned char launch_browser_541[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x04, 0xB4 };
+
+static unsigned char launch_browser_542[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_543[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x33 };
+
+static unsigned char launch_browser_551[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x08, 0xB4 };
+
+static unsigned char launch_browser_552[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_553[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x33 };
+
+static unsigned char launch_browser_561[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x10, 0xB4 };
+
+static unsigned char launch_browser_562[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_563[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x33 };
+
+static unsigned char launch_browser_571[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x20, 0xB4 };
+
+static unsigned char launch_browser_572[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_573[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x33 };
+
+static unsigned char launch_browser_581[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x40, 0xB4 };
+
+static unsigned char launch_browser_582[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_583[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x33 };
+
+static unsigned char launch_browser_591[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x80, 0xB4 };
+
+static unsigned char launch_browser_592[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_593[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x33 };
+
+static unsigned char launch_browser_5101[] = { 0xD0, 0x20, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x31, 0xD0, 0x04,
+                                               0x00, 0x0D, 0x00, 0xB4 };
+
+static unsigned char launch_browser_5102[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01,
+                                               0x15, 0x00, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x0D,
+                                               0x44, 0x65, 0x66, 0x61, 0x75,
+                                               0x6C, 0x74, 0x20, 0x55, 0x52,
+                                               0x4C, 0x20, 0x32 };
+
+static unsigned char launch_browser_611[] = { 0xD0, 0x12, 0x81, 0x03, 0x01,
+                                               0x15, 0x02, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x05,
+                                               0x80, 0x4F, 0x60, 0x59, 0x7D };
+
+static unsigned char launch_browser_711[] = { 0xD0, 0x10, 0x81, 0x03, 0x01,
+                                               0x15, 0x02, 0x82, 0x02, 0x81,
+                                               0x82, 0x31, 0x00, 0x05, 0x03,
+                                               0x80, 0x30, 0xEB };
+
+static struct launch_browser_test launch_browser_data_111 = {
+       .pdu = launch_browser_111,
+       .pdu_len = sizeof(launch_browser_111),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL"
+};
+
+static struct launch_browser_test launch_browser_data_121 = {
+       .pdu = launch_browser_121,
+       .pdu_len = sizeof(launch_browser_121),
+       .qualifier = 0x00,
+       .alpha_id = "",
+       .url = "http://xxx.yyy.zzz"
+};
+
+static struct launch_browser_test launch_browser_data_131 = {
+       .pdu = launch_browser_131,
+       .pdu_len = sizeof(launch_browser_131),
+       .qualifier = 0x00
+};
+
+static struct launch_browser_test launch_browser_data_141 = {
+       .pdu = launch_browser_141,
+       .pdu_len = sizeof(launch_browser_141),
+       .qualifier = 0x00,
+       .bearer = {
+               .len = 1,
+               .array = (unsigned char *) "\x03"
+       },
+       .text_gateway_proxy_id = "abc.def.ghi.jkl"
+};
+
+static struct launch_browser_test launch_browser_data_211 = {
+       .pdu = launch_browser_211,
+       .pdu_len = sizeof(launch_browser_211),
+       .qualifier = 0x02,
+       .alpha_id = "Default URL"
+};
+
+static struct launch_browser_test launch_browser_data_221 = {
+       .pdu = launch_browser_221,
+       .pdu_len = sizeof(launch_browser_221),
+       .qualifier = 0x03,
+       .alpha_id = "Default URL"
+};
+
+static struct launch_browser_test launch_browser_data_231 = {
+       .pdu = launch_browser_231,
+       .pdu_len = sizeof(launch_browser_231),
+       .qualifier = 0x00
+};
+
+static struct launch_browser_test launch_browser_data_311 = {
+       .pdu = launch_browser_311,
+       .pdu_len = sizeof(launch_browser_311),
+       .qualifier = 0x02,
+       .alpha_id = "ЗДРАВСТВУЙТЕ"
+};
+
+static struct launch_browser_test launch_browser_data_411 = {
+       .pdu = launch_browser_411,
+       .pdu_len = sizeof(launch_browser_411),
+       .qualifier = 0x02,
+       .alpha_id = "Not self explan.",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct launch_browser_test launch_browser_data_421 = {
+       .pdu = launch_browser_421,
+       .pdu_len = sizeof(launch_browser_421),
+       .qualifier = 0x02,
+       .alpha_id = "Self explan.",
+       .icon_id = {
+               .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY,
+               .id = 0x01
+       }
+};
+
+static struct launch_browser_test launch_browser_data_511 = {
+       .pdu = launch_browser_511,
+       .pdu_len = sizeof(launch_browser_511),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_512 = {
+       .pdu = launch_browser_512,
+       .pdu_len = sizeof(launch_browser_512),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2"
+};
+
+static struct launch_browser_test launch_browser_data_521 = {
+       .pdu = launch_browser_521,
+       .pdu_len = sizeof(launch_browser_521),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x01, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_522 = {
+       .pdu = launch_browser_522,
+       .pdu_len = sizeof(launch_browser_522),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2"
+};
+
+static struct launch_browser_test launch_browser_data_531 = {
+       .pdu = launch_browser_531,
+       .pdu_len = sizeof(launch_browser_531),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x02, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_532 = {
+       .pdu = launch_browser_532,
+       .pdu_len = sizeof(launch_browser_532),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2"
+};
+
+static struct launch_browser_test launch_browser_data_541 = {
+       .pdu = launch_browser_541,
+       .pdu_len = sizeof(launch_browser_541),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x04, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_542 = {
+       .pdu = launch_browser_542,
+       .pdu_len = sizeof(launch_browser_542),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_543 = {
+       .pdu = launch_browser_543,
+       .pdu_len = sizeof(launch_browser_543),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 3"
+};
+
+static struct launch_browser_test launch_browser_data_551 = {
+       .pdu = launch_browser_551,
+       .pdu_len = sizeof(launch_browser_551),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x08, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_552 = {
+       .pdu = launch_browser_552,
+       .pdu_len = sizeof(launch_browser_552),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_553 = {
+       .pdu = launch_browser_553,
+       .pdu_len = sizeof(launch_browser_553),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 3"
+};
+
+static struct launch_browser_test launch_browser_data_561 = {
+       .pdu = launch_browser_561,
+       .pdu_len = sizeof(launch_browser_561),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x10, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_562 = {
+       .pdu = launch_browser_562,
+       .pdu_len = sizeof(launch_browser_562),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_563 = {
+       .pdu = launch_browser_563,
+       .pdu_len = sizeof(launch_browser_563),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 3"
+};
+
+static struct launch_browser_test launch_browser_data_571 = {
+       .pdu = launch_browser_571,
+       .pdu_len = sizeof(launch_browser_571),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x20, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_572 = {
+       .pdu = launch_browser_572,
+       .pdu_len = sizeof(launch_browser_572),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_573 = {
+       .pdu = launch_browser_573,
+       .pdu_len = sizeof(launch_browser_573),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 3"
+};
+
+static struct launch_browser_test launch_browser_data_581 = {
+       .pdu = launch_browser_581,
+       .pdu_len = sizeof(launch_browser_581),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x40, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_582 = {
+       .pdu = launch_browser_582,
+       .pdu_len = sizeof(launch_browser_582),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_583 = {
+       .pdu = launch_browser_583,
+       .pdu_len = sizeof(launch_browser_583),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 3"
+};
+
+static struct launch_browser_test launch_browser_data_591 = {
+       .pdu = launch_browser_591,
+       .pdu_len = sizeof(launch_browser_591),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x80, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_592 = {
+       .pdu = launch_browser_592,
+       .pdu_len = sizeof(launch_browser_592),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_593 = {
+       .pdu = launch_browser_593,
+       .pdu_len = sizeof(launch_browser_593),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 3"
+};
+
+static struct launch_browser_test launch_browser_data_5101 = {
+       .pdu = launch_browser_5101,
+       .pdu_len = sizeof(launch_browser_5101),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0D, 0x00, 0xB4 }
+       }
+};
+
+static struct launch_browser_test launch_browser_data_5102 = {
+       .pdu = launch_browser_5102,
+       .pdu_len = sizeof(launch_browser_5102),
+       .qualifier = 0x00,
+       .alpha_id = "Default URL 2"
+};
+
+static struct launch_browser_test launch_browser_data_611 = {
+       .pdu = launch_browser_611,
+       .pdu_len = sizeof(launch_browser_611),
+       .qualifier = 0x02,
+       .alpha_id = "你好"
+};
+
+static struct launch_browser_test launch_browser_data_711 = {
+       .pdu = launch_browser_711,
+       .pdu_len = sizeof(launch_browser_711),
+       .qualifier = 0x02,
+       .alpha_id = "ル"
+};
+
+static void test_launch_browser(gconstpointer data)
+{
+       const struct launch_browser_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_LAUNCH_BROWSER);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_browser_id(command->launch_browser.browser_id, test->browser_id);
+       check_url(command->launch_browser.url, test->url);
+       check_bearer(&command->launch_browser.bearer, &test->bearer);
+       check_provisioning_file_references(
+               command->launch_browser.prov_file_refs, test->prov_file_refs);
+       check_text(command->launch_browser.text_gateway_proxy_id,
+                                               test->text_gateway_proxy_id);
+       check_alpha_id(command->launch_browser.alpha_id, test->alpha_id);
+       check_icon_id(&command->launch_browser.icon_id, &test->icon_id);
+       check_text_attr(&command->launch_browser.text_attr, &test->text_attr);
+       check_frame_id(&command->launch_browser.frame_id, &test->frame_id);
+       check_text(command->launch_browser.text_usr, test->text_usr);
+       check_text(command->launch_browser.text_passwd, test->text_passwd);
+
+       stk_command_free(command);
+}
+
+struct open_channel_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_bearer_description bearer_desc;
+       unsigned short buf_size;
+       char *apn;
+       struct stk_other_address local_addr;
+       char *text_usr;
+       char *text_passwd;
+       struct stk_uicc_te_interface uti;
+       struct stk_other_address data_dest_addr;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char open_channel_211[] = { 0xD0, 0x36, 0x81, 0x03, 0x01, 0x40,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x35, 0x07, 0x02, 0x03, 0x04,
+                                               0x03, 0x04, 0x1F, 0x02, 0x39,
+                                               0x02, 0x05, 0x78, 0x0D, 0x08,
+                                               0xF4, 0x55, 0x73, 0x65, 0x72,
+                                               0x4C, 0x6F, 0x67, 0x0D, 0x08,
+                                               0xF4, 0x55, 0x73, 0x65, 0x72,
+                                               0x50, 0x77, 0x64, 0x3C, 0x03,
+                                               0x01, 0xAD, 0x9C, 0x3E, 0x05,
+                                               0x21, 0x01, 0x01, 0x01, 0x01 };
+
+static unsigned char open_channel_221[] = { 0xD0, 0x42, 0x81, 0x03, 0x01, 0x40,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x35, 0x07, 0x02, 0x03, 0x04,
+                                               0x03, 0x04, 0x1F, 0x02, 0x39,
+                                               0x02, 0x05, 0x78, 0x47, 0x0A,
+                                               0x06, 0x54, 0x65, 0x73, 0x74,
+                                               0x47, 0x70, 0x02, 0x72, 0x73,
+                                               0x0D, 0x08, 0xF4, 0x55, 0x73,
+                                               0x65, 0x72, 0x4C, 0x6F, 0x67,
+                                               0x0D, 0x08, 0xF4, 0x55, 0x73,
+                                               0x65, 0x72, 0x50, 0x77, 0x64,
+                                               0x3C, 0x03, 0x01, 0xAD, 0x9C,
+                                               0x3E, 0x05, 0x21, 0x01, 0x01,
+                                               0x01, 0x01 };
+
+static unsigned char open_channel_231[] = { 0xD0, 0x4B, 0x81, 0x03, 0x01, 0x40,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x05, 0x07, 0x4F, 0x70, 0x65,
+                                               0x6E, 0x20, 0x49, 0x44, 0x35,
+                                               0x07, 0x02, 0x03, 0x04, 0x03,
+                                               0x04, 0x1F, 0x02, 0x39, 0x02,
+                                               0x05, 0x78, 0x47, 0x0A, 0x06,
+                                               0x54, 0x65, 0x73, 0x74, 0x47,
+                                               0x70, 0x02, 0x72, 0x73, 0x0D,
+                                               0x08, 0xF4, 0x55, 0x73, 0x65,
+                                               0x72, 0x4C, 0x6F, 0x67, 0x0D,
+                                               0x08, 0xF4, 0x55, 0x73, 0x65,
+                                               0x72, 0x50, 0x77, 0x64, 0x3C,
+                                               0x03, 0x01, 0xAD, 0x9C, 0x3E,
+                                               0x05, 0x21, 0x01, 0x01, 0x01,
+                                               0x01 };
+
+static unsigned char open_channel_241[] = { 0xD0, 0x44, 0x81, 0x03, 0x01, 0x40,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x05, 0x00, 0x35, 0x07, 0x02,
+                                               0x03, 0x04, 0x03, 0x04, 0x1F,
+                                               0x02, 0x39, 0x02, 0x05, 0x78,
+                                               0x47, 0x0A, 0x06, 0x54, 0x65,
+                                               0x73, 0x74, 0x47, 0x70, 0x02,
+                                               0x72, 0x73, 0x0D, 0x08, 0xF4,
+                                               0x55, 0x73, 0x65, 0x72, 0x4C,
+                                               0x6F, 0x67, 0x0D, 0x08, 0xF4,
+                                               0x55, 0x73, 0x65, 0x72, 0x50,
+                                               0x77, 0x64, 0x3C, 0x03, 0x01,
+                                               0xAD, 0x9C, 0x3E, 0x05, 0x21,
+                                               0x01, 0x01, 0x01, 0x01 };
+
+static unsigned char open_channel_511[] = { 0xD0, 0x53, 0x81, 0x03, 0x01, 0x40,
+                                               0x01, 0x82, 0x02, 0x81, 0x82,
+                                               0x05, 0x09, 0x4F, 0x70, 0x65,
+                                               0x6E, 0x20, 0x49, 0x44, 0x20,
+                                               0x31, 0x35, 0x07, 0x02, 0x03,
+                                               0x04, 0x03, 0x04, 0x1F, 0x02,
+                                               0x39, 0x02, 0x05, 0x78, 0x47,
+                                               0x0A, 0x06, 0x54, 0x65, 0x73,
+                                               0x74, 0x47, 0x70, 0x02, 0x72,
+                                               0x73, 0x0D, 0x08, 0xF4, 0x55,
+                                               0x73, 0x65, 0x72, 0x4C, 0x6F,
+                                               0x67, 0x0D, 0x08, 0xF4, 0x55,
+                                               0x73, 0x65, 0x72, 0x50, 0x77,
+                                               0x64, 0x3C, 0x03, 0x01, 0xAD,
+                                               0x9C, 0x3E, 0x05, 0x21, 0x01,
+                                               0x01, 0x01, 0x01, 0xD0, 0x04,
+                                               0x00, 0x09, 0x00, 0xB4 };
+
+static struct open_channel_test open_channel_data_211 = {
+       /*
+        * OPEN CHANNEL, immediate link establishment, GPRS, no local address
+        * no alpha identifier, no network access name
+        */
+       .pdu = open_channel_211,
+       .pdu_len = sizeof(open_channel_211),
+       .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE,
+       .bearer_desc = {
+                       .type = STK_BEARER_TYPE_GPRS_UTRAN,
+                       .gprs = {
+                               .precedence = 3,
+                               .delay = 4,
+                               .reliability = 3,
+                               .peak = 4,
+                               .mean = 31,
+                               .pdp_type = 2,
+                       },
+       },
+       .buf_size = 1400,
+       .text_usr = "UserLog",
+       .text_passwd = "UserPwd",
+       .uti = {
+               .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE,
+               .port = 44444,
+       },
+       .data_dest_addr = {
+                       .type = STK_ADDRESS_IPV4,
+                       .addr = {
+                               .ipv4 = 0x01010101,
+                       },
+       },
+};
+
+static struct open_channel_test open_channel_data_221 = {
+       /*
+        * OPEN CHANNEL, immediate link establishment GPRS,
+        * no alpha identifier, with network access name
+        */
+       .pdu = open_channel_221,
+       .pdu_len = sizeof(open_channel_221),
+       .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE,
+       .bearer_desc = {
+                       .type = STK_BEARER_TYPE_GPRS_UTRAN,
+                       .gprs = {
+                               .precedence = 3,
+                               .delay = 4,
+                               .reliability = 3,
+                               .peak = 4,
+                               .mean = 31,
+                               .pdp_type = 2,
+                       },
+       },
+       .buf_size = 1400,
+       .apn = "TestGp.rs",
+       .text_usr = "UserLog",
+       .text_passwd = "UserPwd",
+       .uti = {
+               .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE,
+               .port = 44444,
+       },
+       .data_dest_addr = {
+                       .type = STK_ADDRESS_IPV4,
+                       .addr = {
+                               .ipv4 = 0x01010101,
+                       },
+       },
+};
+
+static struct open_channel_test open_channel_data_231 = {
+       /*
+        * OPEN CHANNEL, immediate link establishment, GPRS
+        * with alpha identifier
+        */
+       .pdu = open_channel_231,
+       .pdu_len = sizeof(open_channel_231),
+       .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE,
+       .alpha_id = "Open ID",
+       .bearer_desc = {
+                       .type = STK_BEARER_TYPE_GPRS_UTRAN,
+                       .gprs = {
+                               .precedence = 3,
+                               .delay = 4,
+                               .reliability = 3,
+                               .peak = 4,
+                               .mean = 31,
+                               .pdp_type = 2,
+                       },
+       },
+       .buf_size = 1400,
+       .apn = "TestGp.rs",
+       .text_usr = "UserLog",
+       .text_passwd = "UserPwd",
+       .uti = {
+               .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE,
+               .port = 44444,
+       },
+       .data_dest_addr = {
+                       .type = STK_ADDRESS_IPV4,
+                       .addr = {
+                               .ipv4 = 0x01010101,
+                       },
+       },
+};
+
+static struct open_channel_test open_channel_data_241 = {
+       /*
+        * OPEN CHANNEL, immediate link establishment, GPRS,
+        * with null alpha identifier
+        */
+       .pdu = open_channel_241,
+       .pdu_len = sizeof(open_channel_241),
+       .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE,
+       .alpha_id = "",
+       .bearer_desc = {
+                       .type = STK_BEARER_TYPE_GPRS_UTRAN,
+                       .gprs = {
+                               .precedence = 3,
+                               .delay = 4,
+                               .reliability = 3,
+                               .peak = 4,
+                               .mean = 31,
+                               .pdp_type = 2,
+                       },
+       },
+       .buf_size = 1400,
+       .apn = "TestGp.rs",
+       .text_usr = "UserLog",
+       .text_passwd = "UserPwd",
+       .uti = {
+               .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE,
+               .port = 44444,
+       },
+       .data_dest_addr = {
+                       .type = STK_ADDRESS_IPV4,
+                       .addr = {
+                               .ipv4 = 0x01010101,
+                       },
+       },
+};
+
+static struct open_channel_test open_channel_data_511 = {
+       /*
+        * OPEN CHANNEL, immediate link establishment, GPRS
+        * Text Attribute – Left Alignment
+        */
+       .pdu = open_channel_511,
+       .pdu_len = sizeof(open_channel_511),
+       .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE,
+       .alpha_id = "Open ID 1",
+       .bearer_desc = {
+                       .type = STK_BEARER_TYPE_GPRS_UTRAN,
+                       .gprs = {
+                               .precedence = 3,
+                               .delay = 4,
+                               .reliability = 3,
+                               .peak = 4,
+                               .mean = 31,
+                               .pdp_type = 2,
+                       },
+       },
+       .buf_size = 1400,
+       .apn = "TestGp.rs",
+       .text_usr = "UserLog",
+       .text_passwd = "UserPwd",
+       .uti = {
+               .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE,
+               .port = 44444,
+       },
+       .data_dest_addr = {
+                       .type = STK_ADDRESS_IPV4,
+                       .addr = {
+                               .ipv4 = 0x01010101,
+                       },
+       },
+       .text_attr = {
+                       .len = 4,
+                       .attributes = { 0x00, 0x09, 0x00, 0xB4 }
+       },
+};
+
+static void test_open_channel(gconstpointer data)
+{
+       const struct open_channel_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_OPEN_CHANNEL);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       check_alpha_id(command->open_channel.alpha_id, test->alpha_id);
+       check_icon_id(&command->open_channel.icon_id, &test->icon_id);
+       check_bearer_desc(&command->open_channel.bearer_desc,
+                                       &test->bearer_desc);
+       g_assert(command->open_channel.buf_size == test->buf_size);
+       check_network_access_name(command->open_channel.apn, test->apn);
+       check_other_address(&command->open_channel.local_addr,
+                                       &test->local_addr);
+       check_text(command->open_channel.text_usr, test->text_usr);
+       check_text(command->open_channel.text_passwd, test->text_passwd);
+       check_uicc_te_interface(&command->open_channel.uti, &test->uti);
+       check_other_address(&command->open_channel.data_dest_addr,
+                                       &test->data_dest_addr);
+       check_text_attr(&command->open_channel.text_attr, &test->text_attr);
+       check_frame_id(&command->open_channel.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct close_channel_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       enum stk_device_identity_type dst;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char close_channel_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x41,
+                                               0x00, 0x82, 0x02, 0x81, 0x21 };
+
+static struct close_channel_test close_channel_data_111 = {
+       .pdu = close_channel_111,
+       .pdu_len = sizeof(close_channel_111),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1,
+};
+
+static unsigned char close_channel_211[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x41,
+                                               0x00, 0x82, 0x02, 0x81, 0x21,
+                                               0x85, 0x0A, 0x43, 0x6C, 0x6F,
+                                               0x73, 0x65, 0x20, 0x49, 0x44,
+                                               0x20, 0x31, 0xD0, 0x04, 0x00,
+                                               0x0A, 0x00, 0xB4,
+ };
+
+static struct close_channel_test close_channel_data_211 = {
+       .pdu = close_channel_211,
+       .pdu_len = sizeof(close_channel_211),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1,
+       .alpha_id = "Close ID 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0A, 0x00, 0xB4 }
+       },
+};
+
+static void test_close_channel(gconstpointer data)
+{
+       const struct close_channel_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_CLOSE_CHANNEL);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == test->dst);
+
+       check_alpha_id(command->close_channel.alpha_id, test->alpha_id);
+       check_icon_id(&command->close_channel.icon_id, &test->icon_id);
+       check_text_attr(&command->close_channel.text_attr, &test->text_attr);
+       check_frame_id(&command->close_channel.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct receive_data_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       enum stk_device_identity_type dst;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       unsigned char data_len;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char receive_data_111[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x42,
+                                               0x00, 0x82, 0x02, 0x81, 0x21,
+                                               0xB7, 0x01, 0xC8 };
+
+static struct receive_data_test receive_data_data_111 = {
+       .pdu = receive_data_111,
+       .pdu_len = sizeof(receive_data_111),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1,
+       .data_len = 200,
+};
+
+static unsigned char receive_data_211[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x42,
+                                               0x00, 0x82, 0x02, 0x81, 0x21,
+                                               0x85, 0x0E, 0x52, 0x65, 0x63,
+                                               0x65, 0x69, 0x76, 0x65, 0x20,
+                                               0x44, 0x61, 0x74, 0x61, 0x20,
+                                               0x31, 0xB7, 0x01, 0xC8, 0xD0,
+                                               0x04, 0x00, 0x0E, 0x00, 0xB4 };
+
+static struct receive_data_test receive_data_data_211 = {
+       .pdu = receive_data_211,
+       .pdu_len = sizeof(receive_data_211),
+       .qualifier = 0x00,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1,
+       .data_len = 200,
+       .alpha_id = "Receive Data 1",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0E, 0x00, 0xB4 }
+       },
+};
+
+
+static void test_receive_data(gconstpointer data)
+{
+       const struct receive_data_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_RECEIVE_DATA);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == test->dst);
+
+       check_alpha_id(command->receive_data.alpha_id, test->alpha_id);
+       check_icon_id(&command->receive_data.icon_id, &test->icon_id);
+       check_common_byte(command->receive_data.data_len, test->data_len);
+       check_text_attr(&command->receive_data.text_attr, &test->text_attr);
+       check_frame_id(&command->receive_data.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct send_data_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+       enum stk_device_identity_type dst;
+       char *alpha_id;
+       struct stk_icon_id icon_id;
+       struct stk_common_byte_array data;
+       struct stk_text_attribute text_attr;
+       struct stk_frame_id frame_id;
+};
+
+static unsigned char send_data_111[] = { 0xD0, 0x13, 0x81, 0x03, 0x01, 0x43,
+                                               0x01, 0x82, 0x02, 0x81, 0x21,
+                                               0xB6, 0x08, 0x00, 0x01, 0x02,
+                                               0x03, 0x04, 0x05, 0x06, 0x07 };
+
+static struct send_data_test send_data_data_111 = {
+       .pdu = send_data_111,
+       .pdu_len = sizeof(send_data_111),
+       .qualifier = STK_SEND_DATA_IMMEDIATELY,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1,
+       .data = {
+               .array = (unsigned char[8]) {
+                               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+               },
+               .len = 8,
+       },
+};
+
+static unsigned char send_data_121[] = {
+                               0xD0, 0x81, 0xD4, 0x81, 0x03, 0x01, 0x43, 0x00,
+                               0x82, 0x02, 0x81, 0x21, 0xB6, 0x81, 0xC8, 0x00,
+                               0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                               0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+                               0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+                               0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+                               0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+                               0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+                               0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+                               0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+                               0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+                               0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+                               0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+                               0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
+                               0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+                               0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+                               0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+                               0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80,
+                               0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+                               0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90,
+                               0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+                               0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0,
+                               0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
+                               0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
+                               0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
+                               0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
+                               0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 };
+
+static struct send_data_test send_data_data_121 = {
+       .pdu = send_data_121,
+       .pdu_len = sizeof(send_data_121),
+       .qualifier = STK_SEND_DATA_STORE_DATA,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1,
+       .data = {
+               .array = (unsigned char[200]) {
+                               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                               0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                               0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                               0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+                               0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+                               0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+                               0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+                               0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+                               0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+                               0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+                               0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+                               0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+                               0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+                               0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+                               0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+                               0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+                               0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+                               0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+                               0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+                               0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+                               0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+                               0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+                               0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+                               0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+                               0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+               },
+               .len = 200,
+       },
+};
+static unsigned char send_data_211[] = {
+                               0xD0, 0x26, 0x81, 0x03, 0x01, 0x43, 0x01, 0x82,
+                               0x02, 0x81, 0x21, 0x85, 0x0B, 0x53, 0x65, 0x6E,
+                               0x64, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x31,
+                               0xB6, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+                               0x06, 0x07, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4,
+ };
+
+static struct send_data_test send_data_data_211 = {
+       .pdu = send_data_211,
+       .pdu_len = sizeof(send_data_211),
+       .qualifier = STK_SEND_DATA_IMMEDIATELY,
+       .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1,
+       .alpha_id = "Send Data 1",
+       .data = {
+               .array = (unsigned char[8]) {
+                               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+               },
+               .len = 8,
+       },
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x0B, 0x00, 0xB4 }
+       },
+};
+
+static void test_send_data(gconstpointer data)
+{
+       const struct send_data_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_SEND_DATA);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == test->dst);
+
+       check_alpha_id(command->send_data.alpha_id, test->alpha_id);
+       check_icon_id(&command->send_data.icon_id, &test->icon_id);
+       check_channel_data(&command->send_data.data, &test->data);
+       check_text_attr(&command->send_data.text_attr, &test->text_attr);
+       check_frame_id(&command->send_data.frame_id, &test->frame_id);
+
+       stk_command_free(command);
+}
+
+struct get_channel_status_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       unsigned char qualifier;
+};
+
+static unsigned char get_channel_status_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01,
+                                                       0x44, 0x00, 0x82, 0x02,
+                                                       0x81, 0x82 };
+
+static struct get_channel_status_test get_channel_status_data_111 = {
+       .pdu = get_channel_status_111,
+       .pdu_len = sizeof(get_channel_status_111),
+       .qualifier = 0x00,
+};
+
+static void test_get_channel_status(gconstpointer data)
+{
+       const struct get_channel_status_test *test = data;
+       struct stk_command *command;
+
+       command = stk_command_new_from_pdu(test->pdu, test->pdu_len);
+
+       g_assert(command);
+       g_assert(command->status == STK_PARSE_RESULT_OK);
+
+       g_assert(command->number == 1);
+       g_assert(command->type == STK_COMMAND_TYPE_GET_CHANNEL_STATUS);
+       g_assert(command->qualifier == test->qualifier);
+
+       g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC);
+       g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL);
+
+       stk_command_free(command);
+}
+
+struct terminal_response_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       struct stk_response response;
+};
+
+static void test_terminal_response_encoding(gconstpointer data)
+{
+       const struct terminal_response_test *test = data;
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+
+       pdu = stk_pdu_from_response(&test->response, &pdu_len);
+
+       if (test->pdu)
+               g_assert(pdu);
+       else
+               g_assert(pdu == NULL);
+
+       g_assert(pdu_len == test->pdu_len);
+       g_assert(memcmp(pdu, test->pdu, pdu_len) == 0);
+}
+
+static const unsigned char display_text_response_111[] = {
+       0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test display_text_response_data_111 = {
+       .pdu = display_text_response_111,
+       .pdu_len = sizeof(display_text_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x80, /* Wait for user to clear */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char display_text_response_121[] = {
+       0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x20, 0x01,
+};
+
+static const struct terminal_response_test display_text_response_data_121 = {
+       .pdu = display_text_response_121,
+       .pdu_len = sizeof(display_text_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x80, /* Wait for user to clear */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TERMINAL_BUSY,
+                       .additional_len = 1, /* Screen is busy */
+                       .additional = (unsigned char *) "\1",
+               },
+       },
+};
+
+static const unsigned char display_text_response_131[] = {
+       0x81, 0x03, 0x01, 0x21, 0x81, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test display_text_response_data_131 = {
+       .pdu = display_text_response_131,
+       .pdu_len = sizeof(display_text_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x81, /* Wait for user to clear, High priority */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char display_text_response_151[] = {
+       0x81, 0x03, 0x01, 0x21, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test display_text_response_data_151 = {
+       .pdu = display_text_response_151,
+       .pdu_len = sizeof(display_text_response_151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char display_text_response_171[] = {
+       0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x11,
+};
+
+static const struct terminal_response_test display_text_response_data_171 = {
+       .pdu = display_text_response_171,
+       .pdu_len = sizeof(display_text_response_171),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x80, /* Wait for user to clear */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_GO_BACK,
+               },
+       },
+};
+
+static const unsigned char display_text_response_181[] = {
+       0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x10,
+};
+
+static const struct terminal_response_test display_text_response_data_181 = {
+       .pdu = display_text_response_181,
+       .pdu_len = sizeof(display_text_response_181),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x80, /* Wait for user to clear */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_USER_TERMINATED,
+               },
+       },
+};
+
+static const unsigned char display_text_response_191[] = {
+       0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x32,
+};
+
+static const struct terminal_response_test display_text_response_data_191 = {
+       .pdu = display_text_response_191,
+       .pdu_len = sizeof(display_text_response_191),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x80, /* Wait for user to clear */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD,
+               },
+       },
+};
+
+static const unsigned char display_text_response_211[] = {
+       0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x12,
+};
+
+static const struct terminal_response_test display_text_response_data_211 = {
+       .pdu = display_text_response_211,
+       .pdu_len = sizeof(display_text_response_211),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x80, /* Wait for user to clear */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_RESPONSE,
+               },
+       },
+};
+
+static const unsigned char display_text_response_511b[] = {
+       0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test display_text_response_data_511b = {
+       .pdu = display_text_response_511b,
+       .pdu_len = sizeof(display_text_response_511b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_DISPLAY_TEXT,
+               .qualifier = 0x80, /* Wait for user to clear */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+static const unsigned char get_inkey_response_111[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x2b,
+};
+
+static const struct terminal_response_test get_inkey_response_data_111 = {
+       .pdu = get_inkey_response_111,
+       .pdu_len = sizeof(get_inkey_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "+",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_121[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x30,
+};
+
+static const struct terminal_response_test get_inkey_response_data_121 = {
+       .pdu = get_inkey_response_121,
+       .pdu_len = sizeof(get_inkey_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "0",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_131[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x11,
+};
+
+static const struct terminal_response_test get_inkey_response_data_131 = {
+       .pdu = get_inkey_response_131,
+       .pdu_len = sizeof(get_inkey_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_GO_BACK,
+               },
+       },
+};
+
+static const unsigned char get_inkey_response_141[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x10,
+};
+
+static const struct terminal_response_test get_inkey_response_data_141 = {
+       .pdu = get_inkey_response_141,
+       .pdu_len = sizeof(get_inkey_response_141),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_USER_TERMINATED,
+               },
+       },
+};
+
+static const unsigned char get_inkey_response_151[] = {
+       0x81, 0x03, 0x01, 0x22, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x71,
+};
+
+static const struct terminal_response_test get_inkey_response_data_151 = {
+       .pdu = get_inkey_response_151,
+       .pdu_len = sizeof(get_inkey_response_151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x01, /* SMS alphabet */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "q",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_161[] = {
+       0x81, 0x03, 0x01, 0x22, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x78,
+};
+
+static const struct terminal_response_test get_inkey_response_data_161 = {
+       .pdu = get_inkey_response_161,
+       .pdu_len = sizeof(get_inkey_response_161),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x01, /* SMS alphabet */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "x",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_211[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x12,
+};
+
+static const struct terminal_response_test get_inkey_response_data_211 = {
+       .pdu = get_inkey_response_211,
+       .pdu_len = sizeof(get_inkey_response_211),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_RESPONSE,
+               },
+       },
+};
+
+static const unsigned char get_inkey_response_411[] = {
+       0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x03, 0x08, 0x04,
+       0x14,
+};
+
+static const struct terminal_response_test get_inkey_response_data_411 = {
+       .pdu = get_inkey_response_411,
+       .pdu_len = sizeof(get_inkey_response_411),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x03, /* UCS2 alphabet */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "Д",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_511[] = {
+       0x81, 0x03, 0x01, 0x22, 0x04, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x01,
+};
+
+static const struct terminal_response_test get_inkey_response_data_511 = {
+       .pdu = get_inkey_response_511,
+       .pdu_len = sizeof(get_inkey_response_511),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x04, /* Yes/No response */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "Yes",
+                               .yesno = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_512[] = {
+       0x81, 0x03, 0x01, 0x22, 0x04, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x00,
+};
+
+static const struct terminal_response_test get_inkey_response_data_512 = {
+       .pdu = get_inkey_response_512,
+       .pdu_len = sizeof(get_inkey_response_512),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x04, /* Yes/No response */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = NULL,
+                               .yesno = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_611b[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04, 0x8d, 0x02, 0x04, 0x2b,
+};
+
+static const struct terminal_response_test get_inkey_response_data_611b = {
+       .pdu = get_inkey_response_611b,
+       .pdu_len = sizeof(get_inkey_response_611b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "+",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_711[] = {
+       0x81, 0x03, 0x01, 0x22, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x13,
+};
+
+static const struct terminal_response_test get_inkey_response_data_711 = {
+       .pdu = get_inkey_response_711,
+       .pdu_len = sizeof(get_inkey_response_711),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x80, /* Help information available */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_HELP_REQUESTED,
+               },
+       },
+};
+
+static const unsigned char get_inkey_response_712[] = {
+       0x81, 0x03, 0x01, 0x22, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x2b,
+};
+
+static const struct terminal_response_test get_inkey_response_data_712 = {
+       .pdu = get_inkey_response_712,
+       .pdu_len = sizeof(get_inkey_response_712),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x80, /* Help information available */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "+",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_811[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x12, 0x04, 0x02, 0x01, 0x11,
+};
+
+static const struct terminal_response_test get_inkey_response_data_811 = {
+       .pdu = get_inkey_response_811,
+       .pdu_len = sizeof(get_inkey_response_811),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_RESPONSE,
+               },
+               { .get_inkey = {
+                       .duration = {
+                               .unit = STK_DURATION_TYPE_SECONDS,
+                               .interval = 17,
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_912[] = {
+       0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x23,
+};
+
+static const struct terminal_response_test get_inkey_response_data_912 = {
+       .pdu = get_inkey_response_912,
+       .pdu_len = sizeof(get_inkey_response_912),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "#",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_1111[] = {
+       0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x03, 0x08, 0x59,
+       0x7d,
+};
+
+static const struct terminal_response_test get_inkey_response_data_1111 = {
+       .pdu = get_inkey_response_1111,
+       .pdu_len = sizeof(get_inkey_response_1111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x03, /* UCS2 alphabet */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "好",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_inkey_response_1311[] = {
+       0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x03, 0x08, 0x30,
+       0xeb,
+};
+
+static const struct terminal_response_test get_inkey_response_data_1311 = {
+       .pdu = get_inkey_response_1311,
+       .pdu_len = sizeof(get_inkey_response_1311),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INKEY,
+               .qualifier = 0x03, /* UCS2 alphabet */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_inkey = {
+                       .text = {
+                               .text = "ル",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_111[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x31,
+       0x32, 0x33, 0x34, 0x35,
+};
+
+static const struct terminal_response_test get_input_response_data_111 = {
+       .pdu = get_input_response_111,
+       .pdu_len = sizeof(get_input_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "12345",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_121[] = {
+       0x81, 0x03, 0x01, 0x23, 0x08, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x00, 0xb6,
+       0x9b, 0x6a, 0xb4, 0x02,
+};
+
+static const struct terminal_response_test get_input_response_data_121 = {
+       .pdu = get_input_response_121,
+       .pdu_len = sizeof(get_input_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x08, /* Input is packed */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "67*#+",
+                               .packed = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_131[] = {
+       0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x41,
+       0x62, 0x43, 0x64, 0x45,
+};
+
+static const struct terminal_response_test get_input_response_data_131 = {
+       .pdu = get_input_response_131,
+       .pdu_len = sizeof(get_input_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x01, /* Allow all SMS characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "AbCdE",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_141[] = {
+       0x81, 0x03, 0x01, 0x23, 0x04, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x08, 0x04, 0x32,
+       0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+};
+
+static const struct terminal_response_test get_input_response_data_141 = {
+       .pdu = get_input_response_141,
+       .pdu_len = sizeof(get_input_response_141),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x04, /* Hide text */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "2345678",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_151[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x15, 0x04, 0x31,
+       0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+       0x38, 0x39, 0x30,
+};
+
+static const struct terminal_response_test get_input_response_data_151 = {
+       .pdu = get_input_response_151,
+       .pdu_len = sizeof(get_input_response_151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "12345678901234567890",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_161[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x11,
+};
+
+static const struct terminal_response_test get_input_response_data_161 = {
+       .pdu = get_input_response_161,
+       .pdu_len = sizeof(get_input_response_161),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_GO_BACK,
+               },
+       },
+};
+
+static const unsigned char get_input_response_171[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x10,
+};
+
+static const struct terminal_response_test get_input_response_data_171 = {
+       .pdu = get_input_response_171,
+       .pdu_len = sizeof(get_input_response_171),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_USER_TERMINATED,
+               },
+       },
+};
+
+static const unsigned char get_input_response_181[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0xa1, 0x04,
+       0x2a, 0x2a, 0x2a, 0x31, 0x31, 0x31, 0x31, 0x31,
+       0x31, 0x31, 0x31, 0x31, 0x31, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x32, 0x32, 0x32, 0x32, 0x32,
+       0x32, 0x32, 0x32, 0x32, 0x32, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x33, 0x33, 0x33, 0x33, 0x33,
+       0x33, 0x33, 0x33, 0x33, 0x33, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x34, 0x34, 0x34, 0x34, 0x34,
+       0x34, 0x34, 0x34, 0x34, 0x34, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x35, 0x35, 0x35, 0x35, 0x35,
+       0x35, 0x35, 0x35, 0x35, 0x35, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x36, 0x36, 0x36, 0x36, 0x36,
+       0x36, 0x36, 0x36, 0x36, 0x36, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x37, 0x37, 0x37, 0x37, 0x37,
+       0x37, 0x37, 0x37, 0x37, 0x37, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x38, 0x38, 0x38, 0x38, 0x38,
+       0x38, 0x38, 0x38, 0x38, 0x38, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x39, 0x39, 0x39, 0x39, 0x39,
+       0x39, 0x39, 0x39, 0x39, 0x39, 0x23, 0x23, 0x23,
+       0x2a, 0x2a, 0x2a, 0x30, 0x30, 0x30, 0x30, 0x30,
+       0x30, 0x30, 0x30, 0x30, 0x30, 0x23, 0x23, 0x23,
+};
+
+static const struct terminal_response_test get_input_response_data_181 = {
+       .pdu = get_input_response_181,
+       .pdu_len = sizeof(get_input_response_181),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "***1111111111###***2222222222###"
+                                       "***3333333333###***4444444444###"
+                                       "***5555555555###***6666666666###"
+                                       "***7777777777###***8888888888###"
+                                       "***9999999999###***0000000000###",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_191a[] = {
+       0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x01, 0x04,
+};
+
+static const unsigned char get_input_response_191b[] = {
+       0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x00,
+};
+
+static const struct terminal_response_test get_input_response_data_191 = {
+       /* Either get_input_response_191a or get_input_response_191b is ok */
+       .pdu = get_input_response_191a,
+       .pdu_len = sizeof(get_input_response_191a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x01, /* Allow all SMS characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_211[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x12,
+};
+
+static const struct terminal_response_test get_input_response_data_211 = {
+       .pdu = get_input_response_211,
+       .pdu_len = sizeof(get_input_response_211),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_RESPONSE,
+               },
+       },
+};
+
+static const unsigned char get_input_response_311[] = {
+       0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x48,
+       0x45, 0x4c, 0x4c, 0x4f,
+};
+
+static const struct terminal_response_test get_input_response_data_311 = {
+       .pdu = get_input_response_311,
+       .pdu_len = sizeof(get_input_response_311),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x01, /* Allow all SMS characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "HELLO",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_411[] = {
+       0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x19, 0x08, 0x04,
+       0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04,
+       0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04,
+       0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15,
+};
+
+static const struct terminal_response_test get_input_response_data_411 = {
+       .pdu = get_input_response_411,
+       .pdu_len = sizeof(get_input_response_411),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x03, /* Allow all UCS2 characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "ЗДРАВСТВУЙТЕ",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_421[] = {
+       0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0x8d, 0x08,
+       0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10,
+       0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12,
+       0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15,
+       0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10,
+       0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12,
+       0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15,
+       0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10,
+       0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12,
+       0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15,
+       0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10,
+       0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12,
+       0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15,
+       0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10,
+       0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12,
+       0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15,
+       0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10,
+       0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12,
+       0x04, 0x23, 0x04, 0x19,
+};
+
+static const struct terminal_response_test get_input_response_data_421 = {
+       .pdu = get_input_response_421,
+       .pdu_len = sizeof(get_input_response_421),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x03, /* Allow all UCS2 characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ"
+                                       "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ"
+                                       "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_611a[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x2b,
+};
+
+static const struct terminal_response_test get_input_response_data_611a = {
+       .pdu = get_input_response_611a,
+       .pdu_len = sizeof(get_input_response_611a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "+",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_611b[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04, 0x8d, 0x02, 0x04, 0x2b,
+};
+
+static const struct terminal_response_test get_input_response_data_611b = {
+       .pdu = get_input_response_611b,
+       .pdu_len = sizeof(get_input_response_611b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "+",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_711[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x13,
+};
+
+static const struct terminal_response_test get_input_response_data_711 = {
+       .pdu = get_input_response_711,
+       .pdu_len = sizeof(get_input_response_711),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_HELP_REQUESTED,
+               },
+       },
+};
+
+static const unsigned char get_input_response_812[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x32,
+       0x32, 0x32, 0x32, 0x32,
+};
+
+static const struct terminal_response_test get_input_response_data_812 = {
+       .pdu = get_input_response_812,
+       .pdu_len = sizeof(get_input_response_812),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "22222",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_843[] = {
+       0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x33,
+       0x33, 0x33, 0x33, 0x33,
+};
+
+static const struct terminal_response_test get_input_response_data_843 = {
+       .pdu = get_input_response_843,
+       .pdu_len = sizeof(get_input_response_843),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "33333",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_1011[] = {
+       0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x05, 0x08, 0x4f,
+       0x60, 0x59, 0x7d,
+};
+
+static const struct terminal_response_test get_input_response_data_1011 = {
+       .pdu = get_input_response_1011,
+       .pdu_len = sizeof(get_input_response_1011),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x03, /* Allow all UCS2 characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "你好",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_1021[] = {
+       0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0x8d, 0x08,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d,
+       0x4f, 0x60, 0x59, 0x7d,
+};
+
+static const struct terminal_response_test get_input_response_data_1021 = {
+       .pdu = get_input_response_1021,
+       .pdu_len = sizeof(get_input_response_1021),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x03, /* Allow all UCS2 characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "你好你好你好你好你好你好"
+                                       "你好你好你好你好你好你好"
+                                       "你好你好你好你好你好你好"
+                                       "你好你好你好你好你好你好"
+                                       "你好你好你好你好你好你好"
+                                       "你好你好你好你好你好",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_1211[] = {
+       0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x05, 0x08, 0x30,
+       0xeb, 0x30, 0xeb,
+};
+
+static const struct terminal_response_test get_input_response_data_1211 = {
+       .pdu = get_input_response_1211,
+       .pdu_len = sizeof(get_input_response_1211),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x03, /* Allow all UCS2 characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "ルル",
+                       },
+               }},
+       },
+};
+
+static const unsigned char get_input_response_1221[] = {
+       0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0x8d, 0x08,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb,
+       0x30, 0xeb, 0x30, 0xeb,
+};
+
+static const struct terminal_response_test get_input_response_data_1221 = {
+       .pdu = get_input_response_1221,
+       .pdu_len = sizeof(get_input_response_1221),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_INPUT,
+               .qualifier = 0x03, /* Allow all UCS2 characters */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .get_input = {
+                       .text = {
+                               .text = "ルルルルルルルルルルルル"
+                                       "ルルルルルルルルルルルル"
+                                       "ルルルルルルルルルルルル"
+                                       "ルルルルルルルルルルルル"
+                                       "ルルルルルルルルルルルル"
+                                       "ルルルルルルルルルル",
+                       },
+               }},
+       },
+};
+
+static const unsigned char more_time_response_111[] = {
+       0x81, 0x03, 0x01, 0x02, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test more_time_response_data_111 = {
+       .pdu = more_time_response_111,
+       .pdu_len = sizeof(more_time_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_MORE_TIME,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char send_sms_response_111[] = {
+       0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test send_sms_response_data_111 = {
+       .pdu = send_sms_response_111,
+       .pdu_len = sizeof(send_sms_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_SMS,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char send_sms_response_121[] = {
+       0x81, 0x03, 0x01, 0x13, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test send_sms_response_data_121 = {
+       .pdu = send_sms_response_121,
+       .pdu_len = sizeof(send_sms_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_SMS,
+               .qualifier = 0x01, /* Packing required */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char send_sms_response_311b[] = {
+       0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test send_sms_response_data_311b = {
+       .pdu = send_sms_response_311b,
+       .pdu_len = sizeof(send_sms_response_311b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_SMS,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+static const unsigned char play_tone_response_111[] = {
+       0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test play_tone_response_data_111 = {
+       .pdu = play_tone_response_111,
+       .pdu_len = sizeof(play_tone_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PLAY_TONE,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char play_tone_response_119b[] = {
+       0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x30,
+};
+
+static const struct terminal_response_test play_tone_response_data_119b = {
+       .pdu = play_tone_response_119b,
+       .pdu_len = sizeof(play_tone_response_119b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PLAY_TONE,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NOT_CAPABLE,
+               },
+       },
+};
+
+static const unsigned char play_tone_response_1114[] = {
+       0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x10,
+};
+
+static const struct terminal_response_test play_tone_response_data_1114 = {
+       .pdu = play_tone_response_1114,
+       .pdu_len = sizeof(play_tone_response_1114),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PLAY_TONE,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_USER_TERMINATED,
+               },
+       },
+};
+
+static const unsigned char play_tone_response_311b[] = {
+       0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test play_tone_response_data_311b = {
+       .pdu = play_tone_response_311b,
+       .pdu_len = sizeof(play_tone_response_311b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PLAY_TONE,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+/* TS 102 384 */
+static const unsigned char poll_interval_response_111[] = {
+       0x81, 0x03, 0x01, 0x03, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x84, 0x02, 0x01, 0x14,
+};
+
+static const struct terminal_response_test poll_interval_response_data_111 = {
+       .pdu = poll_interval_response_111,
+       .pdu_len = sizeof(poll_interval_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_POLL_INTERVAL,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .poll_interval = {
+                       .max_interval = {
+                               .unit = STK_DURATION_TYPE_SECONDS,
+                               .interval = 20,
+                       },
+               }},
+       },
+};
+
+/* 3GPP TS 31.124 */
+static const unsigned char poll_interval_response_111a[] = {
+       0x81, 0x03, 0x01, 0x03, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x84, 0x02, 0x00, 0x01,
+};
+
+static const unsigned char poll_interval_response_111b[] = {
+       0x81, 0x03, 0x01, 0x03, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x84, 0x02, 0x01, 0x3c,
+};
+
+static const struct terminal_response_test poll_interval_response_data_111a = {
+       /* Either poll_interval_response_111a or b is ok */
+       .pdu = poll_interval_response_111a,
+       .pdu_len = sizeof(poll_interval_response_111a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_POLL_INTERVAL,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .poll_interval = {
+                       .max_interval = {
+                               .unit = STK_DURATION_TYPE_MINUTES,
+                               .interval = 1,
+                       },
+               }},
+       },
+};
+
+static const unsigned char refresh_response_111a[] = {
+       0x81, 0x03, 0x01, 0x01, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test refresh_response_data_111a = {
+       .pdu = refresh_response_111a,
+       .pdu_len = sizeof(refresh_response_111a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x03, /* USIM Initialization */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_111b[] = {
+       0x81, 0x03, 0x01, 0x01, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x03,
+};
+
+static const struct terminal_response_test refresh_response_data_111b = {
+       .pdu = refresh_response_111b,
+       .pdu_len = sizeof(refresh_response_111b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x03, /* USIM Initialization */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_REFRESH_WITH_EFS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_121a[] = {
+       0x81, 0x03, 0x01, 0x01, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test refresh_response_data_121a = {
+       .pdu = refresh_response_121a,
+       .pdu_len = sizeof(refresh_response_121a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x01, /* File Change Notification */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_121b[] = {
+       0x81, 0x03, 0x01, 0x01, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x03,
+};
+
+static const struct terminal_response_test refresh_response_data_121b = {
+       .pdu = refresh_response_121b,
+       .pdu_len = sizeof(refresh_response_121b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x01, /* File Change Notification */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_REFRESH_WITH_EFS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_131a[] = {
+       0x81, 0x03, 0x01, 0x01, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test refresh_response_data_131a = {
+       .pdu = refresh_response_131a,
+       .pdu_len = sizeof(refresh_response_131a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x02, /* USIM Initialization & File Change */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_131b[] = {
+       0x81, 0x03, 0x01, 0x01, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x03,
+};
+
+static const struct terminal_response_test refresh_response_data_131b = {
+       .pdu = refresh_response_131b,
+       .pdu_len = sizeof(refresh_response_131b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x02, /* USIM Initialization & File Change */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_REFRESH_WITH_EFS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_141a[] = {
+       0x81, 0x03, 0x01, 0x01, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test refresh_response_data_141a = {
+       .pdu = refresh_response_141a,
+       .pdu_len = sizeof(refresh_response_141a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x00, /* USIM Initialization & Full File Change */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_141b[] = {
+       0x81, 0x03, 0x01, 0x01, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x03,
+};
+
+static const struct terminal_response_test refresh_response_data_141b = {
+       .pdu = refresh_response_141b,
+       .pdu_len = sizeof(refresh_response_141b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x00, /* USIM Initialization & Full File Change */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_REFRESH_WITH_EFS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_171[] = {
+       0x81, 0x03, 0x01, 0x01, 0x05, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test refresh_response_data_171 = {
+       .pdu = refresh_response_171,
+       .pdu_len = sizeof(refresh_response_171),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x05, /* USIM Application Reset */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char refresh_response_241a[] = {
+       0x81, 0x03, 0x01, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x20, 0x02,
+};
+
+static const struct terminal_response_test refresh_response_data_241a = {
+       .pdu = refresh_response_241a,
+       .pdu_len = sizeof(refresh_response_241a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x06, /* 3G Session Reset */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TERMINAL_BUSY,
+                       .additional_len = 1, /* ME currently busy on call */
+                       .additional = (unsigned char *) "\2",
+               },
+       },
+};
+
+static const unsigned char refresh_response_241b[] = {
+       0x81, 0x03, 0x01, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x20, 0x01,
+};
+
+static const struct terminal_response_test refresh_response_data_241b = {
+       .pdu = refresh_response_241b,
+       .pdu_len = sizeof(refresh_response_241b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x06, /* 3G Session Reset */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TERMINAL_BUSY,
+                       .additional_len = 1, /* Screen is busy */
+                       .additional = (unsigned char *) "\1",
+               },
+       },
+};
+
+static const unsigned char refresh_response_311[] = {
+       0x81, 0x03, 0x01, 0x01, 0x07, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x20,
+};
+
+static const struct terminal_response_test refresh_response_data_311 = {
+       .pdu = refresh_response_311,
+       .pdu_len = sizeof(refresh_response_311),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x07, /* Steering of roaming */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TERMINAL_BUSY,
+               },
+       },
+};
+
+static const unsigned char refresh_response_312[] = {
+       0x81, 0x03, 0x01, 0x01, 0x07, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test refresh_response_data_312 = {
+       .pdu = refresh_response_312,
+       .pdu_len = sizeof(refresh_response_312),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_REFRESH,
+               .qualifier = 0x07, /* Steering of roaming */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char set_up_menu_response_111[] = {
+       0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test set_up_menu_response_data_111 = {
+       .pdu = set_up_menu_response_111,
+       .pdu_len = sizeof(set_up_menu_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_MENU,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char set_up_menu_response_411b[] = {
+       0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test set_up_menu_response_data_411b = {
+       .pdu = set_up_menu_response_411b,
+       .pdu_len = sizeof(set_up_menu_response_411b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_MENU,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+static const unsigned char set_up_menu_response_511[] = {
+       0x81, 0x03, 0x01, 0x25, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test set_up_menu_response_data_511 = {
+       .pdu = set_up_menu_response_511,
+       .pdu_len = sizeof(set_up_menu_response_511),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_MENU,
+               .qualifier = 0x01, /* Soft key selection preferred */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char select_item_response_111[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x02,
+};
+
+static const struct terminal_response_test select_item_response_data_111 = {
+       .pdu = select_item_response_111,
+       .pdu_len = sizeof(select_item_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 2,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_121[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x3d,
+};
+
+static const struct terminal_response_test select_item_response_data_121 = {
+       .pdu = select_item_response_121,
+       .pdu_len = sizeof(select_item_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 61,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_131[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0xfb,
+};
+
+static const struct terminal_response_test select_item_response_data_131 = {
+       .pdu = select_item_response_131,
+       .pdu_len = sizeof(select_item_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 251,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_141[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x11,
+};
+
+static const struct terminal_response_test select_item_response_data_141 = {
+       /* The response can be select_item_response_141 or it can optionally
+        * have an ITEM_ID data object appended with any id (90 01 XX).  */
+       .pdu = select_item_response_141,
+       .pdu_len = sizeof(select_item_response_141),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_GO_BACK,
+               },
+       },
+};
+
+static const unsigned char select_item_response_142[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x10,
+};
+
+static const struct terminal_response_test select_item_response_data_142 = {
+       /* The response can be select_item_response_142 or it can optionally
+        * have an ITEM_ID data object appended with any id (90 01 XX).  */
+       .pdu = select_item_response_142,
+       .pdu_len = sizeof(select_item_response_142),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_USER_TERMINATED,
+               },
+       },
+};
+
+static const unsigned char select_item_response_151[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01,
+};
+
+static const struct terminal_response_test select_item_response_data_151 = {
+       .pdu = select_item_response_151,
+       .pdu_len = sizeof(select_item_response_151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 1,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_311[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x03,
+};
+
+static const struct terminal_response_test select_item_response_data_311 = {
+       .pdu = select_item_response_311,
+       .pdu_len = sizeof(select_item_response_311),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 3,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_411[] = {
+       0x81, 0x03, 0x01, 0x24, 0x80, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x13, 0x90, 0x01, 0x01,
+};
+
+static const struct terminal_response_test select_item_response_data_411 = {
+       .pdu = select_item_response_411,
+       .pdu_len = sizeof(select_item_response_411),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x80, /* Help information available */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_HELP_REQUESTED,
+               },
+               { .select_item = {
+                       .item_id = 1,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_511b[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04, 0x90, 0x01, 0x01,
+};
+
+static const struct terminal_response_test select_item_response_data_511b = {
+       .pdu = select_item_response_511b,
+       .pdu_len = sizeof(select_item_response_511b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+               { .select_item = {
+                       .item_id = 1,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_611[] = {
+       0x81, 0x03, 0x01, 0x24, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01,
+};
+
+static const struct terminal_response_test select_item_response_data_611 = {
+       .pdu = select_item_response_611,
+       .pdu_len = sizeof(select_item_response_611),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x03, /* Choice of navigation options */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 1,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_621[] = {
+       0x81, 0x03, 0x01, 0x24, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01,
+};
+
+static const struct terminal_response_test select_item_response_data_621 = {
+       .pdu = select_item_response_621,
+       .pdu_len = sizeof(select_item_response_621),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x01, /* Choice of data values presentation */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 1,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_711[] = {
+       0x81, 0x03, 0x01, 0x24, 0x04, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01,
+};
+
+static const struct terminal_response_test select_item_response_data_711 = {
+       .pdu = select_item_response_711,
+       .pdu_len = sizeof(select_item_response_711),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x04, /* Selection using soft keys preferred */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .select_item = {
+                       .item_id = 1,
+               }},
+       },
+};
+
+static const unsigned char select_item_response_811[] = {
+       0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x12,
+};
+
+static const struct terminal_response_test select_item_response_data_811 = {
+       .pdu = select_item_response_811,
+       .pdu_len = sizeof(select_item_response_811),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SELECT_ITEM,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_RESPONSE,
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_111[] = {
+       0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test set_up_call_response_data_111 = {
+       .pdu = set_up_call_response_111,
+       .pdu_len = sizeof(set_up_call_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x00, /* Only if not busy on another call */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_121[] = {
+       0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x22,
+};
+
+static const struct terminal_response_test set_up_call_response_data_121 = {
+       .pdu = set_up_call_response_121,
+       .pdu_len = sizeof(set_up_call_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x00, /* Only if not busy on another call */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_USER_REJECT,
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_141[] = {
+       0x81, 0x03, 0x01, 0x10, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test set_up_call_response_data_141 = {
+       .pdu = set_up_call_response_141,
+       .pdu_len = sizeof(set_up_call_response_141),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x02, /* Put all other calls on hold */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_151[] = {
+       0x81, 0x03, 0x01, 0x10, 0x04, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test set_up_call_response_data_151 = {
+       .pdu = set_up_call_response_151,
+       .pdu_len = sizeof(set_up_call_response_151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x04, /* Disconnect all other calls */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_161[] = {
+       0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x20, 0x02,
+};
+
+static const struct terminal_response_test set_up_call_response_data_161 = {
+       .pdu = set_up_call_response_161,
+       .pdu_len = sizeof(set_up_call_response_161),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x00, /* Only if not busy on another call */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TERMINAL_BUSY,
+                       .additional_len = 1, /* ME currently busy on call */
+                       .additional = (unsigned char[1]) { 0x02 },
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_171a[] = {
+       0x81, 0x03, 0x01, 0x10, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x21, 0x00,
+};
+
+static const struct terminal_response_test set_up_call_response_data_171a = {
+       .pdu = set_up_call_response_171a,
+       .pdu_len = sizeof(set_up_call_response_171a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x02, /* Put all other calls on hold */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE,
+                       .additional_len = 1, /* No specific cause given */
+                       .additional = (unsigned char[1]) { 0x00 },
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_171b[] = {
+       0x81, 0x03, 0x01, 0x10, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x21, 0x9d,
+};
+
+static const struct terminal_response_test set_up_call_response_data_171b = {
+       .pdu = set_up_call_response_171b,
+       .pdu_len = sizeof(set_up_call_response_171b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x02, /* Put all other calls on hold */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE,
+                       .additional_len = 1, /* Facility rejected */
+                       .additional = (unsigned char[1]) { 0x9d },
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_1101[] = {
+       0x81, 0x03, 0x01, 0x10, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test set_up_call_response_data_1101 = {
+       .pdu = set_up_call_response_1101,
+       .pdu_len = sizeof(set_up_call_response_1101),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x01, /* Only if not busy, with redial */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_1111b[] = {
+       0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x30,
+};
+
+static const struct terminal_response_test set_up_call_response_data_1111b = {
+       .pdu = set_up_call_response_1111b,
+       .pdu_len = sizeof(set_up_call_response_1111b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x00, /* Only if not busy on another call */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NOT_CAPABLE,
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_1121[] = {
+       0x81, 0x03, 0x01, 0x10, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x21, 0x91,
+};
+
+static const struct terminal_response_test set_up_call_response_data_1121 = {
+       .pdu = set_up_call_response_1121,
+       .pdu_len = sizeof(set_up_call_response_1121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x01, /* Only if not busy, with redial */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE,
+                       .additional_len = 1, /* User busy */
+                       .additional = (unsigned char[1]) { 0x91 },
+               },
+       },
+};
+
+static const unsigned char set_up_call_response_311b[] = {
+       0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test set_up_call_response_data_311b = {
+       .pdu = set_up_call_response_311b,
+       .pdu_len = sizeof(set_up_call_response_311b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_CALL,
+               .qualifier = 0x00, /* Only if not busy on another call */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+static const unsigned char polling_off_response_112[] = {
+       0x81, 0x03, 0x01, 0x04, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test polling_off_response_data_112 = {
+       .pdu = polling_off_response_112,
+       .pdu_len = sizeof(polling_off_response_112),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_POLLING_OFF,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char provide_local_info_response_111a[] = {
+       0x81, 0x03, 0x01, 0x26, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x93, 0x07, 0x00, 0xf1,
+       0x10, 0x00, 0x01, 0x00, 0x01,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_111a = {
+       .pdu = provide_local_info_response_111a,
+       .pdu_len = sizeof(provide_local_info_response_111a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x00, /* Location information (MCC MNC LAC CI) */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .location = {
+                               .mcc = "001",
+                               .mnc = "01",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_111b[] = {
+       0x81, 0x03, 0x01, 0x26, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x93, 0x07, 0x00, 0x11,
+       0x10, 0x00, 0x01, 0x00, 0x01,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_111b = {
+       .pdu = provide_local_info_response_111b,
+       .pdu_len = sizeof(provide_local_info_response_111b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x00, /* Location information (MCC MNC LAC CI) */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .location = {
+                               .mcc = "001",
+                               .mnc = "011",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_121[] = {
+       0x81, 0x03, 0x01, 0x26, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x94, 0x08, 0x1a, 0x32,
+       0x54, 0x76, 0x98, 0x10, 0x32, 0x54, /* Typo in TS 102 384? */
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_121 = {
+       .pdu = provide_local_info_response_121,
+       .pdu_len = sizeof(provide_local_info_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x01, /* IMEI of the Terminal */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .imei = "123456789012345", }
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_131[] = {
+       0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x96, 0x10, 0x34, 0x34,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x0d,
+       0x8c, 0x63, 0x58, 0xe2, 0x39, 0x8f, 0x63, 0xf9,
+       0x06, 0x45, 0x91, 0xa4, 0x90,
+};
+
+static const short bcch_channels_131[] = {
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_131 = {
+       .pdu = provide_local_info_response_131,
+       .pdu_len = sizeof(provide_local_info_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x02, /* Network Measurement Results */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .nmr = {
+                               .nmr = {
+                                       /* RXLEV-FULL-SERVING-CELL=52, no BA,
+                                        * no DTX */
+                                       .array = (unsigned char[16]) {
+                                               0x34, 0x34, 0x00, 0x00,
+                                               0x00, 0x00, 0x00, 0x00,
+                                               0x00, 0x00, 0x00, 0x00,
+                                               0x00, 0x00, 0x00, 0x00,
+                                       },
+                                       .len = 16,
+                               },
+                               .bcch_ch_list = {
+                                       .channels = {
+                                               561, 565, 568, 569, 573,
+                                               575, 577, 581, 582, 585,
+                                       },
+                                       .num = 10,
+                                       .has_list = TRUE,
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_141[] = {
+       0x81, 0x03, 0x01, 0x26, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa6, 0x07, 0x20, 0x50,
+       0x70, 0x41, 0x80, 0x71, 0xff,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_141 = {
+       .pdu = provide_local_info_response_141,
+       .pdu_len = sizeof(provide_local_info_response_141),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x03, /* Date Time and Time Zone */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .datetime = {
+                               .year = 2, /* 2002 - 1900 - 100 */
+                               .month = 5,
+                               .day = 7,
+                               .hour = 14,
+                               .minute = 8,
+                               .second = 17,
+                               .timezone = 0xff, /* No information */
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_151[] = {
+       0x81, 0x03, 0x01, 0x26, 0x04, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xad, 0x02, 0x65, 0x6e,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_151 = {
+       .pdu = provide_local_info_response_151,
+       .pdu_len = sizeof(provide_local_info_response_151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x04, /* Language setting */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .language = "en", }
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_161[] = {
+       0x81, 0x03, 0x01, 0x26, 0x05, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xae, 0x02, 0x00, 0x00,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_161 = {
+       .pdu = provide_local_info_response_161,
+       .pdu_len = sizeof(provide_local_info_response_161),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x05, /* Timing Advance */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .tadv = {
+                               .status = STK_ME_STATUS_IDLE,
+                               .advance = 0,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_171[] = {
+       0x81, 0x03, 0x01, 0x26, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x3f, 0x01, 0x03,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_171 = {
+       .pdu = provide_local_info_response_171,
+       .pdu_len = sizeof(provide_local_info_response_171),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x06, /* Access technology */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .access_technology = STK_ACCESS_TECHNOLOGY_UTRAN, }
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_181[] = {
+       0x81, 0x03, 0x01, 0x26, 0x07, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xc6, 0x04, 0x01, 0x02,
+       0x03, 0x04,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_181 = {
+       .pdu = provide_local_info_response_181,
+       .pdu_len = sizeof(provide_local_info_response_181),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x07, /* ESN of the terminal */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .esn = 0x01020304, }
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_191[] = {
+       0x81, 0x03, 0x01, 0x26, 0x08, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xe2, 0x09, 0x13, 0x32,
+       0x54, 0x76, 0x98, 0x10, 0x32, 0x54, 0xf6,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_191 = {
+       .pdu = provide_local_info_response_191,
+       .pdu_len = sizeof(provide_local_info_response_191),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x08, /* IMEISV of the terminal */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .imeisv = "1234567890123456", }
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_1111[] = {
+       0x81, 0x03, 0x01, 0x26, 0x0a, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xe3, 0x01, 0x04,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_1111 = {
+       .pdu = provide_local_info_response_1111,
+       .pdu_len = sizeof(provide_local_info_response_1111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x0a, /* Charge state of the battery */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .battery_charge = STK_BATTERY_FULL, }
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_1121[] = {
+       0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x00,
+       /* Intra-frequency UTRAN Measurement report in ASN.1 goes here */
+       /* "The remaining bytes shall not be verified" */
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_1121 = {
+       .pdu = provide_local_info_response_1121,
+       .pdu_len = sizeof(provide_local_info_response_1121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x02, /* Network Measurement Results */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .nmr = {
+                               .nmr = {
+                                       .array = (unsigned char[2])
+                                               { 0x80, 0x00 },
+                                       .len = 2,
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_1131[] = {
+       0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x11,
+       /* Inter-frequency UTRAN Measurement report in ASN.1 goes here */
+       /* "The remaining bytes shall not be verified" */
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_1131 = {
+       .pdu = provide_local_info_response_1131,
+       .pdu_len = sizeof(provide_local_info_response_1131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x02, /* Network Measurement Results */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .nmr = {
+                               .nmr = {
+                                       .array = (unsigned char[2])
+                                               { 0x80, 0x11},
+                                       .len = 2,
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_1141[] = {
+       0x81, 0x03, 0x01, 0x26, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x3f, 0x01, 0x08,
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_1141 = {
+       .pdu = provide_local_info_response_1141,
+       .pdu_len = sizeof(provide_local_info_response_1141),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x06, /* Access technology */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .access_technology = STK_ACCESS_TECHNOLOGY_EUTRAN, }
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_1151[] = {
+       0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x00,
+       /* Intra-frequency E-UTRAN Measurement report in ASN.1 goes here */
+       /* "The remaining bytes shall not be verified" */
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_1151 = {
+       .pdu = provide_local_info_response_1151,
+       .pdu_len = sizeof(provide_local_info_response_1151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x02, /* Network Measurement Results */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .nmr = {
+                               .nmr = {
+                                       .array = (unsigned char[2])
+                                               { 0x80, 0x00},
+                                       .len = 2,
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_1161[] = {
+       0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x11,
+       /* Inter-frequency E-UTRAN Measurement report in ASN.1 goes here */
+       /* "The remaining bytes shall not be verified" */
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_1161 = {
+       .pdu = provide_local_info_response_1161,
+       .pdu_len = sizeof(provide_local_info_response_1161),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x02, /* Network Measurement Results */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .nmr = {
+                               .nmr = {
+                                       .array = (unsigned char[2])
+                                               { 0x80, 0x11},
+                                       .len = 2,
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char provide_local_info_response_1171[] = {
+       0x81, 0x03, 0x01, 0x26, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0x93, 0x09, 0x00, 0xf1,
+       0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1f,
+       /* Typo in TS 102 223?  Byte 18 changed to 01 here */
+};
+
+static const struct terminal_response_test
+               provide_local_info_response_data_1171 = {
+       .pdu = provide_local_info_response_1171,
+       .pdu_len = sizeof(provide_local_info_response_1171),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO,
+               .qualifier = 0x00, /* Location information (MCC MNC LAC CI) */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .provide_local_info = {
+                       { .location = {
+                               .mcc = "001",
+                               .mnc = "01",
+                               .lac_tac = 0x0001,
+                               .has_eutran_ci = TRUE,
+                               .eutran_ci = 0x0000001,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char set_up_event_list_response_111[] = {
+       0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test
+               set_up_event_list_response_data_111 = {
+       .pdu = set_up_event_list_response_111,
+       .pdu_len = sizeof(set_up_event_list_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_EVENT_LIST,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char timer_mgmt_response_111[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x01,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_111 = {
+       .pdu = timer_mgmt_response_111,
+       .pdu_len = sizeof(timer_mgmt_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 1,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_112[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x01, 0xa5,
+       0x03, 0x00, 0x30, 0x95,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_112 = {
+       .pdu = timer_mgmt_response_112,
+       .pdu_len = sizeof(timer_mgmt_response_112),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 1,
+                       .value = {
+                               .minute = 3,
+                               .second = 59,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_114[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x01, 0xa5,
+       0x03, 0x00, 0x00, 0x95,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_114 = {
+       .pdu = timer_mgmt_response_114,
+       .pdu_len = sizeof(timer_mgmt_response_114),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 1,
+                       .value = {
+                               .second = 59,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_121[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x02,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_121 = {
+       .pdu = timer_mgmt_response_121,
+       .pdu_len = sizeof(timer_mgmt_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 2,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_122[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x02, 0xa5,
+       0x03, 0x32, 0x85, 0x85,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_122 = {
+       .pdu = timer_mgmt_response_122,
+       .pdu_len = sizeof(timer_mgmt_response_122),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 2,
+                       .value = {
+                               .hour = 23,
+                               .minute = 58,
+                               .second = 58,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_124[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x02, 0xa5,
+       0x03, 0x00, 0x00, 0x95,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_124 = {
+       .pdu = timer_mgmt_response_124,
+       .pdu_len = sizeof(timer_mgmt_response_124),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 2,
+                       .value = {
+                               .second = 59,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_131[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x08,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_131 = {
+       .pdu = timer_mgmt_response_131,
+       .pdu_len = sizeof(timer_mgmt_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 8,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_132[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x08, 0xa5,
+       0x03, 0x00, 0x81, 0x95,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_132 = {
+       .pdu = timer_mgmt_response_132,
+       .pdu_len = sizeof(timer_mgmt_response_132),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 8,
+                       .value = {
+                               .minute = 18,
+                               .second = 59,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_134[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x08, 0xa5,
+       0x03, 0x00, 0x95, 0x92,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_134 = {
+       .pdu = timer_mgmt_response_134,
+       .pdu_len = sizeof(timer_mgmt_response_134),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 8,
+                       .value = {
+                               .minute = 59,
+                               .second = 29,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_141a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x01,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_141a = {
+       .pdu = timer_mgmt_response_141a,
+       .pdu_len = sizeof(timer_mgmt_response_141a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 1,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_141b[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_141b = {
+       .pdu = timer_mgmt_response_141b,
+       .pdu_len = sizeof(timer_mgmt_response_141b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+       },
+};
+
+static const unsigned char timer_mgmt_response_142a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x02,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_142a = {
+       .pdu = timer_mgmt_response_142a,
+       .pdu_len = sizeof(timer_mgmt_response_142a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 2,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_143a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x03,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_143a = {
+       .pdu = timer_mgmt_response_143a,
+       .pdu_len = sizeof(timer_mgmt_response_143a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 3,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_144a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x04,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_144a = {
+       .pdu = timer_mgmt_response_144a,
+       .pdu_len = sizeof(timer_mgmt_response_144a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 4,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_145a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x05,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_145a = {
+       .pdu = timer_mgmt_response_145a,
+       .pdu_len = sizeof(timer_mgmt_response_145a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 5,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_146a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x06,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_146a = {
+       .pdu = timer_mgmt_response_146a,
+       .pdu_len = sizeof(timer_mgmt_response_146a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 6,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_147a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x07,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_147a = {
+       .pdu = timer_mgmt_response_147a,
+       .pdu_len = sizeof(timer_mgmt_response_147a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 7,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_148a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x08,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_148a = {
+       .pdu = timer_mgmt_response_148a,
+       .pdu_len = sizeof(timer_mgmt_response_148a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x02, /* Get the current value of the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 8,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_151a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x01,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_151a = {
+       .pdu = timer_mgmt_response_151a,
+       .pdu_len = sizeof(timer_mgmt_response_151a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 1,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_151b[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_151b = {
+       .pdu = timer_mgmt_response_151b,
+       .pdu_len = sizeof(timer_mgmt_response_151b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+       },
+};
+
+static const unsigned char timer_mgmt_response_152a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x02,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_152a = {
+       .pdu = timer_mgmt_response_152a,
+       .pdu_len = sizeof(timer_mgmt_response_152a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 2,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_153a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x03,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_153a = {
+       .pdu = timer_mgmt_response_153a,
+       .pdu_len = sizeof(timer_mgmt_response_153a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 3,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_154a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x04,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_154a = {
+       .pdu = timer_mgmt_response_154a,
+       .pdu_len = sizeof(timer_mgmt_response_154a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 4,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_155a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x05,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_155a = {
+       .pdu = timer_mgmt_response_155a,
+       .pdu_len = sizeof(timer_mgmt_response_155a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 5,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_156a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x06,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_156a = {
+       .pdu = timer_mgmt_response_156a,
+       .pdu_len = sizeof(timer_mgmt_response_156a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 6,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_157a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x07,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_157a = {
+       .pdu = timer_mgmt_response_157a,
+       .pdu_len = sizeof(timer_mgmt_response_157a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 7,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_158a[] = {
+       0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x08,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_158a = {
+       .pdu = timer_mgmt_response_158a,
+       .pdu_len = sizeof(timer_mgmt_response_158a),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x01, /* Deactivate the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TIMER_CONFLICT,
+               },
+               { .timer_mgmt = {
+                       .id = 8,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_163[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x03,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_163 = {
+       .pdu = timer_mgmt_response_163,
+       .pdu_len = sizeof(timer_mgmt_response_163),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 3,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_164[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x04,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_164 = {
+       .pdu = timer_mgmt_response_164,
+       .pdu_len = sizeof(timer_mgmt_response_164),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 4,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_165[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x05,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_165 = {
+       .pdu = timer_mgmt_response_165,
+       .pdu_len = sizeof(timer_mgmt_response_165),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 5,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_166[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x06,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_166 = {
+       .pdu = timer_mgmt_response_166,
+       .pdu_len = sizeof(timer_mgmt_response_166),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 6,
+               }},
+       },
+};
+
+static const unsigned char timer_mgmt_response_167[] = {
+       0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x07,
+};
+
+static const struct terminal_response_test timer_mgmt_response_data_167 = {
+       .pdu = timer_mgmt_response_167,
+       .pdu_len = sizeof(timer_mgmt_response_167),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT,
+               .qualifier = 0x00, /* Start the Timer */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .timer_mgmt = {
+                       .id = 7,
+               }},
+       },
+};
+
+static const unsigned char set_up_idle_mode_text_response_111[] = {
+       0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test
+               set_up_idle_mode_text_response_data_111 = {
+       .pdu = set_up_idle_mode_text_response_111,
+       .pdu_len = sizeof(set_up_idle_mode_text_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char set_up_idle_mode_text_response_211b[] = {
+       0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test
+               set_up_idle_mode_text_response_data_211b = {
+       .pdu = set_up_idle_mode_text_response_211b,
+       .pdu_len = sizeof(set_up_idle_mode_text_response_211b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+static const unsigned char set_up_idle_mode_text_response_241[] = {
+       0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x32,
+};
+
+static const struct terminal_response_test
+               set_up_idle_mode_text_response_data_241 = {
+       .pdu = set_up_idle_mode_text_response_241,
+       .pdu_len = sizeof(set_up_idle_mode_text_response_241),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD,
+               },
+       },
+};
+
+static const unsigned char run_at_command_response_111[] = {
+       0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00, 0xA9, 0x05, 0x2b, 0x43,
+       0x47, 0x4d, 0x49,
+};
+
+static const struct terminal_response_test run_at_command_response_data_111 = {
+       .pdu = run_at_command_response_111,
+       .pdu_len = sizeof(run_at_command_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_RUN_AT_COMMAND,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .run_at_command = {
+                       .at_response = "+CGMI",
+               }},
+       },
+};
+
+static const unsigned char run_at_command_response_211b[] = {
+       0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04, 0xA9, 0x05, 0x2b, 0x43,
+       0x47, 0x4d, 0x49,
+};
+
+static const struct terminal_response_test run_at_command_response_data_211b = {
+       .pdu = run_at_command_response_211b,
+       .pdu_len = sizeof(run_at_command_response_211b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_RUN_AT_COMMAND,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+               { .run_at_command = {
+                       .at_response = "+CGMI",
+               }},
+       },
+};
+
+static const unsigned char run_at_command_response_251[] = {
+       0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x32,
+};
+
+static const struct terminal_response_test run_at_command_response_data_251 = {
+       .pdu = run_at_command_response_251,
+       .pdu_len = sizeof(run_at_command_response_251),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_RUN_AT_COMMAND,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD,
+               },
+       },
+};
+
+static const unsigned char send_dtmf_response_111[] = {
+       0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test send_dtmf_response_data_111 = {
+       .pdu = send_dtmf_response_111,
+       .pdu_len = sizeof(send_dtmf_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_DTMF,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char send_dtmf_response_141[] = {
+       0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x20, 0x07,
+};
+
+static const struct terminal_response_test send_dtmf_response_data_141 = {
+       .pdu = send_dtmf_response_141,
+       .pdu_len = sizeof(send_dtmf_response_141),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_DTMF,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_TERMINAL_BUSY,
+                       .additional_len = 1, /* Not in speech call */
+                       .additional = (unsigned char[1]) { 0x07 },
+               },
+       },
+};
+
+static const unsigned char send_dtmf_response_211b[] = {
+       0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test send_dtmf_response_data_211b = {
+       .pdu = send_dtmf_response_211b,
+       .pdu_len = sizeof(send_dtmf_response_211b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_DTMF,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+static const unsigned char language_notification_response_111[] = {
+       0x81, 0x03, 0x01, 0x35, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test
+               language_notification_response_data_111 = {
+       .pdu = language_notification_response_111,
+       .pdu_len = sizeof(language_notification_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION,
+               .qualifier = 0x01,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char language_notification_response_121[] = {
+       0x81, 0x03, 0x01, 0x35, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test
+               language_notification_response_data_121 = {
+       .pdu = language_notification_response_121,
+       .pdu_len = sizeof(language_notification_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char launch_browser_response_111[] = {
+       0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test launch_browser_response_data_111 = {
+       .pdu = launch_browser_response_111,
+       .pdu_len = sizeof(launch_browser_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_LAUNCH_BROWSER,
+               .qualifier = 0x00, /* Launch browser, if not running */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char launch_browser_response_211[] = {
+       0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test launch_browser_response_data_211 = {
+       .pdu = launch_browser_response_211,
+       .pdu_len = sizeof(launch_browser_response_211),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_LAUNCH_BROWSER,
+               .qualifier = 0x02, /* Use the existing browser */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char launch_browser_response_221[] = {
+       0x81, 0x03, 0x01, 0x15, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x00,
+};
+
+static const struct terminal_response_test launch_browser_response_data_221 = {
+       .pdu = launch_browser_response_221,
+       .pdu_len = sizeof(launch_browser_response_221),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_LAUNCH_BROWSER,
+               .qualifier = 0x03, /* Re-start browser session */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+       },
+};
+
+static const unsigned char launch_browser_response_231[] = {
+       0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x02, 0x26, 0x02,
+};
+
+static const struct terminal_response_test launch_browser_response_data_231 = {
+       .pdu = launch_browser_response_231,
+       .pdu_len = sizeof(launch_browser_response_231),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_LAUNCH_BROWSER,
+               .qualifier = 0x00, /* Launch browser, if not running */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_BROWSER_TEMPORARY,
+                       .additional_len = 1, /* Browser unavailable */
+                       .additional = (unsigned char[1]) { 0x02 },
+               },
+       },
+};
+
+static const unsigned char launch_browser_response_411b[] = {
+       0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x83, 0x01, 0x04,
+};
+
+static const struct terminal_response_test launch_browser_response_data_411b = {
+       .pdu = launch_browser_response_411b,
+       .pdu_len = sizeof(launch_browser_response_411b),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_LAUNCH_BROWSER,
+               .qualifier = 0x02, /* Use the existing browser */
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_NO_ICON,
+               },
+       },
+};
+
+static const unsigned char open_channel_response_211[] = {
+               0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x00, 0x38, 0x02, 0x81, 0x00, 0x35, 0x07, 0x02, 0x03,
+               0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78,
+};
+
+static const struct terminal_response_test open_channel_response_data_211 = {
+       .pdu = open_channel_response_211,
+       .pdu_len = sizeof(open_channel_response_211),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_OPEN_CHANNEL,
+               .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .open_channel = {
+                       .channel = {
+                       .id = 1,
+                       .status = STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED,
+                       },
+                       .bearer_desc = {
+                                       .type = STK_BEARER_TYPE_GPRS_UTRAN,
+                                       .gprs = {
+                                               .precedence = 3,
+                                               .delay = 4,
+                                               .reliability = 3,
+                                               .peak = 4,
+                                               .mean = 31,
+                                               .pdp_type = 2,
+                                       },
+                       },
+                       .buf_size = 1400,
+               } },
+       },
+};
+
+static const unsigned char open_channel_response_271[] = {
+               0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x22, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F,
+               0x02, 0x39, 0x02, 0x05, 0x78,
+};
+
+static const struct terminal_response_test open_channel_response_data_271 = {
+       .pdu = open_channel_response_271,
+       .pdu_len = sizeof(open_channel_response_271),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_OPEN_CHANNEL,
+               .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_USER_REJECT,
+               },
+               { .open_channel = {
+                       .bearer_desc = {
+                                       .type = STK_BEARER_TYPE_GPRS_UTRAN,
+                                       .gprs = {
+                                               .precedence = 3,
+                                               .delay = 4,
+                                               .reliability = 3,
+                                               .peak = 4,
+                                               .mean = 31,
+                                               .pdp_type = 2,
+                                       },
+                       },
+                       .buf_size = 1400,
+               } },
+       },
+};
+
+static const unsigned char close_channel_response_121[] = {
+               0x81, 0x03, 0x01, 0x41, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x02, 0x3A, 0x03,
+};
+
+static const struct terminal_response_test close_channel_response_data_121 = {
+       .pdu = close_channel_response_121,
+       .pdu_len = sizeof(close_channel_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_CLOSE_CHANNEL,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_BIP_ERROR,
+                       .additional_len = 1, /* Channel identifier not valid */
+                       .additional = (unsigned char[1]) { 0x03 },
+               },
+       },
+};
+
+static const unsigned char close_channel_response_131[] = {
+               0x81, 0x03, 0x01, 0x41, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x02, 0x3A, 0x02,
+};
+
+static const struct terminal_response_test close_channel_response_data_131 = {
+       .pdu = close_channel_response_131,
+       .pdu_len = sizeof(close_channel_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_CLOSE_CHANNEL,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_BIP_ERROR,
+                       .additional_len = 1, /* Channel already closed */
+                       .additional = (unsigned char[1]) { 0x02 },
+               },
+       },
+};
+
+static const unsigned char receive_data_response_111[] = {
+               0x81, 0x03, 0x01, 0x42, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x00, 0xB6, 0x81, 0xC8, 0xc8, 0xc9, 0xca, 0xcb, 0xcc,
+               0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+               0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0,
+               0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+               0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4,
+               0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
+               0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+               0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+               0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+               0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+               0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+               0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+               0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44,
+               0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+               0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+               0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
+               0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+               0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+               0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80,
+               0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+               0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xB7, 0x01, 0xFF,
+};
+
+static const struct terminal_response_test receive_data_response_data_111 = {
+       .pdu = receive_data_response_111,
+       .pdu_len = sizeof(receive_data_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_RECEIVE_DATA,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .receive_data = {
+                               .rx_data = {
+                                       .array = (unsigned char[200]) {
+                                       0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
+                                       0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3,
+                                       0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+                                       0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+                                       0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5,
+                                       0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb,
+                                       0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
+                                       0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+                                       0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,
+                                       0xfe, 0xff, 0x00, 0x01, 0x02, 0x03,
+                                       0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+                                       0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+                                       0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+                                       0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
+                                       0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+                                       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+                                       0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+                                       0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+                                       0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+                                       0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+                                       0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+                                       0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
+                                       0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+                                       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d,
+                                       0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
+                                       0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+                                       0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+                                       0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+                                       0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b,
+                                       0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81,
+                                       0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+                                       0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
+                                       0x8e, 0x8f,
+                                       },
+                                       .len = 200,
+                               },
+                               .rx_remaining = 0xFF,
+               } },
+       },
+};
+
+static const unsigned char send_data_response_111[] = {
+               0x81, 0x03, 0x01, 0x43, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x00, 0xB7, 0x01, 0xFF,
+};
+
+static const struct terminal_response_test send_data_response_data_111 = {
+       .pdu = send_data_response_111,
+       .pdu_len = sizeof(send_data_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_DATA,
+               .qualifier = STK_SEND_DATA_IMMEDIATELY,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .send_data = {
+                               /* More than 255 bytes of space available */
+                               .tx_avail = 0xFF,
+               } },
+       },
+};
+
+static const unsigned char send_data_response_121[] = {
+               0x81, 0x03, 0x01, 0x43, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x00, 0xB7, 0x01, 0xFF,
+};
+
+static const struct terminal_response_test send_data_response_data_121 = {
+       .pdu = send_data_response_121,
+       .pdu_len = sizeof(send_data_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_DATA,
+               .qualifier = STK_SEND_DATA_STORE_DATA,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .send_data = {
+                               /* More than 255 bytes of space available */
+                               .tx_avail = 0xFF,
+               } },
+       },
+};
+
+static const unsigned char send_data_response_151[] = {
+               0x81, 0x03, 0x01, 0x43, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x02, 0x3A, 0x03,
+};
+
+static const struct terminal_response_test send_data_response_data_151 = {
+       .pdu = send_data_response_151,
+       .pdu_len = sizeof(send_data_response_151),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_SEND_DATA,
+               .qualifier = STK_SEND_DATA_IMMEDIATELY,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_BIP_ERROR,
+                       .additional_len = 1, /* Channel identifier not valid */
+                       .additional = (unsigned char[1]) { 0x03 },
+               },
+       },
+};
+
+static const unsigned char get_channel_status_response_111[] = {
+               0x81, 0x03, 0x01, 0x44, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x00, 0xB8, 0x02, 0x00, 0x00,
+};
+
+static const struct terminal_response_test
+                               get_channel_status_response_data_111 = {
+       .pdu = get_channel_status_response_111,
+       .pdu_len = sizeof(get_channel_status_response_111),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_CHANNEL_STATUS,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .channel_status = {
+                       /*
+                        * No Channel available, link not established or
+                        * PDP context not activated
+                        */
+                       .channel = {
+                               .id = 0,
+                               .status =
+                               STK_CHANNEL_PACKET_DATA_SERVICE_NOT_ACTIVATED,
+                       }
+               } },
+       },
+};
+
+static const unsigned char get_channel_status_response_121[] = {
+               0x81, 0x03, 0x01, 0x44, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x00, 0xB8, 0x02, 0x81, 0x00,
+};
+
+static const struct terminal_response_test
+                               get_channel_status_response_data_121 = {
+       .pdu = get_channel_status_response_121,
+       .pdu_len = sizeof(get_channel_status_response_121),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_CHANNEL_STATUS,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .channel_status = {
+               /* Channel 1 open, link established or PDP context activated */
+                       .channel = {
+                               .id = 1,
+                               .status =
+                               STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED,
+                       },
+               } },
+       },
+};
+
+static const unsigned char get_channel_status_response_131[] = {
+               0x81, 0x03, 0x01, 0x44, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83,
+               0x01, 0x00, 0xB8, 0x02, 0x01, 0x05,
+};
+
+static const struct terminal_response_test
+                               get_channel_status_response_data_131 = {
+       .pdu = get_channel_status_response_131,
+       .pdu_len = sizeof(get_channel_status_response_131),
+       .response = {
+               .number = 1,
+               .type = STK_COMMAND_TYPE_GET_CHANNEL_STATUS,
+               .qualifier = 0x00,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               .result = {
+                       .type = STK_RESULT_TYPE_SUCCESS,
+               },
+               { .channel_status = {
+                               /* Channel 1, link dropped */
+                               .channel = {
+                                       .id = 1,
+                                       .status = STK_CHANNEL_LINK_DROPPED,
+                               },
+               } },
+
+       },
+};
+
+struct envelope_test {
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+       struct stk_envelope envelope;
+};
+
+static void test_envelope_encoding(gconstpointer data)
+{
+       const struct envelope_test *test = data;
+       const unsigned char *pdu;
+       unsigned int pdu_len;
+
+       pdu = stk_pdu_from_envelope(&test->envelope, &pdu_len);
+
+       if (test->pdu)
+               g_assert(pdu);
+       else
+               g_assert(pdu == NULL);
+
+       g_assert(pdu_len == test->pdu_len);
+       g_assert(memcmp(pdu, test->pdu, pdu_len) == 0);
+}
+
+static const unsigned char sms_pp_data_download_161[] = {
+       0xd1, 0x2d, 0x82, 0x02, 0x83, 0x81, 0x06, 0x09,
+       0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+       0xf8, 0x8b, 0x1c, 0x04, 0x04, 0x91, 0x21, 0x43,
+       0x7f, 0x16, 0x89, 0x10, 0x10, 0x00, 0x00, 0x00,
+       0x00, 0x0d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x20,
+       0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+};
+
+static const struct envelope_test sms_pp_data_download_data_161 = {
+       .pdu = sms_pp_data_download_161,
+       .pdu_len = sizeof(sms_pp_data_download_161),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .sms_pp_download = {
+                       .address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "112233445566778",
+                       },
+                       .message = {
+                               .oaddr = {
+                                       .number_type =
+                                               SMS_NUMBER_TYPE_INTERNATIONAL,
+                                       .numbering_plan =
+                                               SMS_NUMBERING_PLAN_ISDN,
+                                       .address = "1234",
+                               },
+                               .pid = SMS_PID_TYPE_USIM_DOWNLOAD,
+                               .dcs = 0x16, /* Uncompressed, Class 2, 8-bit */
+                               .scts = {
+                                       .year = 98,
+                                       .month = 1,
+                                       .day = 1,
+                                       .has_timezone = TRUE,
+                                       .timezone = 0,
+                               },
+                               .udl = 13,
+                               .ud = "Short Message",
+                       },
+               }},
+       },
+};
+
+static const unsigned char sms_pp_data_download_162[] = {
+       0xd1, 0x2d, 0x82, 0x02, 0x83, 0x81, 0x06, 0x09,
+       0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+       0xf8, 0x8b, 0x1c, 0x04, 0x04, 0x91, 0x21, 0x43,
+       0x7f, 0xf6, 0x89, 0x10, 0x10, 0x00, 0x00, 0x00,
+       0x00, 0x0d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x20,
+       0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+};
+
+static const struct envelope_test sms_pp_data_download_data_162 = {
+       .pdu = sms_pp_data_download_162,
+       .pdu_len = sizeof(sms_pp_data_download_162),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .sms_pp_download = {
+                       .address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "112233445566778",
+                       },
+                       .message = {
+                               .oaddr = {
+                                       .number_type =
+                                               SMS_NUMBER_TYPE_INTERNATIONAL,
+                                       .numbering_plan =
+                                               SMS_NUMBERING_PLAN_ISDN,
+                                       .address = "1234",
+                               },
+                               .pid = SMS_PID_TYPE_USIM_DOWNLOAD,
+                               .dcs = 0xf6, /* Data, Class 2, 8-bit */
+                               .scts = {
+                                       .year = 98,
+                                       .month = 1,
+                                       .day = 1,
+                                       .has_timezone = TRUE,
+                                       .timezone = 0,
+                               },
+                               .udl = 13,
+                               .ud = "Short Message",
+                       },
+               }},
+       },
+};
+
+static const unsigned char sms_pp_data_download_182[] = {
+       0xd1, 0x3e, 0x82, 0x02, 0x83, 0x81, 0x06, 0x09,
+       0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+       0xf8, 0x8b, 0x2d, 0x44, 0x04, 0x91, 0x21, 0x43,
+       0x7f, 0xf6, 0x89, 0x10, 0x10, 0x00, 0x00, 0x00,
+       0x00, 0x1e, 0x02, 0x70, 0x00, 0x00, 0x19, 0x00,
+       0x0d, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+};
+
+static const struct envelope_test sms_pp_data_download_data_182 = {
+       .pdu = sms_pp_data_download_182,
+       .pdu_len = sizeof(sms_pp_data_download_182),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .sms_pp_download = {
+                       .address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "112233445566778",
+                       },
+                       .message = {
+                               .udhi = TRUE,
+                               .oaddr = {
+                                       .number_type =
+                                               SMS_NUMBER_TYPE_INTERNATIONAL,
+                                       .numbering_plan =
+                                               SMS_NUMBERING_PLAN_ISDN,
+                                       .address = "1234",
+                               },
+                               .pid = SMS_PID_TYPE_USIM_DOWNLOAD,
+                               .dcs = 0xf6, /* Data, Class 2, 8-bit */
+                               .scts = {
+                                       .year = 98,
+                                       .month = 1,
+                                       .day = 1,
+                                       .has_timezone = TRUE,
+                                       .timezone = 0,
+                               },
+                               .udl = 30,
+                               .ud = {
+                                       0x02, 0x70, 0x00, 0x00, 0x19, 0x00,
+                                       0x0d, 0x00, 0x00, 0x00, 0x00, 0xbf,
+                                       0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x01, 0x00, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                               },
+                       },
+               }},
+       },
+};
+
+static const unsigned char cbs_pp_data_download_11[] = {
+       0xd2, 0x5e, 0x82, 0x02, 0x83, 0x81, 0x8c, 0x58,
+       0xc0, 0x11, 0x10, 0x01, 0x01, 0x11, 0xc3, 0x32,
+       0x9b, 0x0d, 0x12, 0xca, 0xdf, 0x61, 0xf2, 0x38,
+       0x3c, 0xa7, 0x83, 0x40, 0x20, 0x10, 0x08, 0x04,
+       0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02,
+       0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81,
+       0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40,
+       0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20,
+       0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10,
+       0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08,
+       0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04,
+       0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02,
+};
+
+static const struct envelope_test cbs_pp_data_download_data_11 = {
+       .pdu = cbs_pp_data_download_11,
+       .pdu_len = sizeof(cbs_pp_data_download_11),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .cbs_pp_download = {
+                       .page = {
+                               .gs = CBS_GEO_SCOPE_CELL_NORMAL,
+                               .message_code = 1,
+                               .update_number = 1,
+                               .message_identifier = 0x1001,
+                               .dcs = CBS_LANGUAGE_ENGLISH, /* GSM 7-bit */
+                               .max_pages = 1,
+                               .page = 1,
+                               .ud = {
+                                       /* 7-bit "Cell Broadcast " repeated */
+                                       0xc3, 0x32, 0x9b, 0x0d, 0x12, 0xca,
+                                       0xdf, 0x61, 0xf2, 0x38, 0x3c, 0xa7,
+                                       0x83, 0x40, 0x20, 0x10, 0x08, 0x04,
+                                       0x02, 0x81, 0x40, 0x20, 0x10, 0x08,
+                                       0x04, 0x02, 0x81, 0x40, 0x20, 0x10,
+                                       0x08, 0x04, 0x02, 0x81, 0x40, 0x20,
+                                       0x10, 0x08, 0x04, 0x02, 0x81, 0x40,
+                                       0x20, 0x10, 0x08, 0x04, 0x02, 0x81,
+                                       0x40, 0x20, 0x10, 0x08, 0x04, 0x02,
+                                       0x81, 0x40, 0x20, 0x10, 0x08, 0x04,
+                                       0x02, 0x81, 0x40, 0x20, 0x10, 0x08,
+                                       0x04, 0x02, 0x81, 0x40, 0x20, 0x10,
+                                       0x08, 0x04, 0x02, 0x81, 0x40, 0x20,
+                                       0x10, 0x08, 0x04, 0x02,
+                               },
+                       },
+               }},
+       },
+};
+
+static const unsigned char cbs_pp_data_download_17[] = {
+       0xd2, 0x5e, 0x82, 0x02, 0x83, 0x81, 0x8c, 0x58,
+       0xc0, 0x11, 0x10, 0x01, 0x96, 0x11, 0x02, 0x70,
+       0x00, 0x00, 0x4d, 0x00, 0x0d, 0x00, 0x00, 0x00,
+       0x00, 0xbf, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x01, 0x00, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+};
+
+static const struct envelope_test cbs_pp_data_download_data_17 = {
+       .pdu = cbs_pp_data_download_17,
+       .pdu_len = sizeof(cbs_pp_data_download_17),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .cbs_pp_download = {
+                       .page = {
+                               .gs = CBS_GEO_SCOPE_CELL_NORMAL,
+                               .message_code = 1,
+                               .update_number = 1,
+                               .message_identifier = 0x1001,
+                               .dcs = SMS_CLASS_2 | (SMS_CHARSET_8BIT << 2) |
+                                       (9 << 4), /* UDHI present */
+                               .max_pages = 1,
+                               .page = 1,
+                               .ud = {
+                                       /* Secured User Header */
+                                       0x02, 0x70, 0x00, 0x00, 0x4d, 0x00,
+                                       0x0d, 0x00, 0x00, 0x00, 0x00, 0xbf,
+                                       0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x01, 0x00, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+                                       0xdc, 0xdc, 0xdc, 0xdc,
+                               },
+                       },
+               }},
+       },
+};
+
+static const unsigned char menu_selection_111[] = {
+       0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0x02,
+};
+
+static const struct envelope_test menu_selection_data_111 = {
+       .pdu = menu_selection_111,
+       .pdu_len = sizeof(menu_selection_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0x2,
+               }},
+       },
+};
+
+static const unsigned char menu_selection_112[] = {
+       0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0x12,
+};
+
+static const struct envelope_test menu_selection_data_112 = {
+       .pdu = menu_selection_112,
+       .pdu_len = sizeof(menu_selection_112),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0x12,
+               }},
+       },
+};
+
+static const unsigned char menu_selection_121[] = {
+       0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0x3d,
+};
+
+static const struct envelope_test menu_selection_data_121 = {
+       .pdu = menu_selection_121,
+       .pdu_len = sizeof(menu_selection_121),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0x3d,
+               }},
+       },
+};
+
+static const unsigned char menu_selection_122[] = {
+       0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0xfb,
+};
+
+static const struct envelope_test menu_selection_data_122 = {
+       .pdu = menu_selection_122,
+       .pdu_len = sizeof(menu_selection_122),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0xfb,
+               }},
+       },
+};
+
+static const unsigned char menu_selection_123[] = {
+       0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0x01,
+};
+
+static const struct envelope_test menu_selection_data_123 = {
+       .pdu = menu_selection_123,
+       .pdu_len = sizeof(menu_selection_123),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0x1,
+               }},
+       },
+};
+
+static const unsigned char menu_selection_211[] = {
+       0xd3, 0x09, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0x02, 0x15, 0x00,
+};
+
+static const struct envelope_test menu_selection_data_211 = {
+       .pdu = menu_selection_211,
+       .pdu_len = sizeof(menu_selection_211),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0x2,
+                       .help_request = TRUE,
+               }},
+       },
+};
+
+static const unsigned char menu_selection_612[] = {
+       0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0x05,
+};
+
+static const struct envelope_test menu_selection_data_612 = {
+       .pdu = menu_selection_612,
+       .pdu_len = sizeof(menu_selection_612),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0x5,
+               }},
+       },
+};
+
+static const unsigned char menu_selection_641[] = {
+       0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01,
+       0x08,
+};
+
+static const struct envelope_test menu_selection_data_641 = {
+       .pdu = menu_selection_641,
+       .pdu_len = sizeof(menu_selection_641),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MENU_SELECTION,
+               .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .menu_selection = {
+                       .item_id = 0x8,
+               }},
+       },
+};
+
+static const unsigned char call_control_111a[] = {
+       0xd4, 0x25, 0x82, 0x02, 0x82, 0x81, 0x86, 0x0b,
+       0x91, 0x10, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32,
+       0x54, 0x76, 0x98, 0x07, 0x07, 0x06, 0x60, 0x04,
+       0x02, 0x00, 0x05, 0x81, 0x13, 0x09, 0x00, 0xf1,
+       0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+};
+
+static const struct envelope_test call_control_data_111a = {
+       .pdu = call_control_111a,
+       .pdu_len = sizeof(call_control_111a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_CALL_CONTROL,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .call_control = {
+                       .type = STK_CC_TYPE_CALL_SETUP,
+                       { .address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "01234567890123456789",
+                       }},
+                       .ccp1 = {
+                               .ccp = {
+                                       0x60, 0x04, 0x02, 0x00, 0x05, 0x81,
+                               },
+                               .len = 6,
+                       },
+                       .location = {
+                               .mcc = "001",
+                               .mnc = "01",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                               .has_ext_ci = TRUE,
+                               .ext_ci = 0x0001,
+                       },
+               }},
+       },
+};
+
+static const unsigned char call_control_111b[] = {
+       0xd4, 0x23, 0x82, 0x02, 0x82, 0x81, 0x86, 0x0b,
+       0x91, 0x10, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32,
+       0x54, 0x76, 0x98, 0x07, 0x07, 0x06, 0x60, 0x04,
+       0x02, 0x00, 0x05, 0x81, 0x13, 0x07, 0x00, 0x11,
+       0x10, 0x00, 0x01, 0x00, 0x01,
+};
+
+static const struct envelope_test call_control_data_111b = {
+       .pdu = call_control_111b,
+       .pdu_len = sizeof(call_control_111b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_CALL_CONTROL,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .call_control = {
+                       .type = STK_CC_TYPE_CALL_SETUP,
+                       { .address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "01234567890123456789",
+                       }},
+                       .ccp1 = {
+                               .ccp = {
+                                       0x60, 0x04, 0x02, 0x00, 0x05, 0x81,
+                               },
+                               .len = 6,
+                       },
+                       .location = {
+                               .mcc = "001",
+                               .mnc = "011",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                       },
+               }},
+       },
+};
+
+static const unsigned char call_control_131a[] = {
+       0xd4, 0x18, 0x82, 0x02, 0x82, 0x81, 0x86, 0x07,
+       0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x13,
+       0x09, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x01,
+       0x00, 0x01,
+       /*
+        * Byte 3 changed to 0x82 and byte 7 changed to 0x86 (Comprehension
+        * Required should be set according to TS 102 223 7.3.1.6)
+        */
+};
+
+static const struct envelope_test call_control_data_131a = {
+       .pdu = call_control_131a,
+       .pdu_len = sizeof(call_control_131a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_CALL_CONTROL,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .call_control = {
+                       .type = STK_CC_TYPE_CALL_SETUP,
+                       { .address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "012340123456",
+                       }},
+                       .location = {
+                               .mcc = "001",
+                               .mnc = "01",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                               .has_ext_ci = TRUE,
+                               .ext_ci = 0x0001,
+                       },
+               }},
+       },
+};
+
+static const unsigned char call_control_131b[] = {
+       0xd4, 0x16, 0x82, 0x02, 0x82, 0x81, 0x86, 0x07,
+       0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x13,
+       0x07, 0x00, 0x11, 0x10, 0x00, 0x01, 0x00, 0x01,
+       /*
+        * Byte 3 changed to 0x82 and byte 7 changed to 0x86 (Comprehension
+        * Required should be set according to TS 102 223 7.3.1.6)
+        */
+};
+
+static const struct envelope_test call_control_data_131b = {
+       .pdu = call_control_131b,
+       .pdu_len = sizeof(call_control_131b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_CALL_CONTROL,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .call_control = {
+                       .type = STK_CC_TYPE_CALL_SETUP,
+                       { .address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "012340123456",
+                       }},
+                       .location = {
+                               .mcc = "001",
+                               .mnc = "011",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                       },
+               }},
+       },
+};
+
+static const unsigned char mo_short_message_control_111a[] = {
+       0xd5, 0x22, 0x02, 0x02, 0x82, 0x81, 0x06, 0x09,
+       0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+       0xf8, 0x06, 0x06, 0x91, 0x10, 0x32, 0x54, 0x76,
+       0xf8, 0x13, 0x09, 0x00, 0xf1, 0x10, 0x00, 0x01,
+       0x00, 0x01, 0x00, 0x01,
+};
+
+static const struct envelope_test mo_short_message_control_data_111a = {
+       .pdu = mo_short_message_control_111a,
+       .pdu_len = sizeof(mo_short_message_control_111a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MO_SMS_CONTROL,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .sms_mo_control = {
+                       .sc_address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "112233445566778",
+                       },
+                       .dest_address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "012345678",
+                       },
+                       .location = {
+                               .mcc = "001",
+                               .mnc = "01",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                               .has_ext_ci = TRUE,
+                               .ext_ci = 0x0001,
+                       },
+               }},
+       },
+};
+
+static const unsigned char mo_short_message_control_111b[] = {
+       0xd5, 0x20, 0x02, 0x02, 0x82, 0x81, 0x06, 0x09,
+       0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+       0xf8, 0x06, 0x06, 0x91, 0x10, 0x32, 0x54, 0x76,
+       0xf8, 0x13, 0x07, 0x00, 0x11, 0x10, 0x00, 0x01,
+       0x00, 0x01,
+};
+
+static const struct envelope_test mo_short_message_control_data_111b = {
+       .pdu = mo_short_message_control_111b,
+       .pdu_len = sizeof(mo_short_message_control_111b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_MO_SMS_CONTROL,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .sms_mo_control = {
+                       .sc_address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "112233445566778",
+                       },
+                       .dest_address = {
+                               .ton_npi = 0x91, /* Intl, ISDN */
+                               .number = "012345678",
+                       },
+                       .location = {
+                               .mcc = "001",
+                               .mnc = "011",
+                               .lac_tac = 0x0001,
+                               .has_ci = TRUE,
+                               .ci = 0x0001,
+                       },
+               }},
+       },
+};
+
+static const unsigned char event_download_mt_call_111[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x00, 0x82, 0x02, 0x83,
+       0x81, 0x9c, 0x01, 0x00,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension
+        * Required should be set according to TS 102 223 7.5.1.2)
+        */
+};
+
+static const struct envelope_test event_download_mt_call_data_111 = {
+       .pdu = event_download_mt_call_111,
+       .pdu_len = sizeof(event_download_mt_call_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_MT_CALL,
+                       { .mt_call = {
+                               .transaction_id = 0,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_mt_call_112[] = {
+       0xd6, 0x0f, 0x99, 0x01, 0x00, 0x82, 0x02, 0x83,
+       0x81, 0x9c, 0x01, 0x00, 0x06, 0x03, 0x81, 0x89,
+       0x67,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to
+        * 0x06 (Comprehension Required should be set according to
+        * TS 102 223 7.5.1.2)
+        */
+};
+
+static const struct envelope_test event_download_mt_call_data_112 = {
+       .pdu = event_download_mt_call_112,
+       .pdu_len = sizeof(event_download_mt_call_112),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_MT_CALL,
+                       { .mt_call = {
+                               .transaction_id = 0,
+                               .caller_address = {
+                                       .ton_npi = 0x81, /* Unknown, ISDN */
+                                       .number = "9876",
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_connected_111[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x01, 0x82, 0x02, 0x82,
+       0x81, 0x9c, 0x01, 0x80,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension
+        * Required should be set according to TS 102 223 7.5.2.2)
+        */
+};
+
+static const struct envelope_test event_download_call_connected_data_111 = {
+       .pdu = event_download_call_connected_111,
+       .pdu_len = sizeof(event_download_call_connected_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_CONNECTED,
+                       { .call_connected = {
+                               .transaction_id = 0x80,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_connected_112[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x01, 0x82, 0x02, 0x83,
+       0x81, 0x9c, 0x01, 0x80,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension
+        * Required should be set according to TS 102 223 7.5.2.2)
+        */
+};
+
+static const struct envelope_test event_download_call_connected_data_112 = {
+       .pdu = event_download_call_connected_112,
+       .pdu_len = sizeof(event_download_call_connected_112),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_CONNECTED,
+                       { .call_connected = {
+                               .transaction_id = 0x80,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_111[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x02, 0x82, 0x02, 0x83,
+       0x81, 0x9c, 0x01, 0x80,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension
+        * Required should be set according to TS 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test event_download_call_disconnected_data_111 = {
+       .pdu = event_download_call_disconnected_111,
+       .pdu_len = sizeof(event_download_call_disconnected_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0x80 },
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_112a[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x9c, 0x01, 0x80,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension
+        * Required should be set according to TS 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_call_disconnected_data_112a = {
+       .pdu = event_download_call_disconnected_112a,
+       .pdu_len = sizeof(event_download_call_disconnected_112a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0x80 },
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_112b[] = {
+       0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x9c, 0x01, 0x80, 0x1a, 0x02, 0x60, 0x90,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to
+        * 1a (Comprehension Required should be set according to TS
+        * 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_call_disconnected_data_112b = {
+       .pdu = event_download_call_disconnected_112b,
+       .pdu_len = sizeof(event_download_call_disconnected_112b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0x80 },
+                               },
+                               .cause = {
+                                       .has_cause = TRUE,
+                                       .len = 2,
+                                       /* Normal call clearing */
+                                       .cause = { 0x60, 0x90 },
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_112c[] = {
+       0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x9c, 0x01, 0x80, 0x1a, 0x02, 0xe0, 0x90,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to
+        * 1a (Comprehension Required should be set according to TS
+        * 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_call_disconnected_data_112c = {
+       .pdu = event_download_call_disconnected_112c,
+       .pdu_len = sizeof(event_download_call_disconnected_112c),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0x80 },
+                               },
+                               .cause = {
+                                       .has_cause = TRUE,
+                                       .len = 2,
+                                       /* Normal call clearing */
+                                       .cause = { 0xe0, 0x90 },
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_113a[] = {
+       0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x83,
+       0x81, 0x9c, 0x01, 0x00, 0x1a, 0x02, 0x60, 0x90,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to
+        * 1a (Comprehension Required should be set according to TS
+        * 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_call_disconnected_data_113a = {
+       .pdu = event_download_call_disconnected_113a,
+       .pdu_len = sizeof(event_download_call_disconnected_113a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0 },
+                               },
+                               .cause = {
+                                       .has_cause = TRUE,
+                                       .len = 2,
+                                       /* Normal call clearing */
+                                       .cause = { 0x60, 0x90 },
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_113b[] = {
+       0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x83,
+       0x81, 0x9c, 0x01, 0x00, 0x1a, 0x02, 0xe0, 0x90,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to
+        * 1a (Comprehension Required should be set according to TS
+        * 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_call_disconnected_data_113b = {
+       .pdu = event_download_call_disconnected_113b,
+       .pdu_len = sizeof(event_download_call_disconnected_113b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0 },
+                               },
+                               .cause = {
+                                       .has_cause = TRUE,
+                                       .len = 2,
+                                       /* Normal call clearing */
+                                       .cause = { 0xe0, 0x90 },
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_114a[] = {
+       0xd6, 0x0c, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x9c, 0x01, 0x80, 0x1a, 0x00,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to
+        * 1a (Comprehension Required should be set according to TS
+        * 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_call_disconnected_data_114a = {
+       .pdu = event_download_call_disconnected_114a,
+       .pdu_len = sizeof(event_download_call_disconnected_114a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0x80 },
+                               },
+                               .cause = {
+                                       .has_cause = TRUE,
+                                       /* Radio link failure */
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_call_disconnected_114b[] = {
+       0xd6, 0x0c, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82,
+       0x81, 0x9c, 0x01, 0x00, 0x1a, 0x00,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to
+        * 1a (Comprehension Required should be set according to TS
+        * 102 223 7.5.3.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_call_disconnected_data_114b = {
+       .pdu = event_download_call_disconnected_114b,
+       .pdu_len = sizeof(event_download_call_disconnected_114b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CALL_DISCONNECTED,
+                       { .call_disconnected = {
+                               .transaction_ids = {
+                                       .len = 1,
+                                       .list = { 0 },
+                               },
+                               .cause = {
+                                       .has_cause = TRUE,
+                                       /* Radio link failure */
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_location_status_111[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x9b, 0x01, 0x02,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension
+        * Required should be set according to TS 102 223 7.5.4.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_location_status_data_111 = {
+       .pdu = event_download_location_status_111,
+       .pdu_len = sizeof(event_download_location_status_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_LOCATION_STATUS,
+                       { .location_status = {
+                               .state = STK_NO_SERVICE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_location_status_112a[] = {
+       0xd6, 0x15, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x9b, 0x01, 0x00, 0x13, 0x09, 0x00, 0xf1,
+       0x10, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension
+        * Required should be set according to TS 102 223 7.5.4.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_location_status_data_112a = {
+       .pdu = event_download_location_status_112a,
+       .pdu_len = sizeof(event_download_location_status_112a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_LOCATION_STATUS,
+                       { .location_status = {
+                               .state = STK_NORMAL_SERVICE,
+                               .info = {
+                                       .mcc = "001",
+                                       .mnc = "01",
+                                       .lac_tac = 0x0002,
+                                       .has_ci = TRUE,
+                                       .ci = 0x0002,
+                                       .has_ext_ci = TRUE,
+                                       .ext_ci = 0x0001,
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_location_status_112b[] = {
+       0xd6, 0x13, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x9b, 0x01, 0x00, 0x13, 0x07, 0x00, 0x11,
+       0x10, 0x00, 0x02, 0x00, 0x02,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension
+        * Required should be set according to TS 102 223 7.5.4.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_location_status_data_112b = {
+       .pdu = event_download_location_status_112b,
+       .pdu_len = sizeof(event_download_location_status_112b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_LOCATION_STATUS,
+                       { .location_status = {
+                               .state = STK_NORMAL_SERVICE,
+                               .info = {
+                                       .mcc = "001",
+                                       .mnc = "011",
+                                       .lac_tac = 0x0002,
+                                       .has_ci = TRUE,
+                                       .ci = 0x0002,
+                               },
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_location_status_122[] = {
+       0xd6, 0x15, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82,
+       0x81, 0x9b, 0x01, 0x00, 0x13, 0x09, 0x00, 0xf1,
+       0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2f,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension
+        * Required should be set according to TS 102 223 7.5.4.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_location_status_data_122 = {
+       .pdu = event_download_location_status_122,
+       .pdu_len = sizeof(event_download_location_status_122),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_LOCATION_STATUS,
+                       { .location_status = {
+                               .state = STK_NORMAL_SERVICE,
+                               .info = {
+                                       .mcc = "001",
+                                       .mnc = "01",
+                                       .lac_tac = 0x0002,
+                                       .has_eutran_ci = TRUE,
+                                       .eutran_ci = 0x0000002,
+                               },
+                       }},
+               }},
+       },
+};
+
+/*
+ * This is from 27.22.7.5.  The ENVELOPE given in 27.22.4.16.1.1 seems to
+ * have invalid length value (2nd byte), but in turn the Comprehension
+ * Required bit is set correctly..
+ */
+static const unsigned char event_download_user_activity_111[] = {
+       0xd6, 0x07, 0x99, 0x01, 0x04, 0x82, 0x02, 0x82,
+       0x81,
+       /*
+        * Byte 3 changed to 0x99 (Comprehension Required should be
+        * set according to TS 102 223 7.5.5.2)
+        */
+};
+
+static const struct envelope_test event_download_user_activity_data_111 = {
+       .pdu = event_download_user_activity_111,
+       .pdu_len = sizeof(event_download_user_activity_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_USER_ACTIVITY,
+               }},
+       },
+};
+
+static const unsigned char event_download_idle_screen_available_111[] = {
+       0xd6, 0x07, 0x99, 0x01, 0x05, 0x82, 0x02, 0x02,
+       0x81,
+       /*
+        * Byte 3 changed to 0x99 (Comprehension Required should be
+        * set according to TS 102 223 7.5.6.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_idle_screen_available_data_111 = {
+       .pdu = event_download_idle_screen_available_111,
+       .pdu_len = sizeof(event_download_idle_screen_available_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_DISPLAY,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE,
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_111a[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x79,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_111a = {
+       .pdu = event_download_card_reader_status_111a,
+       .pdu_len = sizeof(event_download_card_reader_status_111a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = TRUE,
+                               .present = TRUE,
+                               .id1_size = TRUE,
+                               .card_present = TRUE,
+                               .card_powered = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_111b[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x59,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_111b = {
+       .pdu = event_download_card_reader_status_111b,
+       .pdu_len = sizeof(event_download_card_reader_status_111b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = TRUE,
+                               .present = TRUE,
+                               .id1_size = FALSE,
+                               .card_present = TRUE,
+                               .card_powered = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_111c[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x71,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_111c = {
+       .pdu = event_download_card_reader_status_111c,
+       .pdu_len = sizeof(event_download_card_reader_status_111c),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = FALSE,
+                               .present = TRUE,
+                               .id1_size = TRUE,
+                               .card_present = TRUE,
+                               .card_powered = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_111d[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x51,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_111d = {
+       .pdu = event_download_card_reader_status_111d,
+       .pdu_len = sizeof(event_download_card_reader_status_111d),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = FALSE,
+                               .present = TRUE,
+                               .id1_size = FALSE,
+                               .card_present = TRUE,
+                               .card_powered = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_112a[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x39,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_112a = {
+       .pdu = event_download_card_reader_status_112a,
+       .pdu_len = sizeof(event_download_card_reader_status_112a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = TRUE,
+                               .present = TRUE,
+                               .id1_size = TRUE,
+                               .card_present = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_112b[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x19,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_112b = {
+       .pdu = event_download_card_reader_status_112b,
+       .pdu_len = sizeof(event_download_card_reader_status_112b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = TRUE,
+                               .present = TRUE,
+                               .id1_size = FALSE,
+                               .card_present = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_112c[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x31,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_112c = {
+       .pdu = event_download_card_reader_status_112c,
+       .pdu_len = sizeof(event_download_card_reader_status_112c),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = FALSE,
+                               .present = TRUE,
+                               .id1_size = TRUE,
+                               .card_present = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_112d[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x11,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_112d = {
+       .pdu = event_download_card_reader_status_112d,
+       .pdu_len = sizeof(event_download_card_reader_status_112d),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = FALSE,
+                               .present = TRUE,
+                               .id1_size = FALSE,
+                               .card_present = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_212a[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x29,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_212a = {
+       .pdu = event_download_card_reader_status_212a,
+       .pdu_len = sizeof(event_download_card_reader_status_212a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = TRUE,
+                               .present = FALSE,
+                               .id1_size = TRUE,
+                               .card_present = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_card_reader_status_212b[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82,
+       0x81, 0xa0, 0x01, 0x09,
+};
+
+static const struct envelope_test
+               event_download_card_reader_status_data_212b = {
+       .pdu = event_download_card_reader_status_212b,
+       .pdu_len = sizeof(event_download_card_reader_status_212b),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CARD_READER_STATUS,
+                       { .card_reader_status = {
+                               .id = 1,
+                               .removable = TRUE,
+                               .present = FALSE,
+                               .id1_size = FALSE,
+                               .card_present = FALSE,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_language_selection_111[] = {
+       0xd6, 0x0b, 0x99, 0x01, 0x07, 0x82, 0x02, 0x82,
+       0x81, 0xad, 0x02, 0x64, 0x65,
+       /*
+        * Byte 3 changed to 0x99 and byte 10 to 0xad (Comprehension
+        * Required should be set according to TS 102 223 7.5.8.2)
+        */
+};
+
+static const struct envelope_test
+               event_download_language_selection_data_111 = {
+       .pdu = event_download_language_selection_111,
+       .pdu_len = sizeof(event_download_language_selection_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_LANGUAGE_SELECTION,
+                       { .language_selection = "de" },
+               }},
+       },
+};
+
+static const unsigned char event_download_language_selection_122[] = {
+       0xd6, 0x0b, 0x99, 0x01, 0x07, 0x82, 0x02, 0x82,
+       0x81, 0xad, 0x02, 0x73, 0x65,
+       /* Byte 5 changed to 0x07 (Event: Language Selection) */
+       /* Byte 8 changed to 0x82 (Source device: Terminal) */
+       /* Removed the (unexpected?) Transaction ID data object (0x2d) */
+};
+
+static const struct envelope_test
+               event_download_language_selection_data_122 = {
+       .pdu = event_download_language_selection_122,
+       .pdu_len = sizeof(event_download_language_selection_122),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_LANGUAGE_SELECTION,
+                       { .language_selection = "se" },
+               }},
+       },
+};
+
+static const unsigned char event_download_browser_termination_111[] = {
+       0xd6, 0x0a, 0x99, 0x01, 0x08, 0x82, 0x02, 0x82,
+       0x81, 0xb4, 0x01, 0x00,
+};
+
+static const struct envelope_test
+               event_download_browser_termination_data_111 = {
+       .pdu = event_download_browser_termination_111,
+       .pdu_len = sizeof(event_download_browser_termination_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_BROWSER_TERMINATION,
+                       { .browser_termination = {
+                               .cause = STK_BROWSER_USER_TERMINATION,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_data_available_111[] = {
+       0xd6, 0x0e, 0x99, 0x01, 0x09, 0x82, 0x02, 0x82,
+       0x81, 0xb8, 0x02, 0x81, 0x00, 0xb7, 0x01, 0xff,
+};
+
+static const struct envelope_test event_download_data_available_data_111 = {
+       .pdu = event_download_data_available_111,
+       .pdu_len = sizeof(event_download_data_available_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_DATA_AVAILABLE,
+                       { .data_available = {
+                               /* Channel 1 open, Link established */
+                               .channel = {
+                               .id = 1,
+                               .status =
+                               STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED,
+                               },
+                               .channel_data_len = 255,
+                       } },
+               } },
+       },
+};
+
+static const unsigned char event_download_data_available_211[] = {
+       0xd6, 0x0e, 0x99, 0x01, 0x09, 0x82, 0x02, 0x82,
+       0x81, 0xb8, 0x02, 0x81, 0x00, 0xb7, 0x01, 0xff,
+};
+
+static const struct envelope_test event_download_data_available_data_211 = {
+       .pdu = event_download_data_available_211,
+       .pdu_len = sizeof(event_download_data_available_211),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_DATA_AVAILABLE,
+                       { .data_available = {
+                               /* Channel 1 open, Link established */
+                               .channel = {
+                               .id = 1,
+                               .status =
+                               STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED,
+                               },
+                               .channel_data_len = 255,
+                       } },
+               } },
+       },
+};
+
+static const unsigned char event_download_channel_status_131[] = {
+       0xd6, 0x0b, 0x99, 0x01, 0x0a, 0x82, 0x02, 0x82,
+       0x81, 0xb8, 0x02, 0x01, 0x05,
+};
+
+static const struct envelope_test event_download_channel_status_data_131 = {
+       .pdu = event_download_channel_status_131,
+       .pdu_len = sizeof(event_download_channel_status_131),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CHANNEL_STATUS,
+                       { .channel_status = {
+                               /* Channel 1, Link dropped */
+                               .channel = {
+                                       .id = 1,
+                                       .status = STK_CHANNEL_LINK_DROPPED,
+                               },
+                       } },
+               } },
+       },
+};
+
+static const unsigned char event_download_channel_status_211[] = {
+       0xd6, 0x0b, 0x99, 0x01, 0x0a, 0x82, 0x02, 0x82,
+       0x81, 0xb8, 0x02, 0x41, 0x00,
+       /*
+        * Byte 10 changed to 0xb8 (Comprehension Required should be
+        * set according to TS 102 223 7.5.11.2)
+        */
+};
+
+static const struct envelope_test event_download_channel_status_data_211 = {
+       .pdu = event_download_channel_status_211,
+       .pdu_len = sizeof(event_download_channel_status_211),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CHANNEL_STATUS,
+                       { .channel_status = {
+                               /* Channel 1, TCP in LISTEN state */
+                               .channel = {
+                               .id = 1,
+                               .status = STK_CHANNEL_TCP_IN_LISTEN_STATE,
+                               },
+                       } },
+               } },
+       },
+};
+
+static const unsigned char event_download_channel_status_221[] = {
+       0xd6, 0x0b, 0x99, 0x01, 0x0a, 0x82, 0x02, 0x82,
+       0x81, 0xb8, 0x02, 0x81, 0x00,
+       /*
+        * Byte 10 changed to 0xb8 (Comprehension Required should be
+        * set according to TS 102 223 7.5.11.2)
+        */
+};
+
+static const struct envelope_test event_download_channel_status_data_221 = {
+       .pdu = event_download_channel_status_221,
+       .pdu_len = sizeof(event_download_channel_status_221),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_CHANNEL_STATUS,
+                       { .channel_status = {
+                               /* Channel 1 open, TCP Link established */
+                               .channel = {
+                               .id = 1,
+                               .status =
+                               STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED,
+                               },
+                       } },
+               } },
+       },
+};
+
+static const unsigned char event_download_network_rejection_111[] = {
+       0xd6, 0x17, 0x99, 0x01, 0x12, 0x82, 0x02, 0x83,
+       0x81, 0x7d, 0x05, 0x00, 0xf1, 0x10, 0x00, 0x01,
+       0xbf, 0x01, 0x08, 0xf4, 0x01, 0x09, 0xf5, 0x01,
+       0x0b,
+       /*
+        * Byte 3 changed to 99, byte 17 changed to bf, byte 19 to f4 and
+        * byte 22 to f5 (Comprehension Required should be set according
+        * to TS 131 111 7.5.2.2)
+        */
+};
+
+static const struct envelope_test event_download_network_rejection_data_111 = {
+       .pdu = event_download_network_rejection_111,
+       .pdu_len = sizeof(event_download_network_rejection_111),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_NETWORK_REJECTION,
+                       { .network_rejection = {
+                               .tai = {
+                                       .mcc = "001",
+                                       .mnc = "01",
+                                       .tac = 0x0001,
+                               },
+                               .access_tech = STK_ACCESS_TECHNOLOGY_EUTRAN,
+                               .update_attach = STK_UPDATE_ATTACH_EPS_ATTACH,
+                               .cause = STK_CAUSE_EMM_PLMN_NOT_ALLOWED,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char event_download_network_rejection_121[] = {
+       0xd6, 0x17, 0x99, 0x01, 0x12, 0x82, 0x02, 0x83,
+       0x81, 0x7d, 0x05, 0x00, 0xf1, 0x10, 0x00, 0x01,
+       0xbf, 0x01, 0x08, 0xf4, 0x01, 0x0b, 0xf5, 0x01,
+       0x0c,
+       /*
+        * Byte 3 changed to 99, byte 17 changed to bf, byte 19 to f4 and
+        * byte 22 to f5 (Comprehension Required should be set according
+        * to TS 131 111 7.5.2.2)
+        */
+};
+
+static const struct envelope_test event_download_network_rejection_data_121 = {
+       .pdu = event_download_network_rejection_121,
+       .pdu_len = sizeof(event_download_network_rejection_121),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD,
+               .src = STK_DEVICE_IDENTITY_TYPE_NETWORK,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .event_download = {
+                       .type = STK_EVENT_TYPE_NETWORK_REJECTION,
+                       { .network_rejection = {
+                               .tai = {
+                                       .mcc = "001",
+                                       .mnc = "01",
+                                       .tac = 0x0001,
+                               },
+                               .access_tech = STK_ACCESS_TECHNOLOGY_EUTRAN,
+                               .update_attach = STK_UPDATE_ATTACH_TA_UPDATING,
+                               .cause = STK_CAUSE_EMM_TAC_NOT_ALLOWED,
+                       }},
+               }},
+       },
+};
+
+static const unsigned char timer_expiration_211[] = {
+       0xd7, 0x0c, 0x82, 0x02, 0x82, 0x81, 0xa4, 0x01,
+       0x01, 0xa5, 0x03, 0x00, 0x00, 0x01,
+};
+
+static const struct envelope_test timer_expiration_data_211 = {
+       .pdu = timer_expiration_211,
+       .pdu_len = sizeof(timer_expiration_211),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_TIMER_EXPIRATION,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .timer_expiration = {
+                       .id = 1,
+                       .value = {
+                               .second = 10,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+static const unsigned char timer_expiration_221a[] = {
+       0xd7, 0x0c, 0x82, 0x02, 0x82, 0x81, 0xa4, 0x01,
+       0x01, 0xa5, 0x03, 0x00, 0x00, 0x03,
+};
+
+static const struct envelope_test timer_expiration_data_221a = {
+       .pdu = timer_expiration_221a,
+       .pdu_len = sizeof(timer_expiration_221a),
+       .envelope = {
+               .type = STK_ENVELOPE_TYPE_TIMER_EXPIRATION,
+               .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL,
+               .dst = STK_DEVICE_IDENTITY_TYPE_UICC,
+               { .timer_expiration = {
+                       .id = 1,
+                       .value = {
+                               .second = 30,
+                               .has_value = TRUE,
+                       },
+               }},
+       },
+};
+
+struct html_attr_test {
+       char *text;
+       struct stk_text_attribute text_attr;
+       char *html;
+};
+
+static struct html_attr_test html_attr_data_1 = {
+       .text = "Blue green green green",
+       .text_attr = {
+               .len = 8,
+               .attributes = { 0x00, 0x00, 0x03, 0x94, 0x00, 0x04, 0x03,
+                               0x96 },
+       },
+       .html = "<span style=\"color: #0000A0;background-color: #FFFFFF;\">"
+               "Blue</span><span style=\"color: #347235;background-color: "
+               "#FFFFFF;\"> green green green</span>",
+};
+
+static struct html_attr_test html_attr_data_2 = {
+       .text = "abc",
+       .text_attr = {
+               .len = 8,
+               .attributes = { 0x00, 0x02, 0x03, 0x94, 0x01, 0x02, 0x03,
+                               0x96 },
+       },
+       .html = "<span style=\"color: #347235;background-color: #FFFFFF;\">"
+               "a</span><span style=\"color: #0000A0;background-color: "
+               "#FFFFFF;\">bc</span>",
+};
+
+static struct html_attr_test html_attr_data_3 = {
+       .text = "1 < 2, 2 > 1, 1 & 0 == 0\nSpecial Chars are Fun\r\nTo Write",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x00, 0x03, 0x00 },
+       },
+       .html = "1 &lt; 2, 2 &gt; 1, 1 &amp; 0 == 0<br/>Special Chars are Fun"
+               "<br/>To Write",
+};
+
+static struct html_attr_test html_attr_data_4 = {
+       .text = "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€",
+       .text_attr = {
+               .len = 4,
+               .attributes = { 0x00, 0x00, 0x03, 0x94 },
+       },
+       .html = "<span style=\"color: #347235;background-color: #FFFFFF;\">"
+               "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€"
+               "€€€€€€€€€€€€€€€</span>",
+};
+
+static void test_html_attr(gconstpointer data)
+{
+       const struct html_attr_test *test = data;
+       check_text_attr_html(&test->text_attr, test->text, test->html);
+}
+
+struct img_xpm_test {
+       const unsigned char *img;
+       unsigned int len;
+       const unsigned char *clut;
+       unsigned short clut_len;
+       guint8 scheme;
+       char *xpm;
+};
+
+const unsigned char img1[] = { 0x05, 0x05, 0xFE, 0xEB, 0xBF, 0xFF, 0xFF, 0xFF };
+
+const unsigned char img2[] = { 0x08, 0x08, 0x02, 0x03, 0x00, 0x16, 0xAA,
+                                       0xAA, 0x80, 0x02, 0x85, 0x42, 0x81,
+                                       0x42, 0x81, 0x42, 0x81, 0x52, 0x80,
+                                       0x02, 0xAA, 0xAA, 0xFF, 0x00, 0x00,
+                                       0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF };
+
+const unsigned char img3[] = { 0x2E, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x0F,
+                               0xFF, 0x00, 0x00, 0x00, 0x00, 0x77, 0xFE, 0x00,
+                               0x00, 0x00, 0x01, 0xBF, 0xF8, 0x00, 0x00, 0x00,
+                               0x06, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x1A, 0x03,
+                               0x80, 0x00, 0x00, 0x00, 0x6B, 0xF6, 0xBC, 0x00,
+                               0x00, 0x01, 0xAF, 0xD8, 0x38, 0x00, 0x00, 0x06,
+                               0xBF, 0x60, 0x20, 0x00, 0x00, 0x1A, 0xFD, 0x80,
+                               0x40, 0x00, 0x00, 0x6B, 0xF6, 0x00, 0x80, 0x00,
+                               0x01, 0xA0, 0x1F, 0x02, 0x00, 0x00, 0x06, 0xFF,
+                               0xE4, 0x04, 0x00, 0x00, 0x1B, 0xFF, 0x90, 0x10,
+                               0x00, 0x00, 0x6D, 0xEE, 0x40, 0x40, 0x00, 0x01,
+                               0xBF, 0xF9, 0x01, 0x00, 0x00, 0x6F, 0xFF, 0xE4,
+                               0x04, 0x00, 0x00, 0x1B, 0xFF, 0x90, 0x10, 0x00,
+                               0x00, 0x6F, 0xFE, 0x40, 0x40, 0x00, 0x01, 0xBF,
+                               0xF9, 0x01, 0x00, 0x00, 0x06, 0xFF, 0xE6, 0x04,
+                               0x00, 0x00, 0x1B, 0xFF, 0x88, 0x10, 0x00, 0x00,
+                               0x6F, 0xFE, 0x20, 0x40, 0x00, 0x01, 0xBF, 0xF8,
+                               0x66, 0x00, 0x00, 0x06, 0xFF, 0xE0, 0xF0, 0x00,
+                               0x00, 0x1B, 0xFF, 0x80, 0x80, 0x00, 0x00, 0x7F,
+                               0xFE, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0C, 0x00,
+                               0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x1C, 0x21, 0x08, 0x44, 0xEE, 0x00, 0x48, 0xC4,
+                               0x31, 0x92, 0x20, 0x01, 0x25, 0x11, 0x45, 0x50,
+                               0x80, 0x07, 0x14, 0x45, 0x15, 0x43, 0x80, 0x12,
+                               0x71, 0x1C, 0x4D, 0x08, 0x00, 0x4A, 0x24, 0x89,
+                               0x32, 0x20, 0x01, 0xC8, 0x9E, 0x24, 0x4E,
+                               0xE0 };
+
+const unsigned char img4[] = { 0x18, 0x10, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01,
+                               0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x8F,
+                               0x3C, 0xF1, 0x89, 0x20, 0x81, 0x89, 0x20,
+                               0x81, 0x89, 0x20, 0xF1, 0x89, 0x20, 0x11,
+                               0x89, 0x20, 0x11, 0x89, 0x20, 0x11, 0x8F,
+                               0x3C, 0xF1, 0x80, 0x00, 0x01, 0x80, 0x00,
+                               0x01, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0xFF };
+
+const unsigned char img5[] = { 0x08, 0x08, 0xFF, 0x03, 0xA5, 0x99, 0x99,
+                               0xA5, 0xC3, 0xFF };
+
+static struct img_xpm_test xpm_test_1 = {
+       .img = img1,
+       .len = sizeof(img1),
+       .scheme = STK_IMG_SCHEME_BASIC,
+       .xpm = "/* XPM */\n"
+               "static char *xpm[] = {\n"
+               "\"5 5 2 1\",\n"
+               "\"0    c #000000\",\n"
+               "\"1    c #FFFFFF\",\n"
+               "\"11111\",\n"
+               "\"11011\",\n"
+               "\"10101\",\n"
+               "\"11011\",\n"
+               "\"11111\",\n"
+               "};",
+};
+
+static struct img_xpm_test xpm_test_2 = {
+       .img = img2,
+       .len = sizeof(img2),
+       .clut = img2 + 0x16,
+       .clut_len = 0x09,
+       .scheme = STK_IMG_SCHEME_COLOR,
+       .xpm = "/* XPM */\n"
+               "static char *xpm[] = {\n"
+               "\"8 8 3 1\",\n"
+               "\"0    c #FF0000\",\n"
+               "\"1    c #00FF00\",\n"
+               "\"2    c #0000FF\",\n"
+               "\"22222222\",\n"
+               "\"20000002\",\n"
+               "\"20111002\",\n"
+               "\"20011002\",\n"
+               "\"20011002\",\n"
+               "\"20011102\",\n"
+               "\"20000002\",\n"
+               "\"22222222\",\n"
+               "};",
+};
+
+static struct img_xpm_test xpm_test_3 = {
+       .img = img3,
+       .len = sizeof(img3),
+       .scheme = STK_IMG_SCHEME_BASIC,
+       .xpm = "/* XPM */\n"
+               "static char *xpm[] = {\n"
+               "\"46 40 2 1\",\n"
+               "\"0    c #000000\",\n"
+               "\"1    c #FFFFFF\",\n"
+               "\"0000000000000000000000000000000000000000000000\",\n"
+               "\"0000000000000000011111111110000000000000000000\",\n"
+               "\"0000000000000000111111111111000000000000000000\",\n"
+               "\"0000000000000001110111111111100000000000000000\",\n"
+               "\"0000000000000001101111111111100000000000000000\",\n"
+               "\"0000000000000001101111111111100000000000000000\",\n"
+               "\"0000000000000001101000000011100000000000000000\",\n"
+               "\"0000000000000001101011111101101011110000000000\",\n"
+               "\"0000000000000001101011111101100000111000000000\",\n"
+               "\"0000000000000001101011111101100000001000000000\",\n"
+               "\"0000000000000001101011111101100000000100000000\",\n"
+               "\"0000000000000001101011111101100000000010000000\",\n"
+               "\"0000000000000001101000000001111100000010000000\",\n"
+               "\"0000000000000001101111111111100100000001000000\",\n"
+               "\"0000000000000001101111111111100100000001000000\",\n"
+               "\"0000000000000001101101111011100100000001000000\",\n"
+               "\"0000000000000001101111111111100100000001000000\",\n"
+               "\"0000000000011011111111111111100100000001000000\",\n"
+               "\"0000000000000001101111111111100100000001000000\",\n"
+               "\"0000000000000001101111111111100100000001000000\",\n"
+               "\"0000000000000001101111111111100100000001000000\",\n"
+               "\"0000000000000001101111111111100110000001000000\",\n"
+               "\"0000000000000001101111111111100010000001000000\",\n"
+               "\"0000000000000001101111111111100010000001000000\",\n"
+               "\"0000000000000001101111111111100001100110000000\",\n"
+               "\"0000000000000001101111111111100000111100000000\",\n"
+               "\"0000000000000001101111111111100000001000000000\",\n"
+               "\"0000000000000001111111111111100000000000000000\",\n"
+               "\"0000000000000011000000000000110000000000000000\",\n"
+               "\"0000000000000111111111111111111000000000000000\",\n"
+               "\"0000000000000000000000000000000000000000000000\",\n"
+               "\"0000000000000000000000000000000000000000000000\",\n"
+               "\"0000000000000000000000000000000000000000000000\",\n"
+               "\"0000011100001000010000100001000100111011100000\",\n"
+               "\"0000010010001100010000110001100100100010000000\",\n"
+               "\"0000010010010100010001010001010101000010000000\",\n"
+               "\"0000011100010100010001010001010101000011100000\",\n"
+               "\"0000010010011100010001110001001101000010000000\",\n"
+               "\"0000010010100010010010001001001100100010000000\",\n"
+               "\"0000011100100010011110001001000100111011100000\",\n"
+               "};",
+};
+
+static struct img_xpm_test xpm_test_4 = {
+       .img = img4,
+       .len = sizeof(img4),
+       .scheme = STK_IMG_SCHEME_BASIC,
+       .xpm = "/* XPM */\n"
+               "static char *xpm[] = {\n"
+               "\"24 16 2 1\",\n"
+               "\"0    c #000000\",\n"
+               "\"1    c #FFFFFF\",\n"
+               "\"111111111111111111111111\",\n"
+               "\"100000000000000000000001\",\n"
+               "\"100000000000000000000001\",\n"
+               "\"100000000000000000000001\",\n"
+               "\"100011110011110011110001\",\n"
+               "\"100010010010000010000001\",\n"
+               "\"100010010010000010000001\",\n"
+               "\"100010010010000011110001\",\n"
+               "\"100010010010000000010001\",\n"
+               "\"100010010010000000010001\",\n"
+               "\"100010010010000000010001\",\n"
+               "\"100011110011110011110001\",\n"
+               "\"100000000000000000000001\",\n"
+               "\"100000000000000000000001\",\n"
+               "\"100000000000000000000001\",\n"
+               "\"111111111111111111111111\",\n"
+               "};",
+};
+
+static struct img_xpm_test xpm_test_5 = {
+       .img = img5,
+       .len = sizeof(img5),
+       .scheme = STK_IMG_SCHEME_BASIC,
+       .xpm = "/* XPM */\n"
+               "static char *xpm[] = {\n"
+               "\"8 8 2 1\",\n"
+               "\"0    c #000000\",\n"
+               "\"1    c #FFFFFF\",\n"
+               "\"11111111\",\n"
+               "\"00000011\",\n"
+               "\"10100101\",\n"
+               "\"10011001\",\n"
+               "\"10011001\",\n"
+               "\"10100101\",\n"
+               "\"11000011\",\n"
+               "\"11111111\",\n"
+               "};",
+};
+
+static struct img_xpm_test xpm_test_6 = {
+       .img = img2,
+       .len = sizeof(img2),
+       .clut = img2 + 0x16,
+       .clut_len = 0x09,
+       .scheme = STK_IMG_SCHEME_TRANSPARENCY,
+       .xpm = "/* XPM */\n"
+               "static char *xpm[] = {\n"
+               "\"8 8 3 1\",\n"
+               "\"0    c #FF0000\",\n"
+               "\"1    c #00FF00\",\n"
+               "\"2    c None\",\n"
+               "\"22222222\",\n"
+               "\"20000002\",\n"
+               "\"20111002\",\n"
+               "\"20011002\",\n"
+               "\"20011002\",\n"
+               "\"20011102\",\n"
+               "\"20000002\",\n"
+               "\"22222222\",\n"
+               "};",
+};
+
+static void test_img_to_xpm(gconstpointer data)
+{
+       const struct img_xpm_test *test = data;
+       char *xpm;
+
+       xpm = stk_image_to_xpm(test->img, test->len, test->scheme,
+                               test->clut, test->clut_len);
+
+       g_assert(memcmp(xpm, test->xpm, strlen(test->xpm)) == 0);
+       g_free(xpm);
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_data_func("/teststk/Display Text 1.1.1",
+                               &display_text_data_111, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 1.3.1",
+                               &display_text_data_131, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 1.4.1",
+                               &display_text_data_141, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 1.5.1",
+                               &display_text_data_151, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 1.6.1",
+                               &display_text_data_161, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 1.7.1",
+                               &display_text_data_171, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 5.1.1",
+                               &display_text_data_511, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 5.2.1",
+                               &display_text_data_521, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 5.3.1",
+                               &display_text_data_531, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 6.1.1",
+                               &display_text_data_611, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 7.1.1",
+                               &display_text_data_711, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.1.1",
+                               &display_text_data_811, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.2.1",
+                               &display_text_data_821, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.3.1",
+                               &display_text_data_831, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.4.1",
+                               &display_text_data_841, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.5.1",
+                               &display_text_data_851, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.6.1",
+                               &display_text_data_861, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.7.1",
+                               &display_text_data_871, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.8.1",
+                               &display_text_data_881, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 8.9.1",
+                               &display_text_data_891, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 9.1.1",
+                               &display_text_data_911, test_display_text);
+       g_test_add_data_func("/teststk/Display Text 10.1.1",
+                               &display_text_data_1011, test_display_text);
+
+       g_test_add_data_func("/teststk/Display Text response 1.1.1",
+                               &display_text_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 1.2.1",
+                               &display_text_response_data_121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 1.3.1",
+                               &display_text_response_data_131,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 1.5.1",
+                               &display_text_response_data_151,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 1.7.1",
+                               &display_text_response_data_171,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 1.8.1",
+                               &display_text_response_data_181,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 1.9.1",
+                               &display_text_response_data_191,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 2.1.1",
+                               &display_text_response_data_211,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Display Text response 5.1.1B",
+                               &display_text_response_data_511b,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Get Inkey 1.1.1",
+                               &get_inkey_data_111, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 1.2.1",
+                               &get_inkey_data_121, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 1.3.1",
+                               &get_inkey_data_131, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 1.4.1",
+                               &get_inkey_data_141, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 1.5.1",
+                               &get_inkey_data_151, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 1.6.1",
+                               &get_inkey_data_161, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 2.1.1",
+                               &get_inkey_data_211, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 3.1.1",
+                               &get_inkey_data_311, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 3.2.1",
+                               &get_inkey_data_321, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 4.1.1",
+                               &get_inkey_data_411, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 5.1.1",
+                               &get_inkey_data_511, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 5.1.2",
+                               &get_inkey_data_512, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 6.1.1",
+                               &get_inkey_data_611, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 6.2.1",
+                               &get_inkey_data_621, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 6.3.1",
+                               &get_inkey_data_631, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 6.4.1",
+                               &get_inkey_data_641, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 7.1.1",
+                               &get_inkey_data_711, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 7.1.2",
+                               &get_inkey_data_712, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 8.1.1",
+                               &get_inkey_data_811, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.1.1",
+                               &get_inkey_data_911, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.1.2",
+                               &get_inkey_data_912, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.2.1",
+                               &get_inkey_data_921, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.2.2",
+                               &get_inkey_data_922, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.3.1",
+                               &get_inkey_data_931, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.3.2",
+                               &get_inkey_data_932, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.4.1",
+                               &get_inkey_data_941, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.4.2",
+                               &get_inkey_data_942, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.4.3",
+                               &get_inkey_data_943, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.5.1",
+                               &get_inkey_data_951, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.5.2",
+                               &get_inkey_data_952, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.5.3",
+                               &get_inkey_data_953, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.6.1",
+                               &get_inkey_data_961, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.6.2",
+                               &get_inkey_data_962, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.6.3",
+                               &get_inkey_data_963, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.7.1",
+                               &get_inkey_data_971, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.7.2",
+                               &get_inkey_data_972, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.7.3",
+                               &get_inkey_data_973, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.8.1",
+                               &get_inkey_data_981, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.8.2",
+                               &get_inkey_data_982, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.8.3",
+                               &get_inkey_data_983, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.9.1",
+                               &get_inkey_data_991, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.9.2a",
+                               &get_inkey_data_992a, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.9.2b",
+                               &get_inkey_data_992b, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.9.3",
+                               &get_inkey_data_993, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.10.1",
+                               &get_inkey_data_9101, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 9.10.2",
+                               &get_inkey_data_9102, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 10.1.1",
+                               &get_inkey_data_1011, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 10.2.1",
+                               &get_inkey_data_1021, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 11.1.1",
+                               &get_inkey_data_1111, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 12.1.1",
+                               &get_inkey_data_1211, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 12.2.1",
+                               &get_inkey_data_1221, test_get_inkey);
+       g_test_add_data_func("/teststk/Get Inkey 13.1.1",
+                               &get_inkey_data_1311, test_get_inkey);
+
+       g_test_add_data_func("/teststk/Get Inkey response 1.1.1",
+                               &get_inkey_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 1.2.1",
+                               &get_inkey_response_data_121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 1.3.1",
+                               &get_inkey_response_data_131,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 1.4.1",
+                               &get_inkey_response_data_141,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 1.5.1",
+                               &get_inkey_response_data_151,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 1.6.1",
+                               &get_inkey_response_data_161,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 2.1.1",
+                               &get_inkey_response_data_211,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 4.1.1",
+                               &get_inkey_response_data_411,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 5.1.1",
+                               &get_inkey_response_data_511,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 5.1.2",
+                               &get_inkey_response_data_512,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 6.1.1B",
+                               &get_inkey_response_data_611b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 7.1.1",
+                               &get_inkey_response_data_711,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 7.1.2",
+                               &get_inkey_response_data_712,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 8.1.1",
+                               &get_inkey_response_data_811,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 9.1.2",
+                               &get_inkey_response_data_912,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 11.1.1",
+                               &get_inkey_response_data_1111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Inkey response 13.1.1",
+                               &get_inkey_response_data_1311,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Get Input 1.1.1",
+                               &get_input_data_111, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.2.1",
+                               &get_input_data_121, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.3.1",
+                               &get_input_data_131, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.4.1",
+                               &get_input_data_141, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.5.1",
+                               &get_input_data_151, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.6.1",
+                               &get_input_data_161, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.7.1",
+                               &get_input_data_171, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.8.1",
+                               &get_input_data_181, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.9.1",
+                               &get_input_data_191, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 1.10.1",
+                               &get_input_data_1101, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 2.1.1",
+                               &get_input_data_211, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 3.1.1",
+                               &get_input_data_311, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 3.2.1",
+                               &get_input_data_321, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 4.1.1",
+                               &get_input_data_411, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 4.2.1",
+                               &get_input_data_421, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 5.1.1",
+                               &get_input_data_511, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 5.2.1",
+                               &get_input_data_521, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 6.1.1",
+                               &get_input_data_611, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 6.2.1",
+                               &get_input_data_621, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 6.3.1",
+                               &get_input_data_631, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 6.4.1",
+                               &get_input_data_641, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 7.1.1",
+                               &get_input_data_711, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.1.1",
+                               &get_input_data_811, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.1.2",
+                               &get_input_data_812, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.2.1",
+                               &get_input_data_821, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.2.2",
+                               &get_input_data_822, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.3.1",
+                               &get_input_data_831, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.3.2",
+                               &get_input_data_832, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.4.1",
+                               &get_input_data_841, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.4.2",
+                               &get_input_data_842, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.4.3",
+                               &get_input_data_843, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.5.1",
+                               &get_input_data_851, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.5.2",
+                               &get_input_data_852, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.5.3",
+                               &get_input_data_853, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.6.1",
+                               &get_input_data_861, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.6.2",
+                               &get_input_data_862, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.6.3",
+                               &get_input_data_863, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.7.1",
+                               &get_input_data_871, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.7.2",
+                               &get_input_data_872, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.7.3",
+                               &get_input_data_873, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.8.1",
+                               &get_input_data_881, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.8.2",
+                               &get_input_data_882, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.8.3",
+                               &get_input_data_883, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.9.1",
+                               &get_input_data_891, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.9.2",
+                               &get_input_data_892, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.9.3",
+                               &get_input_data_893, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.10.1",
+                               &get_input_data_8101, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 8.10.2",
+                               &get_input_data_8102, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 9.1.1",
+                               &get_input_data_911, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 9.2.1",
+                               &get_input_data_921, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 10.1.1",
+                               &get_input_data_1011, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 10.2.1",
+                               &get_input_data_1021, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 11.1.1",
+                               &get_input_data_1111, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 11.2.1",
+                               &get_input_data_1121, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 12.1.1",
+                               &get_input_data_1211, test_get_input);
+       g_test_add_data_func("/teststk/Get Input 12.2.1",
+                               &get_input_data_1221, test_get_input);
+
+       g_test_add_data_func("/teststk/Get Input response 1.1.1",
+                               &get_input_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.2.1",
+                               &get_input_response_data_121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.3.1",
+                               &get_input_response_data_131,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.4.1",
+                               &get_input_response_data_141,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.5.1",
+                               &get_input_response_data_151,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.6.1",
+                               &get_input_response_data_161,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.7.1",
+                               &get_input_response_data_171,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.8.1",
+                               &get_input_response_data_181,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 1.9.1",
+                               &get_input_response_data_191,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 2.1.1",
+                               &get_input_response_data_211,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 3.1.1",
+                               &get_input_response_data_311,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 4.1.1",
+                               &get_input_response_data_411,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 4.2.1",
+                               &get_input_response_data_421,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 6.1.1A",
+                               &get_input_response_data_611a,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 6.1.1B",
+                               &get_input_response_data_611b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 7.1.1",
+                               &get_input_response_data_711,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 8.1.2",
+                               &get_input_response_data_812,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 8.4.3",
+                               &get_input_response_data_843,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 10.1.1",
+                               &get_input_response_data_1011,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 10.2.1",
+                               &get_input_response_data_1021,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 12.1.1",
+                               &get_input_response_data_1211,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Input response 12.2.1",
+                               &get_input_response_data_1221,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/More Time 1.1.1",
+                               &more_time_data_111, test_more_time);
+
+       g_test_add_data_func("/teststk/More Time response 1.1.1",
+                               &more_time_response_data_111,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Play Tone 1.1.1",
+                               &play_tone_data_111, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.2",
+                               &play_tone_data_112, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.3",
+                               &play_tone_data_113, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.4",
+                               &play_tone_data_114, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.5",
+                               &play_tone_data_115, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.6",
+                               &play_tone_data_116, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.7",
+                               &play_tone_data_117, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.8",
+                               &play_tone_data_118, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.9",
+                               &play_tone_data_119, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.10",
+                               &play_tone_data_1110, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.11",
+                               &play_tone_data_1111, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.12",
+                               &play_tone_data_1112, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.13",
+                               &play_tone_data_1113, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.14",
+                               &play_tone_data_1114, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 1.1.15",
+                               &play_tone_data_1115, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 2.1.1",
+                               &play_tone_data_211, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 2.1.2",
+                               &play_tone_data_212, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 2.1.3",
+                               &play_tone_data_213, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 3.1.1",
+                               &play_tone_data_311, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 3.2.1",
+                               &play_tone_data_321, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 3.3.1",
+                               &play_tone_data_331, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 3.4.1",
+                               &play_tone_data_341, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.1.1",
+                               &play_tone_data_411, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.1.2",
+                               &play_tone_data_412, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.2.1",
+                               &play_tone_data_421, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.2.2",
+                               &play_tone_data_422, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.3.1",
+                               &play_tone_data_431, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.3.2",
+                               &play_tone_data_432, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.4.1",
+                               &play_tone_data_441, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.4.2",
+                               &play_tone_data_442, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.4.3",
+                               &play_tone_data_443, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.5.1",
+                               &play_tone_data_451, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.5.2",
+                               &play_tone_data_452, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.5.3",
+                               &play_tone_data_453, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.6.1",
+                               &play_tone_data_461, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.6.2",
+                               &play_tone_data_462, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.6.3",
+                               &play_tone_data_463, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.7.1",
+                               &play_tone_data_471, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.7.2",
+                               &play_tone_data_472, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.7.3",
+                               &play_tone_data_473, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.8.1",
+                               &play_tone_data_481, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.8.2",
+                               &play_tone_data_482, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.8.3",
+                               &play_tone_data_483, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.9.1",
+                               &play_tone_data_491, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.9.2",
+                               &play_tone_data_492, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.9.3",
+                               &play_tone_data_493, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.10.1",
+                               &play_tone_data_4101, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 4.10.2",
+                               &play_tone_data_4102, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 5.1.1",
+                               &play_tone_data_511, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 5.1.2",
+                               &play_tone_data_512, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 5.1.3",
+                               &play_tone_data_513, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 6.1.1",
+                               &play_tone_data_611, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 6.1.2",
+                               &play_tone_data_612, test_play_tone);
+       g_test_add_data_func("/teststk/Play Tone 6.1.3",
+                               &play_tone_data_613, test_play_tone);
+
+       g_test_add_data_func("/teststk/Play Tone response 1.1.1",
+                               &play_tone_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Play Tone response 1.1.9B",
+                               &play_tone_response_data_119b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Play Tone response 1.1.14",
+                               &play_tone_response_data_1114,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Play Tone response 3.1.1B",
+                               &play_tone_response_data_311b,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Poll Interval 1.1.1",
+                               &poll_interval_data_111, test_poll_interval);
+
+       g_test_add_data_func("/teststk/Poll Interval response 1.1.1",
+                               &poll_interval_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Poll Interval response 1.1.1A",
+                               &poll_interval_response_data_111a,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Setup Menu 1.1.1",
+                               &setup_menu_data_111, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 1.1.2",
+                               &setup_menu_data_112, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 1.1.3",
+                               &setup_menu_data_113, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 1.2.1",
+                               &setup_menu_data_121, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 1.2.2",
+                               &setup_menu_data_122, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 1.2.3",
+                               &setup_menu_data_123, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 2.1.1",
+                               &setup_menu_data_211, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 3.1.1",
+                               &setup_menu_data_311, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 4.1.1",
+                               &setup_menu_data_411, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 4.2.1",
+                               &setup_menu_data_421, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 5.1.1",
+                               &setup_menu_data_511, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.1.1",
+                               &setup_menu_data_611, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.1.2",
+                               &setup_menu_data_612, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.2.1",
+                               &setup_menu_data_621, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.2.2",
+                               &setup_menu_data_622, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.3.1",
+                               &setup_menu_data_631, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.3.2",
+                               &setup_menu_data_632, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.4.1",
+                               &setup_menu_data_641, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.4.2",
+                               &setup_menu_data_642, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.4.3",
+                               &setup_menu_data_643, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.5.1",
+                               &setup_menu_data_651, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.6.1",
+                               &setup_menu_data_661, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.7.1",
+                               &setup_menu_data_671, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.8.1",
+                               &setup_menu_data_681, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.9.1",
+                               &setup_menu_data_691, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 6.10.1",
+                               &setup_menu_data_6101, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 7.1.1",
+                               &setup_menu_data_711, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 7.1.2",
+                               &setup_menu_data_712, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 7.1.3",
+                               &setup_menu_data_713, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 8.1.1",
+                               &setup_menu_data_811, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 8.1.2",
+                               &setup_menu_data_812, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 8.1.3",
+                               &setup_menu_data_813, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 9.1.1",
+                               &setup_menu_data_911, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 9.1.2",
+                               &setup_menu_data_912, test_setup_menu);
+       g_test_add_data_func("/teststk/Setup Menu 9.1.3",
+                               &setup_menu_data_913, test_setup_menu);
+
+       g_test_add_data_func("/teststk/Setup Menu Negative 1",
+                       &setup_menu_data_neg_1, test_setup_menu_missing_val);
+       g_test_add_data_func("/teststk/Setup Menu Negative 2",
+                       &setup_menu_data_neg_2, test_setup_menu_neg);
+       g_test_add_data_func("/teststk/Setup Menu Negative 3",
+                       &setup_menu_data_neg_3, test_setup_menu_neg);
+       g_test_add_data_func("/teststk/Setup Menu Negative 4",
+                       &setup_menu_data_neg_4, test_setup_menu_neg);
+
+       g_test_add_data_func("/teststk/Set Up Menu response 1.1.1",
+                               &set_up_menu_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Menu response 4.1.1B",
+                               &set_up_menu_response_data_411b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Menu response 5.1.1",
+                               &set_up_menu_response_data_511,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Select Item 1.1.1",
+                               &select_item_data_111, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 1.2.1",
+                               &select_item_data_121, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 1.3.1",
+                               &select_item_data_131, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 1.4.1",
+                               &select_item_data_141, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 1.5.1",
+                               &select_item_data_151, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 1.6.1",
+                               &select_item_data_161, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 2.1.1",
+                               &select_item_data_211, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 3.1.1",
+                               &select_item_data_311, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 4.1.1",
+                               &select_item_data_411, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 5.1.1",
+                               &select_item_data_511, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 5.2.1",
+                               &select_item_data_521, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 6.1.1",
+                               &select_item_data_611, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 6.2.1",
+                               &select_item_data_621, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 7.1.1",
+                               &select_item_data_711, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 8.1.1",
+                               &select_item_data_811, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.1.1",
+                               &select_item_data_911, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.1.2",
+                               &select_item_data_912, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.2.1",
+                               &select_item_data_921, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.2.2",
+                               &select_item_data_922, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.3.1",
+                               &select_item_data_931, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.3.2",
+                               &select_item_data_932, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.4.1",
+                               &select_item_data_941, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.4.2",
+                               &select_item_data_942, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.4.3",
+                               &select_item_data_943, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.5.1",
+                               &select_item_data_951, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.5.2",
+                               &select_item_data_952, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.5.3",
+                               &select_item_data_953, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.6.1",
+                               &select_item_data_961, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.6.2",
+                               &select_item_data_962, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.6.3",
+                               &select_item_data_963, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.7.1",
+                               &select_item_data_971, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.7.2",
+                               &select_item_data_972, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.7.3",
+                               &select_item_data_973, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.8.1",
+                               &select_item_data_981, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.8.2",
+                               &select_item_data_982, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.8.3",
+                               &select_item_data_983, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.9.1",
+                               &select_item_data_991, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.9.2",
+                               &select_item_data_992, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.9.3",
+                               &select_item_data_993, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.10.1",
+                               &select_item_data_9101, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 9.10.2",
+                               &select_item_data_9102, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 10.1.1",
+                               &select_item_data_1011, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 10.2.1",
+                               &select_item_data_1021, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 10.3.1",
+                               &select_item_data_1031, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 11.1.1",
+                               &select_item_data_1111, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 12.1.1",
+                               &select_item_data_1211, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 12.2.1",
+                               &select_item_data_1221, test_select_item);
+       g_test_add_data_func("/teststk/Select Item 12.3.1",
+                               &select_item_data_1231, test_select_item);
+
+       g_test_add_data_func("/teststk/Select Item response 1.1.1",
+                               &select_item_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 1.2.1",
+                               &select_item_response_data_121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 1.3.1",
+                               &select_item_response_data_131,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 1.4.1",
+                               &select_item_response_data_141,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 1.4.2",
+                               &select_item_response_data_142,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 1.5.1",
+                               &select_item_response_data_151,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 3.1.1",
+                               &select_item_response_data_311,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 4.1.1",
+                               &select_item_response_data_411,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 5.1.1B",
+                               &select_item_response_data_511b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 6.1.1",
+                               &select_item_response_data_611,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 6.2.1",
+                               &select_item_response_data_621,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 7.1.1",
+                               &select_item_response_data_711,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Select Item response 8.1.1",
+                               &select_item_response_data_811,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Send SMS 1.1.1",
+                               &send_sms_data_111, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 1.2.1",
+                               &send_sms_data_121, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 1.3.1",
+                               &send_sms_data_131, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 1.4.1",
+                               &send_sms_data_141, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 1.5.1",
+                               &send_sms_data_151, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 1.6.1",
+                               &send_sms_data_161, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 1.7.1",
+                               &send_sms_data_171, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 1.8.1",
+                               &send_sms_data_181, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 2.1.1",
+                               &send_sms_data_211, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 2.1.2",
+                               &send_sms_data_212, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 2.1.3",
+                               &send_sms_data_213, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 3.1.1",
+                               &send_sms_data_311, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 3.2.1",
+                               &send_sms_data_321, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.1.1",
+                               &send_sms_data_411, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.1.2",
+                               &send_sms_data_412, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.2.1",
+                               &send_sms_data_421, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.2.2",
+                               &send_sms_data_422, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.3.1",
+                               &send_sms_data_431, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.3.2",
+                               &send_sms_data_432, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.4.1",
+                               &send_sms_data_441, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.4.2",
+                               &send_sms_data_442, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.4.3",
+                               &send_sms_data_443, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.5.1",
+                               &send_sms_data_451, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.5.2",
+                               &send_sms_data_452, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.5.3",
+                               &send_sms_data_453, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.6.1",
+                               &send_sms_data_461, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.6.2",
+                               &send_sms_data_462, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.6.3",
+                               &send_sms_data_463, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.7.1",
+                               &send_sms_data_471, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.7.2",
+                               &send_sms_data_472, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.7.3",
+                               &send_sms_data_473, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.8.1",
+                               &send_sms_data_481, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.8.2",
+                               &send_sms_data_482, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.8.3",
+                               &send_sms_data_483, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.9.1",
+                               &send_sms_data_491, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.9.2",
+                               &send_sms_data_492, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.9.3",
+                               &send_sms_data_493, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.10.1",
+                               &send_sms_data_4101, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 4.10.2",
+                               &send_sms_data_4102, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 5.1.1",
+                               &send_sms_data_511, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 5.1.2",
+                               &send_sms_data_512, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 5.1.3",
+                               &send_sms_data_513, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 6.1.1",
+                               &send_sms_data_611, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 6.1.2",
+                               &send_sms_data_612, test_send_sms);
+       g_test_add_data_func("/teststk/Send SMS 6.1.3",
+                               &send_sms_data_613, test_send_sms);
+
+       g_test_add_data_func("/teststk/Send SS 1.1.1",
+                               &send_ss_data_111, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 1.4.1",
+                               &send_ss_data_141, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 1.5.1",
+                               &send_ss_data_151, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 1.6.1",
+                               &send_ss_data_161, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 2.1.1",
+                               &send_ss_data_211, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 2.2.1",
+                               &send_ss_data_221, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 2.3.1",
+                               &send_ss_data_231, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 2.4.1",
+                               &send_ss_data_241, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 3.1.1",
+                               &send_ss_data_311, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.1.1",
+                               &send_ss_data_411, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.1.2",
+                               &send_ss_data_412, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.2.1",
+                               &send_ss_data_421, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.2.2",
+                               &send_ss_data_422, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.3.1",
+                               &send_ss_data_431, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.3.2",
+                               &send_ss_data_432, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.4.1",
+                               &send_ss_data_441, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.4.2",
+                               &send_ss_data_442, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.4.3",
+                               &send_ss_data_443, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.5.1",
+                               &send_ss_data_451, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.5.2",
+                               &send_ss_data_452, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.5.3",
+                               &send_ss_data_453, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.6.1",
+                               &send_ss_data_461, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.6.2",
+                               &send_ss_data_462, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.6.3",
+                               &send_ss_data_463, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.7.1",
+                               &send_ss_data_471, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.7.2",
+                               &send_ss_data_472, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.7.3",
+                               &send_ss_data_473, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.8.1",
+                               &send_ss_data_481, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.8.2",
+                               &send_ss_data_482, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.8.3",
+                               &send_ss_data_483, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.9.1",
+                               &send_ss_data_491, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.9.2",
+                               &send_ss_data_492, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.9.3",
+                               &send_ss_data_493, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.10.1",
+                               &send_ss_data_4101, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 4.10.2",
+                               &send_ss_data_4102, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 5.1.1",
+                               &send_ss_data_511, test_send_ss);
+       g_test_add_data_func("/teststk/Send SS 6.1.1",
+                               &send_ss_data_611, test_send_ss);
+
+       g_test_add_data_func("/teststk/Send USSD 1.1.1",
+                               &send_ussd_data_111, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 1.2.1",
+                               &send_ussd_data_121, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 1.3.1",
+                               &send_ussd_data_131, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 1.6.1",
+                               &send_ussd_data_161, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 1.7.1",
+                               &send_ussd_data_171, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 1.8.1",
+                               &send_ussd_data_181, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 2.1.1",
+                               &send_ussd_data_211, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 2.2.1",
+                               &send_ussd_data_221, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 2.3.1",
+                               &send_ussd_data_231, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 2.4.1",
+                               &send_ussd_data_241, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 3.1.1",
+                               &send_ussd_data_311, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.1.1",
+                               &send_ussd_data_411, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.1.2",
+                               &send_ussd_data_412, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.2.1",
+                               &send_ussd_data_421, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.2.2",
+                               &send_ussd_data_422, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.3.1",
+                               &send_ussd_data_431, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.3.2",
+                               &send_ussd_data_432, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.4.1",
+                               &send_ussd_data_441, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.4.2",
+                               &send_ussd_data_442, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.4.3",
+                               &send_ussd_data_443, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.5.1",
+                               &send_ussd_data_451, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.5.2",
+                               &send_ussd_data_452, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.5.3",
+                               &send_ussd_data_453, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.6.1",
+                               &send_ussd_data_461, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.6.2",
+                               &send_ussd_data_462, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.6.3",
+                               &send_ussd_data_463, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.7.1",
+                               &send_ussd_data_471, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.7.2",
+                               &send_ussd_data_472, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.7.3",
+                               &send_ussd_data_473, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.8.1",
+                               &send_ussd_data_481, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.8.2",
+                               &send_ussd_data_482, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.8.3",
+                               &send_ussd_data_483, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.9.1",
+                               &send_ussd_data_491, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.9.2",
+                               &send_ussd_data_492, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.9.3",
+                               &send_ussd_data_493, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.10.1",
+                               &send_ussd_data_4101, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 4.10.2",
+                               &send_ussd_data_4102, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 5.1.1",
+                               &send_ussd_data_511, test_send_ussd);
+       g_test_add_data_func("/teststk/Send USSD 6.1.1",
+                               &send_ussd_data_611, test_send_ussd);
+
+       g_test_add_data_func("/teststk/Send SMS response 1.1.1",
+                               &send_sms_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Send SMS response 1.2.1",
+                               &send_sms_response_data_121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Send SMS response 3.1.1B",
+                               &send_sms_response_data_311b,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Setup Call 1.1.1",
+                               &setup_call_data_111, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 1.4.1",
+                               &setup_call_data_141, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 1.5.1",
+                               &setup_call_data_151, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 1.8.1",
+                               &setup_call_data_181, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 1.9.1",
+                               &setup_call_data_191, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 1.10.1",
+                               &setup_call_data_1101, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 1.11.1",
+                               &setup_call_data_1111, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 1.12.1",
+                               &setup_call_data_1121, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 2.1.1",
+                               &setup_call_data_211, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 3.1.1",
+                               &setup_call_data_311, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 3.2.1",
+                               &setup_call_data_321, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 3.3.1",
+                               &setup_call_data_331, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 3.4.1",
+                               &setup_call_data_341, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.1.1",
+                               &setup_call_data_411, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.1.2",
+                               &setup_call_data_412, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.2.1",
+                               &setup_call_data_421, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.2.2",
+                               &setup_call_data_422, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.3.1",
+                               &setup_call_data_431, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.3.2",
+                               &setup_call_data_432, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.4.1",
+                               &setup_call_data_441, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.4.2",
+                               &setup_call_data_442, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.4.3",
+                               &setup_call_data_443, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.5.1",
+                               &setup_call_data_451, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.5.2",
+                               &setup_call_data_452, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.5.3",
+                               &setup_call_data_453, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.6.1",
+                               &setup_call_data_461, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.6.2",
+                               &setup_call_data_462, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.6.3",
+                               &setup_call_data_463, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.7.1",
+                               &setup_call_data_471, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.7.2",
+                               &setup_call_data_472, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.7.3",
+                               &setup_call_data_473, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.8.1",
+                               &setup_call_data_481, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.8.2",
+                               &setup_call_data_482, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.8.3",
+                               &setup_call_data_483, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.9.1",
+                               &setup_call_data_491, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.9.2",
+                               &setup_call_data_492, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.9.3",
+                               &setup_call_data_493, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.10.1",
+                               &setup_call_data_4101, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 4.10.2",
+                               &setup_call_data_4102, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 5.1.1",
+                               &setup_call_data_511, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 5.2.1",
+                               &setup_call_data_521, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 6.1.1",
+                               &setup_call_data_611, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 6.2.1",
+                               &setup_call_data_621, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 7.1.1",
+                               &setup_call_data_711, test_setup_call);
+       g_test_add_data_func("/teststk/Setup Call 7.2.1",
+                               &setup_call_data_721, test_setup_call);
+
+       g_test_add_data_func("/teststk/Set Up Call response 1.1.1",
+                               &set_up_call_response_data_111,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.2.1",
+                               &set_up_call_response_data_121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.4.1",
+                               &set_up_call_response_data_141,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.5.1",
+                               &set_up_call_response_data_151,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.6.1",
+                               &set_up_call_response_data_161,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.7.1A",
+                               &set_up_call_response_data_171a,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.7.1B",
+                               &set_up_call_response_data_171b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.10.1",
+                               &set_up_call_response_data_1101,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.11.1B",
+                               &set_up_call_response_data_1111b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 1.12.1",
+                               &set_up_call_response_data_1121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Call response 3.1.1B",
+                               &set_up_call_response_data_311b,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Refresh 1.2.1",
+                               &refresh_data_121, test_refresh);
+       g_test_add_data_func("/teststk/Refresh 1.5.1",
+                               &refresh_data_151, test_refresh);
+
+       g_test_add_data_func("/teststk/Refresh response 1.1.1A",
+                               &refresh_response_data_111a,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.1.1B",
+                               &refresh_response_data_111b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.2.1A",
+                               &refresh_response_data_121a,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.2.1B",
+                               &refresh_response_data_121b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.3.1A",
+                               &refresh_response_data_131a,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.3.1B",
+                               &refresh_response_data_141b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.4.1A",
+                               &refresh_response_data_141a,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.4.1B",
+                               &refresh_response_data_141b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 1.7.1",
+                               &refresh_response_data_171,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 2.4.1A",
+                               &refresh_response_data_241a,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 2.4.1B",
+                               &refresh_response_data_241b,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 3.1.1",
+                               &refresh_response_data_311,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Refresh response 3.1.2",
+                               &refresh_response_data_312,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Polling off 1.1.2",
+                               &polling_off_data_112, test_polling_off);
+
+       g_test_add_data_func("/teststk/Polling off response 1.1.2",
+                               &polling_off_response_data_112,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Provide Local Info 1.2.1",
+                       &provide_local_info_data_121, test_provide_local_info);
+       g_test_add_data_func("/teststk/Provide Local Info 1.4.1",
+                       &provide_local_info_data_141, test_provide_local_info);
+       g_test_add_data_func("/teststk/Provide Local Info 1.5.1",
+                       &provide_local_info_data_151, test_provide_local_info);
+       g_test_add_data_func("/teststk/Provide Local Info 1.8.1",
+                       &provide_local_info_data_181, test_provide_local_info);
+       g_test_add_data_func("/teststk/Provide Local Info 1.9.1",
+                       &provide_local_info_data_191, test_provide_local_info);
+       g_test_add_data_func("/teststk/Provide Local Info 1.11.1",
+                       &provide_local_info_data_1111, test_provide_local_info);
+
+       g_test_add_data_func("/teststk/Provide Local Info response 1.1.1A",
+                       &provide_local_info_response_data_111a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.1.1B",
+                       &provide_local_info_response_data_111b,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.2.1",
+                       &provide_local_info_response_data_121,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.3.1",
+                       &provide_local_info_response_data_131,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.4.1",
+                       &provide_local_info_response_data_141,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.5.1",
+                       &provide_local_info_response_data_151,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.6.1",
+                       &provide_local_info_response_data_161,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.7.1",
+                       &provide_local_info_response_data_171,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.8.1",
+                       &provide_local_info_response_data_181,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.9.1",
+                       &provide_local_info_response_data_191,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.11.1",
+                       &provide_local_info_response_data_1111,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.12.1",
+                       &provide_local_info_response_data_1121,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.13.1",
+                       &provide_local_info_response_data_1131,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.14.1",
+                       &provide_local_info_response_data_1141,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.15.1",
+                       &provide_local_info_response_data_1151,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.16.1",
+                       &provide_local_info_response_data_1161,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Provide Local Info response 1.17.1",
+                       &provide_local_info_response_data_1171,
+                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Setup Event List 1.1.1",
+                       &setup_event_list_data_111, test_setup_event_list);
+       g_test_add_data_func("/teststk/Setup Event List 1.2.1",
+                       &setup_event_list_data_121, test_setup_event_list);
+       g_test_add_data_func("/teststk/Setup Event List 1.2.2",
+                       &setup_event_list_data_122, test_setup_event_list);
+       g_test_add_data_func("/teststk/Setup Event List 1.3.1",
+                       &setup_event_list_data_131, test_setup_event_list);
+       g_test_add_data_func("/teststk/Setup Event List 1.3.2",
+                       &setup_event_list_data_132, test_setup_event_list);
+       g_test_add_data_func("/teststk/Setup Event List 1.4.1",
+                       &setup_event_list_data_141, test_setup_event_list);
+
+       g_test_add_data_func("/teststk/Set Up Event List response 1.1.1",
+                       &set_up_event_list_response_data_111,
+                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Perform Card APDU 1.1.1",
+                       &perform_card_apdu_data_111, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 1.1.2",
+                       &perform_card_apdu_data_112, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 1.2.1",
+                       &perform_card_apdu_data_121, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 1.2.2",
+                       &perform_card_apdu_data_122, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 1.2.3",
+                       &perform_card_apdu_data_123, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 1.2.4",
+                       &perform_card_apdu_data_124, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 1.2.5",
+                       &perform_card_apdu_data_125, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 1.5.1",
+                       &perform_card_apdu_data_151, test_perform_card_apdu);
+       g_test_add_data_func("/teststk/Perform Card APDU 2.1.1",
+                       &perform_card_apdu_data_211, test_perform_card_apdu);
+
+       g_test_add_data_func("/teststk/Get Reader Status 1.1.1",
+                       &get_reader_status_data_111, test_get_reader_status);
+
+       g_test_add_data_func("/teststk/Timer Management 1.1.1",
+                       &timer_mgmt_data_111, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.1.2",
+                       &timer_mgmt_data_112, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.1.3",
+                       &timer_mgmt_data_113, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.1.4",
+                       &timer_mgmt_data_114, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.2.1",
+                       &timer_mgmt_data_121, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.2.2",
+                       &timer_mgmt_data_122, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.2.3",
+                       &timer_mgmt_data_123, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.2.4",
+                       &timer_mgmt_data_124, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.3.1",
+                       &timer_mgmt_data_131, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.3.2",
+                       &timer_mgmt_data_132, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.3.3",
+                       &timer_mgmt_data_133, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.3.4",
+                       &timer_mgmt_data_134, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.1",
+                       &timer_mgmt_data_141, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.2",
+                       &timer_mgmt_data_142, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.3",
+                       &timer_mgmt_data_143, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.4",
+                       &timer_mgmt_data_144, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.5",
+                       &timer_mgmt_data_145, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.6",
+                       &timer_mgmt_data_146, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.7",
+                       &timer_mgmt_data_147, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.4.8",
+                       &timer_mgmt_data_148, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.1",
+                       &timer_mgmt_data_151, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.2",
+                       &timer_mgmt_data_152, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.3",
+                       &timer_mgmt_data_153, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.4",
+                       &timer_mgmt_data_154, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.5",
+                       &timer_mgmt_data_155, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.6",
+                       &timer_mgmt_data_156, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.7",
+                       &timer_mgmt_data_157, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.5.8",
+                       &timer_mgmt_data_158, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.1",
+                       &timer_mgmt_data_161, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.2",
+                       &timer_mgmt_data_162, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.3",
+                       &timer_mgmt_data_163, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.4",
+                       &timer_mgmt_data_164, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.5",
+                       &timer_mgmt_data_165, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.6",
+                       &timer_mgmt_data_166, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.7",
+                       &timer_mgmt_data_167, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 1.6.8",
+                       &timer_mgmt_data_168, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 2.1.1",
+                       &timer_mgmt_data_211, test_timer_mgmt);
+       g_test_add_data_func("/teststk/Timer Management 2.2.1",
+                       &timer_mgmt_data_221, test_timer_mgmt);
+
+       g_test_add_data_func("/teststk/Timer Management response 1.1.1",
+                       &timer_mgmt_response_data_111,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.1.2",
+                       &timer_mgmt_response_data_112,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.1.4",
+                       &timer_mgmt_response_data_114,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.2.1",
+                       &timer_mgmt_response_data_121,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.2.2",
+                       &timer_mgmt_response_data_122,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.2.4",
+                       &timer_mgmt_response_data_124,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.3.1",
+                       &timer_mgmt_response_data_131,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.3.2",
+                       &timer_mgmt_response_data_132,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.3.4",
+                       &timer_mgmt_response_data_134,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.1A",
+                       &timer_mgmt_response_data_141a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.1B",
+                       &timer_mgmt_response_data_141b,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.2A",
+                       &timer_mgmt_response_data_142a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.3A",
+                       &timer_mgmt_response_data_143a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.4A",
+                       &timer_mgmt_response_data_144a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.5A",
+                       &timer_mgmt_response_data_145a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.6A",
+                       &timer_mgmt_response_data_146a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.7A",
+                       &timer_mgmt_response_data_147a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.4.8A",
+                       &timer_mgmt_response_data_148a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.1A",
+                       &timer_mgmt_response_data_151a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.1B",
+                       &timer_mgmt_response_data_151b,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.2A",
+                       &timer_mgmt_response_data_152a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.3A",
+                       &timer_mgmt_response_data_153a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.4A",
+                       &timer_mgmt_response_data_154a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.5A",
+                       &timer_mgmt_response_data_155a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.6A",
+                       &timer_mgmt_response_data_156a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.7A",
+                       &timer_mgmt_response_data_157a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.5.8A",
+                       &timer_mgmt_response_data_158a,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.6.3",
+                       &timer_mgmt_response_data_163,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.6.4",
+                       &timer_mgmt_response_data_164,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.6.5",
+                       &timer_mgmt_response_data_165,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.6.6",
+                       &timer_mgmt_response_data_166,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Timer Management response 1.6.7",
+                       &timer_mgmt_response_data_167,
+                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 1.1.1",
+               &setup_idle_mode_text_data_111, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 1.2.1",
+               &setup_idle_mode_text_data_121, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 1.3.1",
+               &setup_idle_mode_text_data_131, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 1.7.1",
+               &setup_idle_mode_text_data_171, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 2.1.1",
+               &setup_idle_mode_text_data_211, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 2.2.1",
+               &setup_idle_mode_text_data_221, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 2.3.1",
+               &setup_idle_mode_text_data_231, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 2.4.1",
+               &setup_idle_mode_text_data_241, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 3.1.1",
+               &setup_idle_mode_text_data_311, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.1.1",
+               &setup_idle_mode_text_data_411, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.1.2",
+               &setup_idle_mode_text_data_412, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.2.1",
+               &setup_idle_mode_text_data_421, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.2.2",
+               &setup_idle_mode_text_data_422, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.3.1",
+               &setup_idle_mode_text_data_431, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.3.2",
+               &setup_idle_mode_text_data_432, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.4.1",
+               &setup_idle_mode_text_data_441, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.4.2",
+               &setup_idle_mode_text_data_442, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.4.3",
+               &setup_idle_mode_text_data_443, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.5.1",
+               &setup_idle_mode_text_data_451, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.5.2",
+               &setup_idle_mode_text_data_452, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.5.3",
+               &setup_idle_mode_text_data_453, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.6.1",
+               &setup_idle_mode_text_data_461, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.6.2",
+               &setup_idle_mode_text_data_462, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.6.3",
+               &setup_idle_mode_text_data_463, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.7.1",
+               &setup_idle_mode_text_data_471, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.7.2",
+               &setup_idle_mode_text_data_472, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.7.3",
+               &setup_idle_mode_text_data_473, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.8.1",
+               &setup_idle_mode_text_data_481, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.8.2",
+               &setup_idle_mode_text_data_482, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.8.3",
+               &setup_idle_mode_text_data_483, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.9.1",
+               &setup_idle_mode_text_data_491, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.9.2",
+               &setup_idle_mode_text_data_492, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.9.3",
+               &setup_idle_mode_text_data_493, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.10.1",
+               &setup_idle_mode_text_data_4101, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 4.10.2",
+               &setup_idle_mode_text_data_4102, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 5.1.1",
+               &setup_idle_mode_text_data_511, test_setup_idle_mode_text);
+       g_test_add_data_func("/teststk/Setup Idle Mode Text 6.1.1",
+               &setup_idle_mode_text_data_611, test_setup_idle_mode_text);
+
+       g_test_add_data_func("/teststk/Set Up Idle Mode Text response 1.1.1",
+                       &set_up_idle_mode_text_response_data_111,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Idle Mode Text response 2.1.1B",
+                       &set_up_idle_mode_text_response_data_211b,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Set Up Idle Mode Text response 2.4.1",
+                       &set_up_idle_mode_text_response_data_241,
+                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Run At Command 1.1.1",
+                       &run_at_command_data_111, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 1.2.1",
+                       &run_at_command_data_121, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 1.3.1",
+                       &run_at_command_data_131, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 2.1.1",
+                       &run_at_command_data_211, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 2.2.1",
+                       &run_at_command_data_221, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 2.3.1",
+                       &run_at_command_data_231, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 2.4.1",
+                       &run_at_command_data_241, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 2.5.1",
+                       &run_at_command_data_251, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.1.1",
+                       &run_at_command_data_311, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.1.2",
+                       &run_at_command_data_312, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.2.1",
+                       &run_at_command_data_321, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.2.2",
+                       &run_at_command_data_322, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.3.1",
+                       &run_at_command_data_331, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.3.2",
+                       &run_at_command_data_332, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.4.1",
+                       &run_at_command_data_341, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.4.2",
+                       &run_at_command_data_342, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.4.3",
+                       &run_at_command_data_343, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.5.1",
+                       &run_at_command_data_351, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.5.2",
+                       &run_at_command_data_352, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.5.3",
+                       &run_at_command_data_353, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.6.1",
+                       &run_at_command_data_361, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.6.2",
+                       &run_at_command_data_362, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.6.3",
+                       &run_at_command_data_363, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.7.1",
+                       &run_at_command_data_371, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.7.2",
+                       &run_at_command_data_372, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.7.3",
+                       &run_at_command_data_373, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.8.1",
+                       &run_at_command_data_381, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.8.2",
+                       &run_at_command_data_382, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.8.3",
+                       &run_at_command_data_383, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.9.1",
+                       &run_at_command_data_391, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.9.2",
+                       &run_at_command_data_392, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.9.3",
+                       &run_at_command_data_393, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.10.1",
+                       &run_at_command_data_3101, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 3.10.2",
+                       &run_at_command_data_3102, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 4.1.1",
+                       &run_at_command_data_411, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 5.1.1",
+                       &run_at_command_data_511, test_run_at_command);
+       g_test_add_data_func("/teststk/Run At Command 6.1.1",
+                       &run_at_command_data_611, test_run_at_command);
+
+       g_test_add_data_func("/teststk/Run AT Command response 1.1.1",
+                       &run_at_command_response_data_111,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Run AT Command response 2.1.1B",
+                       &run_at_command_response_data_211b,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Run AT Command response 2.5.1",
+                       &run_at_command_response_data_251,
+                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Send DTMF 1.1.1",
+                       &send_dtmf_data_111, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 1.2.1",
+                       &send_dtmf_data_121, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 1.3.1",
+                       &send_dtmf_data_131, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 2.1.1",
+                       &send_dtmf_data_211, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 2.2.1",
+                       &send_dtmf_data_221, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 2.3.1",
+                       &send_dtmf_data_231, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 3.1.1",
+                       &send_dtmf_data_311, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.1.1",
+                       &send_dtmf_data_411, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.1.2",
+                       &send_dtmf_data_412, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.2.1",
+                       &send_dtmf_data_421, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.2.2",
+                       &send_dtmf_data_422, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.3.1",
+                       &send_dtmf_data_431, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.3.2",
+                       &send_dtmf_data_432, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.4.1",
+                       &send_dtmf_data_441, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.4.2",
+                       &send_dtmf_data_442, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.4.3",
+                       &send_dtmf_data_443, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.5.1",
+                       &send_dtmf_data_451, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.5.2",
+                       &send_dtmf_data_452, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.5.3",
+                       &send_dtmf_data_453, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.6.1",
+                       &send_dtmf_data_461, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.6.2",
+                       &send_dtmf_data_462, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.6.3",
+                       &send_dtmf_data_463, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.7.1",
+                       &send_dtmf_data_471, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.7.2",
+                       &send_dtmf_data_472, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.7.3",
+                       &send_dtmf_data_473, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.8.1",
+                       &send_dtmf_data_481, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.8.2",
+                       &send_dtmf_data_482, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.8.3",
+                       &send_dtmf_data_483, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.9.1",
+                       &send_dtmf_data_491, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.9.2",
+                       &send_dtmf_data_492, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.9.3",
+                       &send_dtmf_data_493, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.10.1",
+                       &send_dtmf_data_4101, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 4.10.2",
+                       &send_dtmf_data_4102, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 5.1.1",
+                       &send_dtmf_data_511, test_send_dtmf);
+       g_test_add_data_func("/teststk/Send DTMF 6.1.1",
+                       &send_dtmf_data_611, test_send_dtmf);
+
+       g_test_add_data_func("/teststk/Send DTMF response 1.1.1",
+                       &send_dtmf_response_data_111,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Send DTMF response 1.4.1",
+                       &send_dtmf_response_data_141,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Send DTMF response 2.1.1B",
+                       &send_dtmf_response_data_211b,
+                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Language Notification 1.1.1",
+               &language_notification_data_111, test_language_notification);
+       g_test_add_data_func("/teststk/Language Notification 1.2.1",
+               &language_notification_data_121, test_language_notification);
+
+       g_test_add_data_func("/teststk/Language Notification response 1.1.1",
+                       &language_notification_response_data_111,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Language Notification response 1.2.1",
+                       &language_notification_response_data_121,
+                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Launch Browser 1.1.1",
+                               &launch_browser_data_111, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 1.2.1",
+                               &launch_browser_data_121, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 1.3.1",
+                               &launch_browser_data_131, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 1.4.1",
+                               &launch_browser_data_141, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 2.1.1",
+                               &launch_browser_data_211, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 2.2.1",
+                               &launch_browser_data_221, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 2.3.1",
+                               &launch_browser_data_231, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 3.1.1",
+                               &launch_browser_data_311, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 4.1.1",
+                               &launch_browser_data_411, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 4.2.1",
+                               &launch_browser_data_421, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.1.1",
+                               &launch_browser_data_511, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.1.2",
+                               &launch_browser_data_512, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.2.1",
+                               &launch_browser_data_521, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.2.2",
+                               &launch_browser_data_522, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.3.1",
+                               &launch_browser_data_531, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.3.2",
+                               &launch_browser_data_532, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.4.1",
+                               &launch_browser_data_541, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.4.2",
+                               &launch_browser_data_542, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.4.3",
+                               &launch_browser_data_543, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.5.1",
+                               &launch_browser_data_551, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.5.2",
+                               &launch_browser_data_552, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.5.3",
+                               &launch_browser_data_553, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.6.1",
+                               &launch_browser_data_561, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.6.2",
+                               &launch_browser_data_562, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.6.3",
+                               &launch_browser_data_563, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.7.1",
+                               &launch_browser_data_571, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.7.2",
+                               &launch_browser_data_572, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.7.3",
+                               &launch_browser_data_573, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.8.1",
+                               &launch_browser_data_581, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.8.2",
+                               &launch_browser_data_582, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.8.3",
+                               &launch_browser_data_583, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.9.1",
+                               &launch_browser_data_591, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.9.2",
+                               &launch_browser_data_592, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.9.3",
+                               &launch_browser_data_593, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.10.1",
+                               &launch_browser_data_5101, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 5.10.2",
+                               &launch_browser_data_5102, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 6.1.1",
+                               &launch_browser_data_611, test_launch_browser);
+       g_test_add_data_func("/teststk/Launch Browser 7.1.1",
+                               &launch_browser_data_711, test_launch_browser);
+
+       g_test_add_data_func("/teststk/Launch Browser response 1.1.1",
+                       &launch_browser_response_data_111,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Launch Browser response 2.1.1",
+                       &launch_browser_response_data_211,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Launch Browser response 2.2.1",
+                       &launch_browser_response_data_221,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Launch Browser response 2.3.1",
+                       &launch_browser_response_data_231,
+                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Launch Browser response 4.1.1B",
+                       &launch_browser_response_data_411b,
+                       test_terminal_response_encoding);
+
+
+       g_test_add_data_func("/teststk/Open channel 2.1.1",
+                               &open_channel_data_211, test_open_channel);
+       g_test_add_data_func("/teststk/Open channel 2.2.1",
+                               &open_channel_data_221, test_open_channel);
+       g_test_add_data_func("/teststk/Open channel 2.3.1",
+                               &open_channel_data_231, test_open_channel);
+       g_test_add_data_func("/teststk/Open channel 2.4.1",
+                               &open_channel_data_241, test_open_channel);
+       g_test_add_data_func("/teststk/Open channel 5.1.1",
+                               &open_channel_data_511, test_open_channel);
+       g_test_add_data_func("/teststk/Open channel response 2.1.1",
+                               &open_channel_response_data_211,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Open channel response 2.7.1",
+                               &open_channel_response_data_271,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Close channel 1.1.1",
+                               &close_channel_data_111, test_close_channel);
+       g_test_add_data_func("/teststk/Close channel 2.1.1",
+                               &close_channel_data_211, test_close_channel);
+       g_test_add_data_func("/teststk/Close channel response 1.2.1",
+                               &close_channel_response_data_121,
+                               test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Close channel response 1.3.1",
+                               &close_channel_response_data_131,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Receive data 1.1.1",
+                               &receive_data_data_111, test_receive_data);
+       g_test_add_data_func("/teststk/Receive data 2.1.1",
+                               &receive_data_data_211, test_receive_data);
+       g_test_add_data_func("/teststk/Receive data response 1.1.1",
+                               &receive_data_response_data_111,
+                               test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Send data 1.1.1",
+                                       &send_data_data_111, test_send_data);
+       g_test_add_data_func("/teststk/Send data 1.2.1",
+                                       &send_data_data_121, test_send_data);
+       g_test_add_data_func("/teststk/Send data 2.1.1",
+                                       &send_data_data_211, test_send_data);
+       g_test_add_data_func("/teststk/Send data response 1.1.1",
+                                       &send_data_response_data_111,
+                                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Send data response 1.2.1",
+                                       &send_data_response_data_121,
+                                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Send data response 1.5.1",
+                                       &send_data_response_data_151,
+                                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/Get Channel status 1.1.1",
+                       &get_channel_status_data_111, test_get_channel_status);
+       g_test_add_data_func("/teststk/Get Channel status response 1.1.1",
+                                       &get_channel_status_response_data_111,
+                                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Channel status response 1.2.1",
+                                       &get_channel_status_response_data_121,
+                                       test_terminal_response_encoding);
+       g_test_add_data_func("/teststk/Get Channel status response 1.3.1",
+                                       &get_channel_status_response_data_131,
+                                       test_terminal_response_encoding);
+
+       g_test_add_data_func("/teststk/SMS-PP data download 1.6.1",
+                       &sms_pp_data_download_data_161,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/SMS-PP data download 1.6.2",
+                       &sms_pp_data_download_data_162,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/SMS-PP data download 1.8.2",
+                       &sms_pp_data_download_data_182,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/CBS-PP data download 1.1",
+                       &cbs_pp_data_download_data_11, test_envelope_encoding);
+       g_test_add_data_func("/teststk/CBS-PP data download 1.7",
+                       &cbs_pp_data_download_data_17, test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Menu Selection 1.1.1",
+                       &menu_selection_data_111, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Menu Selection 1.1.2",
+                       &menu_selection_data_112, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Menu Selection 1.2.1",
+                       &menu_selection_data_121, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Menu Selection 1.2.2",
+                       &menu_selection_data_122, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Menu Selection 1.2.3",
+                       &menu_selection_data_123, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Menu Selection 2.1.1",
+                       &menu_selection_data_211, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Menu Selection 6.1.2",
+                       &menu_selection_data_612, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Menu Selection 6.4.1",
+                       &menu_selection_data_641, test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Call Control 1.1.1A",
+                       &call_control_data_111a, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Call Control 1.1.1B",
+                       &call_control_data_111b, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Call Control 1.3.1A",
+                       &call_control_data_131a, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Call Control 1.3.1B",
+                       &call_control_data_131b, test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/MO Short Message Control 1.1.1A",
+                       &mo_short_message_control_data_111a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/MO Short Message Control 1.1.1B",
+                       &mo_short_message_control_data_111b,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: MT Call 1.1.1",
+                       &event_download_mt_call_data_111,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: MT Call 1.1.2",
+                       &event_download_mt_call_data_112,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Call Connected 1.1.1",
+                       &event_download_call_connected_data_111,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Connected 1.1.2",
+                       &event_download_call_connected_data_112,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.1",
+                       &event_download_call_disconnected_data_111,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.2A",
+                       &event_download_call_disconnected_data_112a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.2B",
+                       &event_download_call_disconnected_data_112b,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.2C",
+                       &event_download_call_disconnected_data_112c,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.3A",
+                       &event_download_call_disconnected_data_113a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.3B",
+                       &event_download_call_disconnected_data_113b,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.4A",
+                       &event_download_call_disconnected_data_114a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.4B",
+                       &event_download_call_disconnected_data_114b,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Location Status 1.1.1",
+                       &event_download_location_status_data_111,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Location Status 1.1.2A",
+                       &event_download_location_status_data_112a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Location Status 1.1.2B",
+                       &event_download_location_status_data_112b,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Location Status 1.2.2",
+                       &event_download_location_status_data_122,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: User Activity 1.1.1",
+                       &event_download_user_activity_data_111,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Idle Screen Available 1.1.1",
+                       &event_download_idle_screen_available_data_111,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1A",
+                       &event_download_card_reader_status_data_111a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1B",
+                       &event_download_card_reader_status_data_111b,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1C",
+                       &event_download_card_reader_status_data_111c,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1D",
+                       &event_download_card_reader_status_data_111d,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2A",
+                       &event_download_card_reader_status_data_112a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2B",
+                       &event_download_card_reader_status_data_112b,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2C",
+                       &event_download_card_reader_status_data_112c,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2D",
+                       &event_download_card_reader_status_data_112d,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 2.1.2A",
+                       &event_download_card_reader_status_data_212a,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Card Reader Status 2.1.2B",
+                       &event_download_card_reader_status_data_212b,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Language Selection 1.1.1",
+                       &event_download_language_selection_data_111,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Language Selection 1.2.2",
+                       &event_download_language_selection_data_122,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Browser Termination 1.1.1",
+                       &event_download_browser_termination_data_111,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Data Available 1.1.1",
+                       &event_download_data_available_data_111,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Data Available 2.1.1",
+                       &event_download_data_available_data_211,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Channel Status 1.3.1",
+                       &event_download_channel_status_data_131,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Channel Status 2.1.1",
+                       &event_download_channel_status_data_211,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Channel Status 2.2.1",
+                       &event_download_channel_status_data_221,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Event: Network Rejection 1.1.1",
+                       &event_download_network_rejection_data_111,
+                       test_envelope_encoding);
+       g_test_add_data_func("/teststk/Event: Network Rejection 1.2.1",
+                       &event_download_network_rejection_data_121,
+                       test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/Timer Expiration 2.1.1",
+                       &timer_expiration_data_211, test_envelope_encoding);
+       g_test_add_data_func("/teststk/Timer Expiration 2.2.1A",
+                       &timer_expiration_data_221a, test_envelope_encoding);
+
+       g_test_add_data_func("/teststk/HTML Attribute Test 1",
+                               &html_attr_data_1, test_html_attr);
+       g_test_add_data_func("/teststk/HTML Attribute Test 2",
+                               &html_attr_data_2, test_html_attr);
+       g_test_add_data_func("/teststk/HTML Attribute Test 3",
+                               &html_attr_data_3, test_html_attr);
+       g_test_add_data_func("/teststk/HTML Attribute Test 4",
+                               &html_attr_data_4, test_html_attr);
+
+       g_test_add_data_func("/teststk/IMG to XPM Test 1",
+                               &xpm_test_1, test_img_to_xpm);
+       g_test_add_data_func("/teststk/IMG to XPM Test 2",
+                               &xpm_test_2, test_img_to_xpm);
+       g_test_add_data_func("/teststk/IMG to XPM Test 3",
+                               &xpm_test_3, test_img_to_xpm);
+       g_test_add_data_func("/teststk/IMG to XPM Test 4",
+                               &xpm_test_4, test_img_to_xpm);
+       g_test_add_data_func("/teststk/IMG to XPM Test 5",
+                               &xpm_test_5, test_img_to_xpm);
+       g_test_add_data_func("/teststk/IMG to XPM Test 6",
+                               &xpm_test_6, test_img_to_xpm);
+
+       return g_test_run();
+}
diff --git a/unit/test-util.c b/unit/test-util.c
new file mode 100644 (file)
index 0000000..481c123
--- /dev/null
@@ -0,0 +1,1012 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <glib.h>
+
+#include "util.h"
+
+const unsigned char invalid_gsm_extended[] = {
+       0x1b, 0x15
+};
+
+const unsigned char invalid_gsm_extended_len[] = {
+       0x1b, 0x28, 0x1b
+};
+
+const unsigned char invalid_ucs2[] = {
+       0x03, 0x93, 0x00, 0x00
+};
+
+unsigned short gsm_to_unicode_map[] =
+{
+0x00,  0x0040,
+0x01,  0x00A3,
+0x02,  0x0024,
+0x03,  0x00A5,
+0x04,  0x00E8,
+0x05,  0x00E9,
+0x06,  0x00F9,
+0x07,  0x00EC,
+0x08,  0x00F2,
+0x09,  0x00C7,
+0x0A,  0x000A,
+0x0B,  0x00D8,
+0x0C,  0x00F8,
+0x0D,  0x000D,
+0x0E,  0x00C5,
+0x0F,  0x00E5,
+0x10,  0x0394,
+0x11,  0x005F,
+0x12,  0x03A6,
+0x13,  0x0393,
+0x14,  0x039B,
+0x15,  0x03A9,
+0x16,  0x03A0,
+0x17,  0x03A8,
+0x18,  0x03A3,
+0x19,  0x0398,
+0x1A,  0x039E,
+/*0x1B,        0x00A0,*/
+0x1B0A,        0x000C,
+0x1B14,        0x005E,
+0x1B28,        0x007B,
+0x1B29,        0x007D,
+0x1B2F,        0x005C,
+0x1B3C,        0x005B,
+0x1B3D,        0x007E,
+0x1B3E,        0x005D,
+0x1B40,        0x007C,
+0x1B65,        0x20AC,
+0x1C,  0x00C6,
+0x1D,  0x00E6,
+0x1E,  0x00DF,
+0x1F,  0x00C9,
+0x20,  0x0020,
+0x21,  0x0021,
+0x22,  0x0022,
+0x23,  0x0023,
+0x24,  0x00A4,
+0x25,  0x0025,
+0x26,  0x0026,
+0x27,  0x0027,
+0x28,  0x0028,
+0x29,  0x0029,
+0x2A,  0x002A,
+0x2B,  0x002B,
+0x2C,  0x002C,
+0x2D,  0x002D,
+0x2E,  0x002E,
+0x2F,  0x002F,
+0x30,  0x0030,
+0x31,  0x0031,
+0x32,  0x0032,
+0x33,  0x0033,
+0x34,  0x0034,
+0x35,  0x0035,
+0x36,  0x0036,
+0x37,  0x0037,
+0x38,  0x0038,
+0x39,  0x0039,
+0x3A,  0x003A,
+0x3B,  0x003B,
+0x3C,  0x003C,
+0x3D,  0x003D,
+0x3E,  0x003E,
+0x3F,  0x003F,
+0x40,  0x00A1,
+0x41,  0x0041,
+0x42,  0x0042,
+0x43,  0x0043,
+0x44,  0x0044,
+0x45,  0x0045,
+0x46,  0x0046,
+0x47,  0x0047,
+0x48,  0x0048,
+0x49,  0x0049,
+0x4A,  0x004A,
+0x4B,  0x004B,
+0x4C,  0x004C,
+0x4D,  0x004D,
+0x4E,  0x004E,
+0x4F,  0x004F,
+0x50,  0x0050,
+0x51,  0x0051,
+0x52,  0x0052,
+0x53,  0x0053,
+0x54,  0x0054,
+0x55,  0x0055,
+0x56,  0x0056,
+0x57,  0x0057,
+0x58,  0x0058,
+0x59,  0x0059,
+0x5A,  0x005A,
+0x5B,  0x00C4,
+0x5C,  0x00D6,
+0x5D,  0x00D1,
+0x5E,  0x00DC,
+0x5F,  0x00A7,
+0x60,  0x00BF,
+0x61,  0x0061,
+0x62,  0x0062,
+0x63,  0x0063,
+0x64,  0x0064,
+0x65,  0x0065,
+0x66,  0x0066,
+0x67,  0x0067,
+0x68,  0x0068,
+0x69,  0x0069,
+0x6A,  0x006A,
+0x6B,  0x006B,
+0x6C,  0x006C,
+0x6D,  0x006D,
+0x6E,  0x006E,
+0x6F,  0x006F,
+0x70,  0x0070,
+0x71,  0x0071,
+0x72,  0x0072,
+0x73,  0x0073,
+0x74,  0x0074,
+0x75,  0x0075,
+0x76,  0x0076,
+0x77,  0x0077,
+0x78,  0x0078,
+0x79,  0x0079,
+0x7A,  0x007A,
+0x7B,  0x00E4,
+0x7C,  0x00F6,
+0x7D,  0x00F1,
+0x7E,  0x00FC,
+0x7F,  0x00E0,
+};
+
+unsigned short gsm_turkish_to_unicode_map[] =
+{
+0x00, 0x0040,
+0x01, 0x00A3,
+0x02, 0x0024,
+0x03, 0x00A5,
+0x04, 0x20AC,
+0x05, 0x00E9,
+0x06, 0x00F9,
+0x07, 0x0131,
+0x08, 0x00F2,
+0x09, 0x00C7,
+0x0A, 0x000A,
+0x0B, 0x011E,
+0x0C, 0x011F,
+0x0D, 0x000D,
+0x0E, 0x00C5,
+0x0F, 0x00E5,
+0x10, 0x0394,
+0x11, 0x005F,
+0x12, 0x03A6,
+0x13, 0x0393,
+0x14, 0x039B,
+0x15, 0x03A9,
+0x16, 0x03A0,
+0x17, 0x03A8,
+0x18, 0x03A3,
+0x19, 0x0398,
+0x1A, 0x039E,
+/* We're not including some of the single shift codes to this map,
+* because the turkish variant isn't symmetric, i.e., the same
+* character is present in both the locking shift table as well as the
+* single shift table */
+0x1B0A, 0x000C,
+0x1B14, 0x005E,
+0x1B28, 0x007B,
+0x1B29, 0x007D,
+0x1B2F, 0x005C,
+0x1B3C, 0x005B,
+0x1B3D, 0x007E,
+0x1B3E, 0x005D,
+0x1B40, 0x007C,
+/*0x1B47, 0x011E,*/
+/*0x1B49, 0x0130,*/
+/*0x1B53, 0x015E,*/
+/*0x1B63, 0x00E7,*/
+/*0x1B65, 0x20AC,*/
+/*0x1B67, 0x011F,*/
+/*0x1B69, 0x0131,*/
+/*0x1B73, 0x015F,*/
+0x1C, 0x015E,
+0x1D, 0x015F,
+0x1E, 0x00DF,
+0x1F, 0x00C9,
+0x20, 0x0020,
+0x21, 0x0021,
+0x22, 0x0022,
+0x23, 0x0023,
+0x24, 0x00A4,
+0x25, 0x0025,
+0x26, 0x0026,
+0x27, 0x0027,
+0x28, 0x0028,
+0x29, 0x0029,
+0x2A, 0x002A,
+0x2B, 0x002B,
+0x2C, 0x002C,
+0x2D, 0x002D,
+0x2E, 0x002E,
+0x2F, 0x002F,
+0x30, 0x0030,
+0x31, 0x0031,
+0x32, 0x0032,
+0x33, 0x0033,
+0x34, 0x0034,
+0x35, 0x0035,
+0x36, 0x0036,
+0x37, 0x0037,
+0x38, 0x0038,
+0x39, 0x0039,
+0x40, 0x0130,
+0x3A, 0x003A,
+0x3B, 0x003B,
+0x3C, 0x003C,
+0x3D, 0x003D,
+0x3E, 0x003E,
+0x3F, 0x003F,
+0x40, 0x0130,
+0x41, 0x0041,
+0x42, 0x0042,
+0x43, 0x0043,
+0x44, 0x0044,
+0x45, 0x0045,
+0x46, 0x0046,
+0x47, 0x0047,
+0x48, 0x0048,
+0x49, 0x0049,
+0x4A, 0x004A,
+0x4B, 0x004B,
+0x4C, 0x004C,
+0x4D, 0x004D,
+0x4E, 0x004E,
+0x4F, 0x004F,
+0x50, 0x0050,
+0x51, 0x0051,
+0x52, 0x0052,
+0x53, 0x0053,
+0x54, 0x0054,
+0x55, 0x0055,
+0x56, 0x0056,
+0x57, 0x0057,
+0x58, 0x0058,
+0x59, 0x0059,
+0x5A, 0x005A,
+0x5B, 0x00C4,
+0x5C, 0x00D6,
+0x5D, 0x00D1,
+0x5E, 0x00DC,
+0x5F, 0x00A7,
+0x60, 0x00E7,
+0x61, 0x0061,
+0x62, 0x0062,
+0x63, 0x0063,
+0x64, 0x0064,
+0x65, 0x0065,
+0x66, 0x0066,
+0x67, 0x0067,
+0x68, 0x0068,
+0x69, 0x0069,
+0x6A, 0x006A,
+0x6B, 0x006B,
+0x6C, 0x006C,
+0x6D, 0x006D,
+0x6E, 0x006E,
+0x6F, 0x006F,
+0x70, 0x0070,
+0x71, 0x0071,
+0x72, 0x0072,
+0x73, 0x0073,
+0x74, 0x0074,
+0x75, 0x0075,
+0x76, 0x0076,
+0x77, 0x0077,
+0x78, 0x0078,
+0x79, 0x0079,
+0x7A, 0x007A,
+0x7B, 0x00E4,
+0x7C, 0x00F6,
+0x7D, 0x00F1,
+0x7E, 0x00FC,
+0x7F, 0x00E0
+};
+
+#define UTF8_LENGTH(c) \
+       ((c) < 0x80 ? 1 : \
+        ((c) < 0x800 ? 2 : 3))
+
+static void test_invalid(void)
+{
+       long nwritten;
+       long nread;
+       char *res;
+       unsigned char *gsm;
+
+       res = convert_gsm_to_utf8(invalid_gsm_extended, 0, &nread, &nwritten,
+                                       0);
+       g_assert(res);
+       g_assert(nread == 0);
+       g_assert(nwritten == 0);
+       g_assert(res[0] == '\0');
+       g_free(res);
+
+       res = convert_gsm_to_utf8(invalid_gsm_extended,
+                                       sizeof(invalid_gsm_extended),
+                                       &nread, &nwritten, 0);
+       g_assert(res == NULL);
+       g_assert(nread == 1);
+
+       res = convert_gsm_to_utf8(invalid_gsm_extended_len,
+                                       sizeof(invalid_gsm_extended_len),
+                                       &nread, &nwritten, 0);
+       g_assert(res == NULL);
+       g_assert(nread == 3);
+
+       gsm = convert_ucs2_to_gsm(invalid_ucs2,
+                                       sizeof(invalid_ucs2),
+                                       &nread, &nwritten, 0);
+       g_assert(gsm == NULL);
+       g_assert(nread == 2);
+
+       nread = 0;
+       gsm = convert_ucs2_to_gsm(invalid_ucs2,
+                                       sizeof(invalid_ucs2) - 1,
+                                       &nread, &nwritten, 0);
+       g_assert(gsm == NULL);
+       g_assert(nread == 0);
+}
+
+static void test_valid(void)
+{
+       long nwritten;
+       long nread;
+       char *res;
+       int i;
+       long size;
+       gunichar *verify;
+       unsigned char *back;
+
+       unsigned char buf[2];
+
+       static int map_size =
+               sizeof(gsm_to_unicode_map) / sizeof(unsigned short) / 2;
+
+       for (i = 0; i < map_size; i++) {
+               unsigned short c = gsm_to_unicode_map[i*2];
+
+               if (c & 0x1b00) {
+                       buf[0] = 0x1b;
+                       buf[1] = c & 0x7f;
+                       size = 2;
+               } else {
+                       size = 1;
+                       buf[0] = c & 0x7f;
+               }
+
+               res = convert_gsm_to_utf8(buf, size, &nread, &nwritten, 0);
+               g_assert(res);
+
+               if (g_test_verbose())
+                       g_print("size: %ld, nread:%ld, nwritten:%ld, %s\n",
+                               size, nread, nwritten, res);
+
+               g_assert(nread == size);
+
+               verify = g_utf8_to_ucs4(res, -1, NULL, NULL, NULL);
+
+               g_assert(verify[0] == gsm_to_unicode_map[i*2+1]);
+               g_assert(verify[1] == 0);
+
+               g_assert(nwritten == UTF8_LENGTH(verify[0]));
+
+               back = convert_utf8_to_gsm(res, -1, &nread, &nwritten, 0);
+
+               g_assert(back);
+
+               g_assert(nwritten == size);
+
+               if (c & 0x1b00) {
+                       g_assert(back[0] == 0x1b);
+                       g_assert(back[1] == (c & 0x7f));
+               } else {
+                       g_assert(back[0] == (c & 0x7f));
+               }
+
+               g_free(back);
+               g_free(verify);
+               g_free(res);
+       }
+}
+
+static void test_valid_turkish(void)
+{
+       long nwritten;
+       long nread;
+       char *res;
+       int i;
+       long size;
+       gunichar *verify;
+       unsigned char *back;
+
+       unsigned char buf[2];
+
+       static int map_size =
+               sizeof(gsm_turkish_to_unicode_map) / sizeof(unsigned short) / 2;
+
+       for (i = 0; i < map_size; i++) {
+               unsigned short c = gsm_turkish_to_unicode_map[i*2];
+
+               if (c & 0x1b00) {
+                       buf[0] = 0x1b;
+                       buf[1] = c & 0x7f;
+                       size = 2;
+               } else {
+                       size = 1;
+                       buf[0] = c & 0x7f;
+               }
+
+               res = convert_gsm_to_utf8_with_lang(buf, size, &nread,
+                                                       &nwritten, 0, 1, 1);
+               g_assert(res);
+
+               if (g_test_verbose())
+                       g_print("size: %ld, nread:%ld, nwritten:%ld, %s\n",
+                               size, nread, nwritten, res);
+
+               g_assert(nread == size);
+
+               verify = g_utf8_to_ucs4(res, -1, NULL, NULL, NULL);
+
+               g_assert(verify[0] == gsm_turkish_to_unicode_map[i*2+1]);
+               g_assert(verify[1] == 0);
+
+               g_assert(nwritten == UTF8_LENGTH(verify[0]));
+
+               back = convert_utf8_to_gsm_with_lang(res, -1, &nread,
+                                                       &nwritten, 0, 1, 1);
+
+               g_assert(back);
+
+               g_assert(nwritten == size);
+
+               if (c & 0x1b00) {
+                       g_assert(back[0] == 0x1b);
+                       g_assert(back[1] == (c & 0x7f));
+               } else {
+                       g_assert(back[0] == (c & 0x7f));
+               }
+
+               g_free(back);
+               g_free(verify);
+               g_free(res);
+       }
+}
+
+static const char hex_packed[] = "493A283D0795C3F33C88FE06C9CB6132885EC6D34"
+                                       "1EDF27C1E3E97E7207B3A0C0A5241E377BB1D"
+                                       "7693E72E";
+static const char expected[] = "It is easy to read text messages via AT "
+                               "commands.";
+static int reported_text_size = 49;
+
+static void test_decode_encode(void)
+{
+       const char *sms = hex_packed;
+       unsigned char *decoded, *packed;
+       char *utf8, *hex_packed;
+       unsigned char *gsm, *gsm_encoded;
+       long hex_decoded_size;
+       long unpacked_size, packed_size;
+       long gsm_encoded_size;
+       long i;
+
+       if (g_test_verbose())
+               g_print("Size of the orig string: %u\n",
+                       (unsigned int)strlen(sms));
+
+       decoded = decode_hex(sms, -1, &hex_decoded_size, 0);
+
+       g_assert(decoded != NULL);
+
+       if (g_test_verbose())
+               g_print("Decode to %ld bytes\n", hex_decoded_size);
+
+       if (g_test_verbose()) {
+               g_print("%s\n", sms);
+
+               for (i = 0; i < hex_decoded_size; i++)
+                       g_print("%02X", decoded[i]);
+               g_print("\n");
+       }
+
+       gsm = unpack_7bit(decoded, hex_decoded_size, 0, FALSE,
+                               reported_text_size, &unpacked_size, 0xff);
+
+       g_assert(gsm != NULL);
+
+       if (g_test_verbose())
+               g_print("String unpacked to %ld bytes\n", unpacked_size);
+
+       utf8 = convert_gsm_to_utf8(gsm, -1, NULL, NULL, 0xff);
+
+       g_assert(utf8 != NULL);
+
+       if (g_test_verbose())
+               g_print("String is: -->%s<--\n", utf8);
+
+       g_assert(strcmp(utf8, expected) == 0);
+
+       gsm_encoded = convert_utf8_to_gsm(utf8, -1, NULL,
+                                               &gsm_encoded_size, 0xff);
+
+       g_assert(gsm_encoded != NULL);
+
+       if (g_test_verbose())
+               g_print("Converted back to GSM string of %ld bytes\n",
+                               gsm_encoded_size);
+
+       g_assert(gsm_encoded[gsm_encoded_size] == 0xff);
+       g_assert(gsm_encoded_size == unpacked_size);
+       g_assert(memcmp(gsm_encoded, gsm, gsm_encoded_size) == 0);
+
+       g_free(utf8);
+       g_free(gsm);
+
+       packed = pack_7bit(gsm_encoded, -1, 0, FALSE, &packed_size, 0xff);
+
+       g_free(gsm_encoded);
+
+       g_assert(packed != NULL);
+
+       if (g_test_verbose())
+               g_print("Packed GSM to size of %ld bytes\n", packed_size);
+
+       if (g_test_verbose()) {
+               for (i = 0; i < packed_size; i++)
+                       g_print("%02X", packed[i]);
+               g_print("\n");
+       }
+
+       g_assert(packed_size == hex_decoded_size);
+       g_assert(memcmp(packed, decoded, packed_size) == 0);
+
+       g_free(decoded);
+
+       hex_packed = encode_hex(packed, packed_size, 0);
+
+       g_assert(hex_packed != NULL);
+
+       g_free(packed);
+
+       if (g_test_verbose())
+               g_print("Hex encoded packed to size %ld bytes\n",
+                               (long)strlen(hex_packed));
+
+       g_assert(strlen(hex_packed) == strlen(sms));
+       g_assert(strcmp(hex_packed, sms) == 0);
+
+       g_free(hex_packed);
+}
+
+static void test_pack_size(void)
+{
+       unsigned char c1[] = { 'a' };
+       unsigned char c2[] = { 'a', 'b' };
+       unsigned char c3[] = { 'a', 'b', 'c' };
+       unsigned char c4[] = { 'a', 'b', 'c', 'd' };
+       unsigned char c5[] = { 'a', 'b', 'c', 'd', 'e' };
+       unsigned char c6[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
+       unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
+       unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+
+       unsigned char *packed;
+       long size;
+
+       packed = pack_7bit(c1, 1, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 1);
+       g_free(packed);
+
+       packed = pack_7bit(c2, 2, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 2);
+       g_free(packed);
+
+       packed = pack_7bit(c3, 3, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 3);
+       g_free(packed);
+
+       packed = pack_7bit(c4, 4, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 4);
+       g_free(packed);
+
+       packed = pack_7bit(c5, 5, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 5);
+       g_free(packed);
+
+       packed = pack_7bit(c6, 6, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 6);
+       g_free(packed);
+
+       packed = pack_7bit(c7, 7, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 7);
+       g_assert((packed[6] & 0xfe) == 0);
+       g_free(packed);
+
+       packed = pack_7bit(c7, 7, 0, TRUE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 7);
+       g_assert(((packed[6] & 0xfe) >> 1) == '\r');
+       g_free(packed);
+
+       packed = pack_7bit(c8, 8, 0, FALSE, &size, 0);
+       g_assert(packed != NULL);
+       g_assert(size == 7);
+       g_free(packed);
+}
+
+static void test_cr_handling(void)
+{
+       unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
+       unsigned char c7_expected[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+                                       '\r' };
+       unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '\r' };
+       unsigned char c8_expected[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+                                       '\r', '\r' };
+
+       unsigned char *packed;
+       unsigned char *unpacked;
+       long packed_size;
+       long unpacked_size;
+
+       packed = pack_7bit(c8, 8, 0, TRUE, &packed_size, 0);
+       g_assert(packed != NULL);
+       g_assert(packed_size == 8);
+       g_assert(((packed[6] & 0xfe) >> 1) == '\r');
+       g_assert((packed[7] & 0x7f) == '\r');
+
+       unpacked = unpack_7bit(packed, 8, 0, TRUE, -1, &unpacked_size, 0);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 9);
+       g_assert(memcmp(c8_expected, unpacked, 9) == 0);
+
+       g_free(unpacked);
+       g_free(packed);
+
+       packed = pack_7bit(c7, 7, 0, TRUE, &packed_size, 0);
+       g_assert(packed != NULL);
+       g_assert(packed_size == 7);
+       g_assert(((packed[6] & 0xfe) >> 1) == '\r');
+
+       unpacked = unpack_7bit(packed, 7, 0, TRUE, -1, &unpacked_size, 0);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 7);
+       g_assert(memcmp(c7, unpacked, 7) == 0);
+
+       g_free(unpacked);
+       g_free(packed);
+
+       /* As above, but now unpack using SMS style, we should now have cr at
+        * the end of the stream
+        */
+       packed = pack_7bit(c7, 7, 0, TRUE, &packed_size, 0);
+       unpacked = unpack_7bit(packed, 7, 0, FALSE, 8, &unpacked_size, 0);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 8);
+       g_assert(memcmp(c7_expected, unpacked, 8) == 0);
+
+       g_free(unpacked);
+       g_free(packed);
+}
+
+static void test_sms_handling(void)
+{
+       unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
+
+       unsigned char *packed;
+       unsigned char *unpacked;
+       long packed_size;
+       long unpacked_size;
+
+       packed = pack_7bit(c7, 7, 0, FALSE, &packed_size, 0);
+       g_assert(packed != NULL);
+       g_assert(packed_size == 7);
+
+       unpacked = unpack_7bit(packed, 7, 0, FALSE, 8, &unpacked_size, 0xff);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 8);
+       g_assert(unpacked[7] == 0);
+       g_assert(unpacked[8] == 0xff);
+
+       g_free(unpacked);
+       g_free(packed);
+
+       packed = pack_7bit(c7, 7, 0, FALSE, &packed_size, 0);
+       g_assert(packed != NULL);
+       g_assert(packed_size == 7);
+
+       unpacked = unpack_7bit(packed, 7, 0, FALSE, 7, &unpacked_size, 0xff);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 7);
+       g_assert(unpacked[7] == 0xff);
+
+       g_free(unpacked);
+       g_free(packed);
+}
+
+static void test_offset_handling(void)
+{
+       unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
+       unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
+       unsigned char c9[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };
+       unsigned char *packed;
+       unsigned char *unpacked;
+       long packed_size;
+       long unpacked_size;
+
+       /* Pack at offset = 2 bytes, e.g. starting with 21st bit */
+       packed = pack_7bit(c7, 6, 2, FALSE, &packed_size, 0);
+
+       if (g_test_verbose())
+               g_print("Packed to size: %ld\n", packed_size);
+
+       g_assert(packed != NULL);
+       g_assert(packed_size == 6);
+
+       unpacked = unpack_7bit(packed, 6, 2, FALSE, 6, &unpacked_size, 0xff);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 6);
+       g_assert(unpacked[6] == 0xff);
+       g_assert(unpacked[0] == 'a');
+       g_assert(unpacked[5] == 'f');
+
+       g_free(unpacked);
+       g_free(packed);
+
+       /* Pack at offset = 6 bytes, we should be able to fit one character
+        * into the first byte, and the other 7 characters into the following
+        * 7 bytes.  The 7 MSB bits of the last byte should be 0 since
+        * we're not using CBS packing
+        */
+       packed = pack_7bit(c8, 8, 6, FALSE, &packed_size, 0);
+
+       if (g_test_verbose())
+               g_print("Packed to size: %ld\n", packed_size);
+
+       g_assert(packed != NULL);
+       g_assert(packed_size == 8);
+
+       unpacked = unpack_7bit(packed, 8, 6, FALSE, 8, &unpacked_size, 0xff);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 8);
+       g_assert(unpacked[8] == 0xff);
+       g_assert(unpacked[0] == 'a');
+       g_assert(unpacked[7] == 'h');
+
+       g_free(unpacked);
+       g_free(packed);
+
+       /* Same as above, but instead pack in 9 characters */
+       packed = pack_7bit(c9, 9, 6, FALSE, &packed_size, 0);
+
+       if (g_test_verbose())
+               g_print("Packed to size: %ld\n", packed_size);
+
+       g_assert(packed != NULL);
+       g_assert(packed_size == 8);
+
+       unpacked = unpack_7bit(packed, 8, 6, FALSE, 9, &unpacked_size, 0xff);
+       if (g_test_verbose())
+               g_print("Unpacked to size: %ld\n", unpacked_size);
+
+       g_assert(unpacked != NULL);
+       g_assert(unpacked_size == 9);
+       g_assert(unpacked[9] == 0xff);
+       g_assert(unpacked[0] == 'a');
+       g_assert(unpacked[8] == 'i');
+
+       g_free(unpacked);
+       g_free(packed);
+}
+
+static unsigned char sim_7bit[] = { 0x6F, 0x46, 0x6F, 0x6E, 0x6F, 0xFF, 0xFF };
+static unsigned char sim_80_1[] = { 0x80, 0x00, 0x6F, 0x00, 0x6E, 0x00,
+                                       0x6F };
+static unsigned char sim_80_2[] = { 0x80, 0x00, 0x6F, 0x00, 0x6E, 0x00,
+                                       0x6F, 0xFF, 0xFF, 0xFF};
+static unsigned char sim_80_3[] = { 0x80, 0x00, 0x6F, 0x00, 0x6E, 0x00,
+                                       0x6F, 0xFF, 0xFF};
+static unsigned char sim_81_0[] = { 0x81, 0x05, 0x13, 0x53, 0x95, 0xA6,
+                                       0xA6, 0xFF, 0xFF };
+static unsigned char sim_81_1[] = { 0x81, 0x03, 0x00, 0x6F, 0x6E, 0x6F, 0xFF };
+static unsigned char sim_81_2[] = { 0x81, 0x05, 0x08, 0xB3, 0xB4, 0xB5, 0x53,
+                                       0x54, 0xFF, 0xFF, 0xFF };
+static unsigned char sim_82_0[] = { 0x82, 0x05, 0x05, 0x30, 0x2D, 0x82,
+                                       0xD3, 0x2D, 0x31 };
+static unsigned char sim_82_1[] = { 0x82, 0x05, 0x04, 0x00, 0x2D, 0xB3, 0xB4,
+                                       0x2D, 0x31 };
+static unsigned char sim_82_2[] = { 0x82, 0x05, 0xD8, 0x00, 0x2D, 0xB3, 0xB4,
+                                       0x2D, 0x31 };
+
+static void test_sim(void)
+{
+       char *utf8;
+
+       utf8 = sim_string_to_utf8(sim_7bit, sizeof(sim_7bit));
+
+       g_assert(utf8);
+       g_assert(strcmp(utf8, "oFono") == 0);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_80_1, sizeof(sim_80_1));
+       g_assert(utf8);
+       g_assert(strcmp(utf8, "ono") == 0);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_80_2, sizeof(sim_80_2));
+       g_assert(utf8);
+       g_assert(strcmp(utf8, "ono") == 0);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_80_3, sizeof(sim_80_3));
+       g_assert(utf8);
+       g_assert(strcmp(utf8, "ono") == 0);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_81_0, sizeof(sim_81_0));
+       g_assert(utf8);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_81_2, sizeof(sim_81_2));
+       g_assert(utf8);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_81_1, sizeof(sim_81_1));
+       g_assert(utf8);
+       g_assert(strcmp(utf8, "ono") == 0);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_82_0, sizeof(sim_82_0));
+       g_assert(utf8);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_82_1, sizeof(sim_82_1));
+       g_assert(utf8);
+       g_free(utf8);
+
+       utf8 = sim_string_to_utf8(sim_82_2, sizeof(sim_82_2));
+       g_assert(utf8 == NULL);
+}
+
+static void test_unicode_to_gsm(void)
+{
+       long nwritten;
+       long nread;
+       int i;
+       unsigned char *res;
+       char *utf8;
+       unsigned char buf[2];
+       unsigned char *back;
+       gunichar2 verify;
+
+       static int map_size =
+               sizeof(gsm_to_unicode_map) / sizeof(unsigned short) / 2;
+
+       for (i = 0; i < map_size; i++) {
+               unsigned short c = gsm_to_unicode_map[i*2+1];
+
+               buf[0] = c >> 8;
+               buf[1] = c & 0xff;
+
+               res = convert_ucs2_to_gsm(buf, 2, &nread, &nwritten, 0);
+               g_assert(res);
+
+               if (g_test_verbose())
+                       g_print("nread:%ld, nwritten:%ld, %s\n",
+                               nread, nwritten, res);
+
+               if (res[0] == 0x1B)
+                       g_assert(nwritten == 2);
+               else
+                       g_assert(nwritten == 1);
+
+               utf8 = g_convert((const gchar *) buf, 2,
+                               "UTF-8", "UCS-2BE",
+                               NULL, NULL, NULL);
+               g_assert(utf8);
+
+               back = convert_utf8_to_gsm(utf8, strlen(utf8), &nread,
+                                               &nwritten, 0);
+               g_assert(back);
+
+               if (back[0] == 0x1B) {
+                       g_assert(nwritten == 2);
+                       verify = back[0] << 8 | back[1];
+               } else {
+                       g_assert(nwritten == 1);
+                       verify = back[0];
+               }
+
+               if (g_test_verbose())
+                       g_print("nwritten:%ld, verify: 0x%x\n",
+                               nwritten, verify);
+
+               g_assert(verify == gsm_to_unicode_map[i*2]);
+
+               g_free(res);
+               g_free(back);
+               g_free(utf8);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/testutil/Invalid Conversions", test_invalid);
+       g_test_add_func("/testutil/Valid Conversions", test_valid);
+       g_test_add_func("/testutil/Valid Turkish National Variant Conversions",
+                       test_valid_turkish);
+       g_test_add_func("/testutil/Decode Encode", test_decode_encode);
+       g_test_add_func("/testutil/Pack Size", test_pack_size);
+       g_test_add_func("/testutil/CBS CR Handling", test_cr_handling);
+       g_test_add_func("/testutil/SMS Handling", test_sms_handling);
+       g_test_add_func("/testutil/Offset Handling", test_offset_handling);
+       g_test_add_func("/testutil/SIM conversions", test_sim);
+       g_test_add_func("/testutil/Valid Unicode to GSM Conversion",
+                       test_unicode_to_gsm);
+
+       return g_test_run();
+}