All devices should inherit from either
CDMAAdapter
or
WCDMAAdapter
depending on the device type. Both classes accept one argument for
its constructor, an instance of the class,
DBusDevicePlugin
.
A DBusDevicePlugin contains all the necessary information to speak
with the device: a data port, a control port (if exists), a baudrate,
etc. It also has the following attributes and members that you can
customise for your device plugin:
custom
: An instance or subclass ofCDMACustomizer
orWCDMACustomizer
depending on the device type.simklass
: An instance or subclass ofSIMBaseClass
.baudrate
: At what speed are we going to talk with this device (default: 115200).__remote_name__
: As some devices share the same vendor_id and product_id, we will issue anAT+GMR
command right at the beginning to find out the real device model, set this attribute to whatever your device replies to theAT+GMR
command.mapping
: A dictionary that, when is not empty, means that that particular combination of vendor and product id is shared between several models from the same company. As the IDs are the same, the only way to differentiate them is issuing anAT+GMR
command to get the device model. This dict must have an entry for each model and adefault
entry that will be used in cases where we do not know about the card model. For more information about the mapping dictionary, have a look at thewader.common.plugins.huawei_exxx
module.
The object WCDMACustomizer
or
CDMACustomizer
acts as a container for all the device-specific customizations, such as:
adapter
: Specifies the adapter that will be used to wrap theDevice
object. Use it in situations where the default adapter is not enough, such as troublesome devices with buggy commands that need a workaround.- State machines: Each device its a world on its own, and even
though they are supposed to support the relevant GSM and 3GPP
standards, some devices prefer to differ from them. The Customizer
object contains references to the state machines that the device
should use, (on situations where it applies, such as with WCDMA
devices of course):
authklass
: The state machine used to authenticate against the device, default isAuthStateMachine
netrklass
: The state machine used to register on the network, default isNetworkRegStateMachine
connklass
: The state machine used during runtime (once device is properly initialized, authenticated, etc. default isConnectStateMachine
async_regexp
: regular expression object that will match whatever pattern of unsolicited notifications the given device sends us.signal_translations
: Dictionary of tuples, each tuple has two members: the first is the signal id and the second is a function that will translate the signal arguments and the signal to the internal representation that wader uses. You can find some sample code in thehuawei
module.band_dict
: Dictionary with the AT strings necessary to switch the band technology that the device uses. The number of possible bands is limited by the device itself and the modifications that the manufacturer might have done to the device's firmware. The reference band dictionary is in theoption
module.conn_dict
: Dictionary with 5 items, each one defines the AT string that must be sent to the device in order to configure the connection mode preferences (Gprs only, 3G preferred, any, etc.) This dictionaries can be shared most of the time between different models from the same manufacturer.cmd_dict
: Dictionary with information about how each command should be processed.cmd_dict
most of the time will be a shallow copy of thecommand
dict with minor modifications about how a particular command is processed on the given device.device_capabilities
: List with all the unsolicited notifications that this device will send us. If the device sends us every RSSI change that detects, we don't need to poll manually the device for that information.
Overview of a simple DevicePlugin
Lets have a look at the NovatelXU870 plugin:
from wader.common.hardware.novatel import NovatelWCDMADevicePlugin class NovatelXU870(NovatelWCDMADevicePlugin): """L{wader.common.plugin.DBusDevicePlugin} for Novatel's XU870""" name = "Novatel XU870" version = "0.1" author = u"Pablo Martí" __remote_name__ = "Merlin XU870 ExpressCard" __properties__ = { 'usb_device.vendor_id' : [0x1410], 'usb_device.product_id' : [0x1430], } novatelxu870 = NovatelXU870()
In an ideal world, devices have a unique vendor and product id
tuple, they conform to the relevant CDMA or WCDMA specs, and that's
it. The device is identified by its vendor and product ids and
double-checked with its __remote_name__
attribute
(the response to a AT+GMR
command). This vendor and
product id tuple will usually use the usb
bus, however
some devices might end up attached to the pci
or
pcmcia
buses. The last line in the plugin will create
an instace of the plugin in wader's plugin system -otherwise it will
not be found!.
Overview of a relatively simple DevicePlugin
Take for example the HuaweiE620 class:
import re from wader.common.hardware.huawei import (HuaweiWCDMADevicePlugin, HuaweiWCDMACustomizer) from wader.common.command import get_cmd_dict_copy, OK_REGEXP, ERROR_REGEXP info = dict(echo=None, end=OK_REGEXP, error=ERROR_REGEXP, extract=re.compile( """ \r\n \+CPOL:\s(?P<index>\d+),"(?P<netid>\d+)" """, re.VERBOSE)) E620_CMD_DICT = get_cmd_dict_copy() E620_CMD_DICT['get_roaming_ids'] = info class HuaweiE620Customizer(HuaweiWCDMACustomizer): cmd_dict = E620_CMD_DICT class HuaweiE620(HuaweiWCDMADevicePlugin): """L{wader.common.plugin.DBusDevicePlugin} for Huawei's E620""" name = "Huawei E620" version = "0.1" author = u"Pablo Martí" custom = HuaweiE620Customizer() __remote_name__ = "E620" __properties__ = { 'usb_device.vendor_id': [0x12d1], 'usb_device.product_id': [0x1001], }
The E620 plugin is identical to the XU870 except one small
difference regarding the parsing of the get_roaming_ids
command. The E620 ommits some information that other devices do output,
and the regular expression object that parses it has to be updated.
We get a new copy of the cmd_dict
dictionary and modify it
with the new regexp the get_roaming_ids
entry. The new
cmd_dict
is specified in its Customizer object.
Overview of a not so simple DevicePlugin
from wader.common.hardware.huawei import HuaweiWCDMADevicePlugin, HuaweiSIMClass class HuaweiE220SIMClass(HuaweiSIMClass): """Huawei E220 SIM Class""" def __init__(self, sconn): super(HuaweiE220SIMClass, self).__init__(sconn) def initialize(self, set_encoding=False): d = super(HuaweiE220SIMClass, self).initialize(set_encoding=False) def init_cb(size): self.sconn.get_smsc() # before switching to UCS2, we need to get once the SMSC number # otherwise as soon as we send a SMS, the device would reset # as if it had been unplugged and replugged to the system def process_charset(charset): """ Do not set charset to UCS2 if is not necessary, returns size """ if charset == "UCS2": self.set_charset(charset) return size else: d3 = self.sconn.set_charset("UCS2") d3.addCallback(lambda ignored: size) return d3 d2 = self.sconn.get_charset() d2.addCallback(process_charset) return d2 d.addCallback(init_cb) return d class HuaweiE220(HuaweiWCDMADevicePlugin): """L{wader.common.plugin.DBusDevicePlugin} for Huawei's E220""" name = "Huawei E220" version = "0.1" author = u"Pablo Martí" simklass = HuaweiE220SIMClass __remote_name__ = "E220" __properties__ = { 'usb_device.vendor_id': [0x12d1], 'usb_device.product_id': [0x1003, 0x1004], }
Huawei's E220, despite sharing its manufacturer with the E620, has a couple of minor differences that deserve some explanation. There's a bug in its firmware that will reset the device if you ask its SMSC. The workaround is to get once the SMSC before switching to UCS2, you'd be amazed of how long took me to discover the fix. The second difference with the E620 is that the E220 can have several product_ids, thus its product_id list have two elements.
Overview of a complex DevicePlugin
from twisted.python import log from wader.common.hardware.option import (OptionWCDMADevicePlugin, OptionWCDMACustomizer) from wader.common.sim import SIMBaseClass from wader.common.statem.auth import AuthStateMachine from epsilon.modal import mode class OptionColtAuthStateMachine(AuthStateMachine): """ Custom AuthStateMachine for Option Colt This device has a rather buggy firmware that yields all sort of weird errors. For example, if PIN authentication is disabled on the SIM and you issue an AT+CPIN? command, it will reply with a +CPIN: SIM PUK2 """ pin_needed_status = AuthStateMachine.pin_needed_status puk_needed_status = AuthStateMachine.puk_needed_status puk2_needed_status = AuthStateMachine.puk2_needed_status class get_pin_status(mode): """ Ask the PIN what's the PIN status The SIM can be in one of the following states: - SIM is ready (already authenticated, or PIN disabled) - PIN is needed - PIN2 is needed (not handled) - PUK is needed - PUK2 is needed - SIM is not inserted - SIM's firmware error """ def __enter__(self): pass def __exit__(self): pass def do_next(self): log.msg("Instantiating get_pin_status mode....") d = self.device.sconn.get_pin_status() d.addCallback(self.get_pin_status_cb) d.addErrback(self.sim_failure_eb) d.addErrback(self.sim_busy_eb) d.addErrback(self.sim_no_present_eb) d.addErrback(log.err) class OptionColtSIMClass(SIMBaseClass): """Huawei E220 SIM Class""" def __init__(self, sconn): super(OptionColtSIMClass, self).__init__(sconn) def initialize(self, set_encoding=False): self.charset = 'IRA' d = super(OptionColtSIMClass, self).initialize(set_encoding) d.addCallback(lambda size: self.set_size(size)) return d class OptionColtCustomizer(OptionWCDMACustomizer): """L{wader.common.hardware.Customizer} for Option Colt""" authklass = OptionColtAuthStateMachine class OptionColt(OptionWCDMADevicePlugin): """L{wader.common.plugin.DBusDevicePlugin} for Option Colt""" name = "Option Colt" version = "0.1" author = u"Pablo Martí" custom = OptionColtCustomizer() simklass = OptionColtSIMClass __remote_name__ = "129" __properties__ = { 'usb_device.vendor_id' : [0x0af0], 'usb_device.product_id' : [0x5000], } optioncolt = OptionColt()
Option 3G Datacard is the buggiest card we've found so far, and has proven to be an excellent challenge for the extensibility and granularity of our plugin system. Basically we've found the following bugs on the card's firmware:
- If PIN authentication is disabled and you issue an
AT+CPIN?
, the card will reply with a+CPIN: SIM PUK2
. - Don't ask me why, but
AT+CPBR=1,250
does not work once the application is running. I have tried replacing the command with an equivalent one (AT+CPBF=""
) without luck. Thus the main screen never loads completely. This is were we are stuck at, and some input would be really helpful.
So we had to modify the AuthStateMachine for this particular device
and its cmd_dict
.