Setup and Tools
What you need:
- Moto Z
- Computer running Linux (including as a virtual machine)
- Moto Mods Development Kit
- USB Type-C cable
Tools Setup
http://developer.motorola.com/build/tools/setup-environment
Clone and Build
http://developer.motorola.com/build/tools/build-from-source
Flash Bootloader and Firmware
http://developer.motorola.com/build/tools/flashing-firmware
Example and Developer Modes
Example Mode simplifies switching between Personality Cards
- VID = 0x312
- Custom bootloader that reads the PID from an EEPROM in the Personality Card.
- The Moto Z will download and update the firmware
- Example firmwares monitor switches at the 80pin header to detect insertion/removal. The MuC resets on transition.
Always detach the Reference Moto Mod from the Moto Z before inserting/removing a Personality Card.
Developer Mode lets you hack without system interference
- VID = 0x42
- Bypass Moto Z’s firmware update check
- Flash firmware using the MDK Utility
- “developer” config in the muc-loader repository
The MDK Utility requires Personality Cards removed from the Reference Moto Mod before allowing the switch to Developer mode.
Blinky
Blinky is the “Hello, World!” of Moto Mods.
- Raw protocol used for simple on/off switch
- Uses hdk_powered as the base project with battery and charging support
Firmware Reference Paths
Manifest | nuttx/apps/greybus-utils/manifests/hdk-blinky.mnfs |
Project Definition | nuttx/nuttx/config/hdk/muc/blinky |
Kconfig | nuttx/nuttx/config/hdk/muc/Kconfig |
Makefile | nuttx/nuttx/config/hdk/muc/src/Makefile |
Source Files | nuttx/nuttx/config/hdk/muc/src |
nuttx/apps/greybus-utils/manifests/hdk-blinky.mnfs
[manifest-header] version-major = 0 version-minor = 1 | Version of the Manifest file |
[interface-descriptor] vendor-string-id = 1 product-string-id = 2 ; Interface vendor string (id can't be 0) [string-descriptor 1] string = Motorola Mobility, LLC ; Interface product string (id can't be 0) [string-descriptor 2] string = MDK-BLINKY | Vendor and Product String definitions |
; Control protocol on CPort 0 [cport-descriptor 0] bundle = 0 protocol = 0x00 ; Control protocol Bundle 0 [bundle-descriptor 0] class = 0 ; Motorola specific on CPort 1 [cport-descriptor 1] bundle = 1 protocol = 0xff ; Debug related Bundle 1 [bundle-descriptor 1] class = 0xff | Greybus and Moto Mod interfaces for enumeration, communication, and control. |
; Battery on CPort 2 [cport-descriptor 2] bundle = 2 protocol = 0x08
; PTP on CPort 3 [cport-descriptor 3] bundle = 2 protocol = 0xef
; Battery related Bundle 2 [bundle-descriptor 2] class = 0x08 | Battery and Power Transfer Protocols for metering and charging.
These are grouped together into a single Bundle, meaning both must enumerate properly for either to be accessible. |
; RAW interface on CPort 4 [cport-descriptor 4] bundle = 3 protocol = 0xfe
; RAW Bundle 3 [bundle-descriptor 3] class = 0xfe | Raw Protocol, used for basic on/off switch. |
Blinky is based off hdk-powered.mnfs. hdk.mnfs should be used if your project does not require a battery.
nuttx/nuttx/config/hdk/muc/blinky Project Files
Make.defs | Makefile definitions |
setenv.sh | Script to configure environment to build |
defconfig | Moto Mod interfaces for enumeration, communication, and control. Modified via “make menuconfig” |
After creating your project, make it the active one:
cd $BUILD_TOP/nuttx/nuttx
make distclean
cd $BUILD_TOP/nuttx/nuttx/tools
./configure hdk/muc/blinky
cd $BUILD_TOP/nuttx/nuttx
make
Blinky is based off of base-powered. base_unpowered should be used if your project does not require a battery.
nuttx/nuttx/config/hdk/muc/Kconfig
Update Kconfig to add your new project’s support:
config MODS_RAW_BLINKY
bool "Blinky LED Mods Raw support"
default n
depends on GREYBUS_RAW
select DEVICE_CORE
select STM32_TIM6
---help---
Enable Blinky LED Raw support
This integrates Blinky with the ‘make menuconfig’ and defines any build dependencies needed to support the ‘Board Selection’ step later. A primer on the NuttX menuconfig is available here:
http://www.programering.com/a/MjN1gjMwATE.html
Additional information on configuring NuttX can be found here:
http://nuttx.org/doku.php?id=documentation:configvars
http://nuttx.org/doku.php?id=documentation:menuconfig
Update the VID/PID
Go to the nuttx top and run use menuconfig
cd $BUILD_TOP/nuttx/nuttx
make menuconfig
Select “System Type” from the top level menu
Update the Board VID and PID
For Developer Mode:
VID = 0x42
PID = 0x1
Select <Save>, then <Exit> to the top level
Board Selection
Select “Board Selection” from the top level menu
Scroll to “Blinky LED Mods Raw Support” and press the space bar to toggle On
Select <Save>, then <Exit> to the top level
Setting the Manifest
1. Select “Application Configuration” from the top level menu
2. Select “Greybus Utility”
3. Select “manifest name”. Type “hdk-blinky” in the dialog box and press Enter.
Select <Save>, then <Exit> to the top level
Setup Raw Support
1. Select “Device Drivers” from the top level menu
2. Select “Greybus Support”
3. Scroll to “Vendor Raw Support” and press the space bar to toggle On
Select <Save>, then <Exit> to the top level
Enable Timer6 for LED Toggle Interrupts
1. Select “System Type” from the top level menu
2. Select “STM32 Peripheral Support”
3. Scroll to “TIM6” and press the space bar to toggle On
Select <Save>, then <Exit> to the top level
Update the Makefile
Add the new files to nuttx/nuttx/config/hdk/muc/src/Makefile
ifeq ($(CONFIG_MODS_RAW_BLINKY),y)
CSRCS += stm32_modsraw_blinky.c
endif
To ensure the changes made with “make menuconfig” are permanent, the nuttx/nuttx/.config should be copied over top the nuttx/nuttx/config/hdk/muc/{project}/defconfig
Setup Raw Device Driver
Copy stm32_modsraw.c to stm32_modsraw_blinky.c to use as a starting point.
Customize the device_driver and associated structures:
static struct device_raw_type_ops blinky_type_ops = {
.recv = blinky_recv,
.register_callback = blinky_register_callback,
.unregister_callback = blinky_unregister_callback,
};
static struct device_driver_ops blinky_driver_ops = {
.probe = blinky_probe,
.type_ops = &blinky_type_ops,
};
struct device_driver mods_raw_blinky_driver = {
.type = DEVICE_TYPE_RAW_HW,
.name = "mods_raw_blinky",
.desc = "Blinky LED Raw Interface",
.ops = &blinky_driver_ops,
};
Implement Driver Functions
From the ops structures, there are 4 functions to be implemented for Raw:
- blinky_probe()
- Called by kernel after device registration
- blinky_recv()
- Called by greybus when data has arrived on the Raw channel
- blinky_register_callback()
- Called by kernel to provide a pointer to the raw_send_callback. The driver should save this pointer and use when sending data to the Moto Z
- blinky_unregister_callback()
- Called by the kernel when the device is no longer registered
static int blinky_probe(struct device *dev)
{
gpio_direction_out(GPIO_MODS_LED_DRV_3, LED_OFF);
gpio_direction_out(GPIO_MODS_DEMO_ENABLE, 0);
return 0;
}
static int blinky_recv(struct device *dev, uint32_t len, uint8_t data[])
{
if (len == 0)
return -EINVAL;
if (data[0] == 0 || data[0] == '0')
blinky_timer_stop();
else
blinky_timer_start();
return 0;
}
Implement Behavior
For Blinky, 3 other functions are implemented to provide the actual behavior:
- blinky_timer_start()
- Called when a Raw message arrives with the ON command
- Creates and starts the timer used to blink the LED on the Reference Moto Mod on and off
- blinky_timer_stop()
- Called when a Raw message arrives with the OFF command
- Cancels the running timer and ensures the LED is off
- blinky_timer_handler()
- Handles interrupts from the timer and toggles the GPIO state
These functions also reside in stm32_modsraw_blinky.c
Register the Device Driver
- Open nuttx/nuttx/configs/hdk/muc/src/stm32_boot.c
- In the devices[] array, add the following:
#ifdef CONFIG_MODS_RAW_BLINKY
{
.type = DEVICE_TYPE_RAW_HW,
.name = "mods_raw_blinky",
.desc = "Blinky LED Raw Interface",
.id = 0,
},
#endif
- In the board_initialize() function, add the following:
#ifdef CONFIG_MODS_RAW_BLINKY
extern struct device_driver mods_raw_blinky_driver;
device_register_driver(&mods_raw_blinky_driver);
#endif
Blinky Support Application (MDK Utility)
Your Moto Mod project may require an Android application to unlock its full capabilities. To this end, Moto has released the modlib. This library enables your app to query and receive Moto Mods status, as well as controls specific to the different Protocols.
Blinky uses two main components from the modlib:
- ModManager
- Provides Moto Mod Connect/Disconnect status
- Provides details about currently attached Moto Mod
- Allows access to Moto Mod protocols and handles permissions
- Raw access
- Retrieve File Descriptor to talk to the Moto Mod’s Raw protocol
The MDK Utility provides other developer functionality as well. See http://developer.motorola.com/build/examples/mdk-utility for more detail.
Blinky Application Setup
Ensure that Android Studio 2.0 and the Android SDK API 23 is installed.
Download the modlib from http://developer.motorola.com/build/tools/setup-environment
Open Android Studio and create a New Project
Then add the modlib.jar and version.xml files to your Project
cp modlib-{version}.jar $APP_TOP/app/libs
cp res/version.xml $APP_TOP/app/src/main/res/values
AndroidManifest.xml for all Moto Mods
To be a Moto Mods aware application, features and permissions must be added:
<uses-feature android:name="com.motorola.hardware.mods"/>
This defines your application as using the Moto Mods feature for Play Store filtering
<meta-data
android:name="com.motorola.mod.version"
android:value="@integer/moto_mod_services_version" />
This uses the value from the version.xml file as the Moto Mods Services version. This is used for future compatibility checks as Moto Mods capabilities expand.
<uses-permission android:name="com.motorola.mod.permission.MOD_ACCESS_INFO" />
This permission allows access to the ModManager for access to Moto Mods information
Blinky access to the Raw Protocol
Blinky makes use of the Raw protocol as a custom data path to turn the LED on and off. Since the Raw protocol provides direct access to communicate with the Moto Mod, it is protected by a unique permission in the AndroidManifest.xml.
<uses-permission android:name="com.motorola.mod.permission.RAW_PROTOCOL" />
This permission (when granted by the user) allows this Application to access the Raw protocol.
Before opening Raw, you should always check the VID/PID to ensure this is the Moto Mod you wish to communicate with. And, of course, check with the user:
requestPermissions(new String[]{ModManager.PERMISSION_USE_RAW_PROTOCOL},
REQUEST_RAW_PERMISSION);
Implement onRequestPermissionsResult() to handle the result of the Permission request. Only request the Raw file descriptor after receiving Permission or an exception will be thrown.
Handling Moto Mods State
Obtaining the basic of Moto Mods is done through the ModManager. First and foremost, your application should determine whether the current device supports Moto Mods.
if(ModManager.isModServicesAvailable(context) == ModManager.SUCCESS) {
// This device supports Moto Mods
}
The Moto Mods state can either be monitored through Intents, or via direct callbacks from the ModManager. Blinky makes use of a BroadcastReceiver and Intents.
modReceiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter(ModManager.ACTION_MOD_ATTACH);
filter.addAction(ModManager.ACTION_MOD_DETACH);
context.registerReceiver(modReceiver, filter, ModManager.PERMISSION_MOD_INTERNAL, null);
For additional information on the ModManager, refer to http://developer.motorola.com/explore/software/mod-management
Check what was attached
As mentioned earlier, when using the Raw protocol you should always verify the VID/PID. Never open the Raw protocol simply because it is available, since the actual data will be custom to that product.
String action = intent.getAction();
if(action != null ){
if(action.equals(ModManager.ACTION_MOD_ATTACH)){
int vid = intent.getIntExtra(ModManager.EXTRA_VENDOR_ID,
INVALID_ID);
int pid = intent.getIntExtra(ModManager.EXTRA_PRODUCT_ID,
INVALID_ID);
if ((vid == MY_BLINKY_VID) && (pid == MY_BLINKY_PID)) {
startRawService();
}
}
}
When using Raw, reads and writes should be done separate from the UI thread. The MDK Utility handles this via the RawPersonalityService class.
Opening the Raw Interface
The Raw protocol is exposed to Android applications via a ParcelFileDescriptor provided by the ModManager:
List<ModInterfaceDelegation> devices =
modManager.getModInterfaceDelegationsByProtocol(modDevice,
ModProtocol.Protocol.RAW);
if (devices != null && !devices.isEmpty()) {
ModInterfaceDelegation device = devices.get(0);
Log.d(Constants.TAG, "openRawDeviceifAvailable checking " + device);
parcelFD = modManager.openModInterface(device,
ParcelFileDescriptor.MODE_READ_WRITE);
This code block obtains the Raw interface and retrieves a file descriptor that is used for direct read/write access.
Sending Data over Raw
FileDescriptor fd = parcelFD.getFileDescriptor();
outputStream = new FileOutputStream(fd);
outputStream.write((byte[]) cmd);
Although sending data over Raw appears simple, this should always be performed on a separate Thread from the UI or via an AsyncTask. Otherwise, you will may receive ANR while your application is attempting to communicate with the Moto Mod.