March 12, 2015

cmake add include path and library path

Some tips for cMake beginners. This is done on Linux.

How to use cMake

cmake generates a bunch of intermediate files, including the final Makefile. To make things clean, make a separate directory for the build process:

mkdir build
cd build
cmake ..

To re-run cmake after changing the CMakeLists.txt, do:

cd build
rm -rf *
cmake ..

Cross Compile to ARM

To cross compile to an embedded target, add the following lines to you CMakeLists.txt at the very top (after VERSION)
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_C_COMPILER arm-none-linux-gnueabi-gcc)

Add include paths

include_directories("${PROJECT_SOURCE_DIR}/../" "${PROJECT_SOURCE_DIR}/build/" "${PROJECT_SOURCE_DIR}")

Add a library

FIND_LIBRARY(json json-c json)

1st parameter: output,  The variable that will hold the results of FIND_LIBRARY
2nd parameter: the library to look for (in this case, libjson-c.so)
3rd parameter: the path to look, starting with ${PROJECT_SOURCE_DIR}

The returned varilable can be used in the following statement to add the library to link time:
TARGET_LINK_LIBRARIES(${json})

Assign a variable

SET(LIBS ${ubox} ${blobmsg_json} ${json})

assigns the concatenated value of ubox, blobmsg_json and json to the variable LIBS



March 11, 2015

AT&T Data plan web page

https://buyasession.att.com

Linux running 4G LTE modem

The modem I have is Sierra Wireless Air Prime MC7453 Mini PCI Express Module.  The carrier I tested is AT&T.

Before running on Linux, I had to install the card on a regular Laptop that supports 4G card, and run Windows 7 on the laptop, install all the drivers and the Sierra Wireless Connect Manager to try out the card. This confirmed that the card has the latest firmware, and the carrier setting is correct, etc.

Next I run it on Linux. Instead of using the Gobi driver from Sierra Wireless, I used the qmi_wwan driver in the latest Linux (after kernel version 3.14).  An user land utility is also needed. libqmi seems to be popular, but it depends on a fairly new version of glib, which itself depends on a bunch of libraries. It becomes a hassle to compile everything. Then I discovered uqmi, a small utility developed and used by the openwrt project. It's much more lightweight, and seems to be pretty mature. I was able to get my modem to connect and ping with the following commands.

device="/dev/cdc-wdm1"
modes="lte"
uqmi -s -d "$device" --get-pin-status
uqmi -s -d "$device" --get-serving-system
uqmi -s -d "$device" --set-data-format 802.3
uqmi -s -d "$device" --wda-set-data-format 802.3
uqmi -s -d "$device" --set-network-modes "$modes"
wds=`uqmi -s -d "$device" --get-client-id wds`
uqmi -s -d "$device" --set-client-id wds,"$wds" --start-network "ISP.CINGULAR" --autoconnect
uqmi -s -d "$device" --get-signal-info
uqmi -s -d "$device" --get-data-status
dhclient wwan1

Now I can ping

To disconnect:
uqmi -s -d "$device" --stop-network 0xffffffff    --autoconnect > /dev/null


[1] The home page of uqmi:  http://nbd.name/gitweb.cgi?p=uqmi.git;a=summary
[2] A list of useful commands of how to use uqmi: http://trac.gateworks.com/wiki/modem
[3] The official Openwrt qmi script that uses uqmi:  https://dev.openwrt.org/browser/trunk/package/network/utils/uqmi/files/lib/netifd/proto/qmi.sh
[4] Another useful link: https://lists.openwrt.org/pipermail/openwrt-devel/2014-April/025046.html

March 5, 2015

How to get Linux USB Report Descriptor when it says UNAVAILABLE

Start by plugging in your USB device and running dmesg to get the kernel output for the event. It will look something like this:

input: Sony PLAYSTATION(R)3 Controller as /devices/pci0000:00/0000:00:04.1/usb2/2-4/2-4.2/2-4.2:1.0/input/input16

The first line input: ... is what we want. It contains the full device path and in this case we are interested in third last component of the path: 2-4.2:1.0.

To get the USB report Descriptor with "lsusb -v", we need to unbind the device first (as root).
$ sudo bash -c "echo -n 2-4.2:1.0 >/sys/bus/usb/drivers/usbhid/unbind"

Now run lsusb again for the device and you should see the report descriptor in the output!

Patch Linux kernel to support Esterline Medigenic keyboard

The Esterline Medigenic keyboard does not work under stock Linux kernel (as of kernel version 4.0RC). This is because the HID Report Descriptor of the keyboard is wrong for Linux (and Mac OS). Under Linux kernel tree driver/hid there are a bunch of "special" drivers for different hid devices, and we can create one for the medigenic keyboard to fix up the HID report descriptor so that it works under Linux.

1. Create a file under driver/hid called hid-medigenic.c with the following content:
/*
 * 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.
 */

#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>

#include "hid-ids.h"

/* the fixups that need to be done:
 *   - change keyboard Usage Mininum from 1 to 0
 */
static __u8 *medigenic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
{
        if (*rsize >= 59 &&
                rdesc[24] == 0x19 && rdesc[25] == 0x01 &&
                rdesc[26] == 0x29 && rdesc[27] == 0x65 ) {
                dev_info(&hdev->dev, "fixing up medigenic keyboard report descriptor\n");
                rdesc[25] = 0x00;
        }
        return rdesc;
}

static const struct hid_device_id medigenic_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ESTERLINE, USB_DEVICE_ID_MEDIGENIC) },
        { }
};
MODULE_DEVICE_TABLE(hid, medigenic_devices);

static struct hid_driver medigenic_driver = {
        .name = "medigenic",
        .id_table = medigenic_devices,
        .report_fixup = medigenic_report_fixup,
};

static int __init medigenic_init(void)
{
        return hid_register_driver(&medigenic_driver);
}

static void __exit medigenic_exit(void)
{
        hid_unregister_driver(&medigenic_driver);
}

module_init(medigenic_init);
module_exit(medigenic_exit);
MODULE_LICENSE("GPL");

2. Edit file driver/hid/hid-ids.h to add the following two lines:
#define USB_VENDOR_ID_ESTERLINE 0x059d
#define USB_DEVICE_ID_MEDIGENIC 0x0708


3. Edit file driver/hid/Kconfig to add the following lines:
config HID_MEDIGENIC
    tristate "Medigenic Keyboard Support" if EMBEDDED
    depends on USB_HID
    default !EMBEDDED
    ---help---
    Support for Medigenic keyboard.

4. Edit file driver/hid/Makefile to add the following line:
obj-$(CONFIG_HID_MEDIGENIC)     += hid-medigenic.o

Then in top directory of kernel, do make menuconfig, and exit, and save (somehow medigenic is automatically set to Y when you do this). Check .config to make sure you have the following line:
CONFIG_HID_MEDIGENIC=y

The build your kernel and the keyboard should work now.

memdump function in kernel

void memdump(unsigned char *rdesc, int *rsize){
    int i,pos=0;
    unsigned char outbuf[256];
    printk(KERN_WARNING "rsize=%d\n",*rsize);
    for (i=0;i<*rsize;i++){
        pos+=sprintf(outbuf+pos,"%02x ",rdesc[i]);
        if (i>0 && ((i+1)%16)==0){
            printk(KERN_WARNING "%04x dump %s\n",(i/16)*16,outbuf);
            pos=0;
        }
    }
    if (pos>0){
        printk(KERN_WARNING "%04x dump %s\n",(i/16)*16,outbuf);
    }
}

March 3, 2015

replace systemd with a simple busybox inittab

1. install busybox
2. create file /etc/inittab with the following content:
# Start "rc init" on boot
::sysinit:/opt/rc init

# Set up the TTY's 1 through 4
tty1::askfirst:/sbin/agetty -8 -s 38400 tty1 linux
tty2::respawn:/sbin/agetty -8 -s 38400 tty2 linux

# Stop all services on shutdown
::shutdown:/opt/rc shutdown

# Killing everything on shutdown
::shutdown:echo :: sending SIGTERM to all
::shutdown:/bin/kill -s TERM -1
::shutdown:sleep 1
::shutdown:echo :: sending SIGKILL to all
::shutdown:/bin/kill -s KILL -1

# Unmount everything on shutdown
::shutdown:echo :: unmounting everything
::shutdown:/bin/umount -a -r
::shutdown:/bin/mount -o remount,ro /

3. ln -s /bin/busybox /opt/init
4. create /opt/rc with the following content:
#!/bin/sh
on_boot() {
    #===================
    # mount the API filesystem
    # /proc, /sys, /run, /dev, /run/lock, /dev/pts, /dev/shm
    echo 3 mounting API filesystem...
    mountpoint -q /proc    || mount -t proc proc /proc -o nosuid,noexec,nodev
    mountpoint -q /sys     || mount -t sysfs sys /sys -o nosuid,noexec,nodev
    mountpoint -q /run     || mount -t tmpfs run /run -o mode=0755,nosuid,nodev
    mountpoint -q /dev     || mount -t devtmpfs dev /dev -o mode=0755,nosuid
    mkdir -p /dev/pts /dev/shm
    mountpoint -q /dev/pts || mount -t devpts devpts /dev/pts -o mode=0620,gid=5,nosuid,noexec
    mountpoint -q /dev/shm || mount -t tmpfs shm /dev/shm -o mode=1777,nosuid,nodev

    #===================
    # initialize system
    echo 3 setting up loopback device...
    /usr/sbin/ip link set up dev lo

    echo 3 initializing udev...
        busybox mdev -s
        echo /sbin/mdev > /proc/sys/kernel/hotplug

    echo 3 setting hostname...
    cat /etc/hostname >| /proc/sys/kernel/hostname

    echo 3 mounting...
    mount -a
    mount -o remount,rw /

        dhclient eth0&
        /etc/init.d/ssh start&
}

on_shutdown() {
    #===================
    echo 3 shutting down udev...
        killall busybox
        killall mdev

    #===================
    # umount the API filesystem
    echo 3 unmounting API filesystem...
    umount -r /run
}

#===================
# handle arguments
case "$1" in
init)
    on_boot;;
shutdown)
    on_shutdown;;
esac

5. reboot and add the following parameter to your kernel command line on grub:
init=/opt/init

Enjoy