Friday, February 13, 2015

Linux on the Zotac ZBOX Pi320

If you've found this post, you know what the Zotac ZBOX pico 320 (and 321) is, so I won't go into that. If you found this post, you also know how annoying it is to make this device work with linux.

Its 4:30am, so I'll keep this short. There's 3 main problems:
  1. 32bit EFI, which means you'll have to download an amd64 distribution, but patch the install-USB stick to also provide a 32bit grub. There's sufficient documentation on that.
  2. WiFi won't work.
  3. The power button doesn't work.
  4. Possibly sound doesn't work - but I don't care

For problems 2 and 3, I pestered people on the respective mailing lists. At the end, Arend van Spriel and Mika Westerberg were extremely helpful and had the right ideas:

http://marc.info/?t=142321396800002&r=1&w=2
http://www.spinics.net/lists/linux-acpi/msg55693.html

Some discussion took part off the lists (especially ACPI related), so I'd like to document the solution here. WiFi doesn't work because it is switched off by default

# cat /sys/bus/acpi/devices/INT33BB\:00/power_state
D3cold

You can enable it by issuing

# echo on > /sys/bus/platform/devices/INT33BB:00/power/control 

If you don't have the file already, copy brcmfmac4330-sdio.bin from linux-firmware to

/lib/firmware/brcm/brcmfmac4330-sdio.bin

The following file was copied from the windows driver and should be copied to

/lib/firmware/brcm/brcmfmac4330-sdio.txt:

#Sample variables file for BCM94330 SD FC AGB board T77H360.04_EVT5_20120731                     
manfid=0x2d0                                                                                      
prodid=0x0547                                                                                     
vendid=0x14e4                                                                                     
devid=0x4360                                                                                      
boardtype=0x05e1                                                                                  
boardrev=0x1202                                                                                     
boardflags=0x10080a00                                                                                 
nocrc=1                                                                                               
xtalfreq=26000                                                                                        
boardnum=22                                                                                             
macaddr=00:90:4c:c5:12:38                                                                               
ag0=0x40                                                                                                 
ag1=0xBF                                                                                                   
aa2g=1                                                                                                       
aa5g=1                                                                                                       
ccode=all                                                                                                     
pa0itssit=0x20
pa0b0=4924
pa0b1=-595
pa0b2=-157
rssismf2g=0xa
rssismc2g=0x3
rssisav2g=0x7
#rssi params for 5GHz
rssismf5g=0x4
rssismc5g=0x3
rssisav5g=0x7
#PA parameters for lower a-band
pa1lob0=5050
pa1lob1=-630
pa1lob2=-195
#PA parameters for midband
pa1b0=4880
pa1b1=-625
pa1b2=-200
#PA parameters for high band
pa1hib0=4880
pa1hib1=-620
pa1hib2=-190
rxpo5g=0
maxp2ga0=0x44
maxp5ga0=0x3A
maxp5gla0=0x3A
maxp5gha0=0x3A
# 2.4G Tx Power offsets
ofdm2gpo=0x22222222
mcs2gpo0=0x4444
mcs2gpo1=0x4444
# 5G Tx Power offsets
ofdm5gpo=0x00000000
ofdm5glpo=0x00000000
ofdm5ghpo=0x00000000
mcs5gpo0=0x2222
mcs5gpo1=0x2222
mcs5glpo0=0x2222
mcs5glpo1=0x2222
mcs5ghpo0=0x2222
mcs5ghpo1=0x2222
sromrev=3
il0macaddr=00:90:4c:c5:12:38
wl0id=0x431b
cckPwrOffset=6
triso2g=3
#swctrlmap_2g=0x44844484,0x42824282,0x42824282,0x18282,0x1ff
swctrlmap_2g=0x04040404,0x02020202,0x02020202,0x18282,0x1ff
triso5g=6
swctrlmap_5g=0x10101010,0x28282020,0x20202020,0x10202,0x0f8
noise_cal_ref_2g=53
rfreg033=0x19
rfreg033_cck=0x1f
dacrate2g=160
dacrate5g=160
txalpfbyp2g=1
bphyscale=17
cckPwrIdxCorr=-15
pacalpwr5glo=13
pacalpwr5glo1=11
pacalpwr5g=13
pacalpwr5g1=11
pacalpwr5ghi=13
pacalpwr5ghi1=11
#pacalpwr2g1=13
#pacalath2g=180
#pacalidx2g1=55
pacalpwr2g=13
txgaintbl=1
rfreg088=15
cckdigfilttype=20
noise_cal_adj_2g=-3
#wake on wireless LAN
sd_gpout=0
sd_gpval=1
sd_gpdc=0


Now "modprobe brcmfmac" and life should be good. I tested wpa_supplicant and hostapd, both work.

After a little email-pingpong, Mika Westerberg whipped up a patch that fixes the power button:

From 3404c6f2422c9e8d3f51a8832fbce01483c423d9 Mon Sep 17 00:00:00 2001
From: Mika Westerberg 
Date: Wed, 11 Feb 2015 17:21:18 +0200
Subject: [PATCH] pinctrl: baytrail: Relax GPIO request rules for certain pin

On Baytrail-T based machines the power button is part of GPIO keys like
button array (and implemented as a GPIO). However, Zotac ZBOX BIOS fails to
configure the pin properly which prevents the driver to request it.
Following is printed on the console:

  byt_gpio INT33FC:02: pin 16 cannot be used as GPIO.

Fix this by relaxing GPIO request rules for this particular pin.

Reported-by: Benjamin Adler 
Signed-off-by: Mika Westerberg 
---
 drivers/pinctrl/intel/pinctrl-baytrail.c | 40 +++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 9 deletions(-)

diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c
index 5afe03e28b91..44a7fef8a0f8 100644
--- a/drivers/pinctrl/intel/pinctrl-baytrail.c
+++ b/drivers/pinctrl/intel/pinctrl-baytrail.c
@@ -158,17 +158,29 @@ static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset,
  return vg->reg_base + reg_offset + reg;
 }
 
-static bool is_special_pin(struct byt_gpio *vg, unsigned offset)
+static unsigned get_gpio_mux(struct byt_gpio *vg, unsigned offset)
 {
  /* SCORE pin 92-93 */
  if (!strcmp(vg->range->name, BYT_SCORE_ACPI_UID) &&
   offset >= 92 && offset <= 93)
-  return true;
+  return 1;
 
  /* SUS pin 11-21 */
  if (!strcmp(vg->range->name, BYT_SUS_ACPI_UID) &&
   offset >= 11 && offset <= 21)
-  return true;
+  return 1;
+
+ return 0;
+}
+
+static bool can_mux_as_gpio(struct byt_gpio *vg, unsigned offset)
+{
+ if (!strcmp(vg->range->name, BYT_SUS_ACPI_UID)) {
+  switch (offset) {
+  case 16: /* Power button on BYT-T machines */
+   return true;
+  }
+ }
 
  return false;
 }
@@ -177,8 +189,10 @@ static int byt_gpio_request(struct gpio_chip *chip, unsigned offset)
 {
  struct byt_gpio *vg = to_byt_gpio(chip);
  void __iomem *reg = byt_gpio_reg(chip, offset, BYT_CONF0_REG);
+ unsigned long flags;
  u32 value;
- bool special;
+
+ spin_lock_irqsave(&vg->lock, flags);
 
  /*
   * In most cases, func pin mux 000 means GPIO function.
@@ -187,13 +201,21 @@ static int byt_gpio_request(struct gpio_chip *chip, unsigned offset)
   * func pin mux preset as GPIO function by BIOS/FW.
   */
  value = readl(reg) & BYT_PIN_MUX;
- special = is_special_pin(vg, offset);
- if ((special && value != 1) || (!special && value)) {
-  dev_err(&vg->pdev->dev,
-   "pin %u cannot be used as GPIO.\n", offset);
-  return -EINVAL;
+ if (value != get_gpio_mux(vg, offset)) {
+  /* Some misconfigured pins can still be muxed as GPIOs */
+  if (can_mux_as_gpio(vg, offset)) {
+   value = readl(reg);
+   value |= 1;
+   writel(value, reg);
+  } else {
+   dev_err(&vg->pdev->dev,
+    "pin %u cannot be used as GPIO.\n", offset);
+   spin_unlock_irqrestore(&vg->lock, flags);
+   return -EINVAL;
+  }
  }
 
+ spin_unlock_irqrestore(&vg->lock, flags);
  pm_runtime_get(&vg->pdev->dev);
 
  return 0;

This fix will become part of mainline, maybe 3.20, and once applied, acpid will catch the power button without any further configuration.

Godspeed!

Thursday, December 26, 2013

TI SensorTag, Bluetooth Low Energy (BLE) and Linux

I've  been interested in wireless sensing of temperature and relative humidity. All the options I found previously were at least one of the following:

  • expensive, as in >€100
  • outdated, e.g. still using IEEE 802.11b
  • large
  • power-hungry
Then, I found the TI SensorTag. Texas Instruments calls it a development kit and uses it to promote its CC2541 Bluetooth Low Energy (BLE) chip. The whole board is small enough to fit into a box of matches, yet hosts temperature, humidity, 3d accelerometers and gyros, IR temperature and probably other sensors that I forgot.


Mike Saunby kindly reported on how to access this device using linux and the BlueZ utilities. He also provides a python script to retrieve the sensor's values in a scripty fashion and convert them to values meaningful to humans.

Pretty sweet! So I ordered two. And it works. My flat was built in the beginning of the last century, and using a Digitus DN-30210 Bluetooth dongle (Broadcom 20702), the lack of concrete allows a range of about 15m, with two "walls" in between. Great!

The only trouble was that the SensorTag would advertise (and wait for incoming connections) exactly 180 seconds, then shut down. This is a real problem if you're trying to use them for long-term data capture. You'd have to keep the connection alive permanently, and if it ever drops for more than 3 minutes (server restart, bugs etc.) you're disconnected. That might be ok at home, but not in remote locations. It's just not robust.

I read a lot about BLE, central devices, peripheral devices, masters and slaves, advertising and whatnot, but I could never find out whether it's possible in principle to connect to the SensorTag when it's NOT advertising. After all, I thought advertising is just to let others know you exist and what MAC you have, but accepting connections might still be possible without it. I still don't know, but I guess not.

TI does distribute the source-code for the firmware running on the SensorTag (it's one of the sample projects in their BLE stack, which is only partly open source). To get rid of the 180-second-timeout, I'd have to recompile this sample project without the timeout and flash it onto the SensorTag.

To flash new firmware, you'd either use the Texas Instruments CC Debugger ($49, international shipping is free), or, maybe, you can use an iOS-App from TI to flash the firmware wirelessly (TI calls this OAD, over-the-air download). Because I don't have Apple hardware and am scared of flashing firmware without cables, I ordered the debugger.

TI's toolchain for building software for the CC2541 is called "IAR Embedded Workbench for 8051", currently at version 8.30 (you need a recent version to work with v1.40 of the BLE stack). This software is closed, proprietary, and hideously expensive. Like, 4-figure expensive. And they guard it well.

You can download a "free" version that is limited to a 4kb code size. Nice, but quite useless, as the SensorTag's firmware boils down to around 400kb.

The other option is evaluating a full-featured version for 30 days. My evaluation period started yesterday night at 3 am.

So, after installing IAR Embedded Workbench and unzipping TI's BLE stack, you need to open
$TI_BLE_Stack\Projects\ble\common\cc2540\ti_51ew_cc2540b.xcl
in an editor and find the line
-Z(DATA)VREG+_NR_OF_VIRTUAL_REGISTERS=08-7F
 replace it with
-Z(DATA)VREG=08-7F
Now you can open
$TI_BLE_Stack\Projects\ble\SensorTag\CC2541DB\SensorTag.eww
 in IAR. In SensorTag.c, find the line
// General discoverable mode advertises indefinitely
#define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_LIMITED
 and replace it with
#define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
This line makes sure that advertising doesn't stop anymore. I have no idea how long the CR2032 battery will last if you keep advertising at the default rate of 10Hz forever, so I changed the advertising interval. Find the line
// What is the advertising interval when device is discoverable (units of 625us, 160=100ms)
#define DEFAULT_ADVERTISING_INTERVAL          160
and replace it with
#define DEFAULT_ADVERTISING_INTERVAL          16384
which means that advertising happens every 10.24 seconds. I read somewhere in TI's forums that this is the maximum value (and while I wonder what they use those other two bits for, I haven't tried going above 2^14).

You can also change the name that shows up when scanning for devices (default "SensorTag").

Now right-click on the project and select make. It should build fine and create
$TI_BLE_Stack\Projects\ble\SensorTag\CC2541DB\CC2541DK-Sensor\Exe\SensorTag.hex
In my case, that ended up like this:
md5sum aa086082c665d82e701fe2d061f710c0  SensorTag.hex
You can then try your luck using the OAD-iOS thingy, or use TI's SmartRF Flash Programmer to flash it to the SensorTag. I wrote the firmware to the primary location and didn't change that IEEE address (I think it's the bluetooth MAC, but I'm not sure).


And it works! The green LED flashes shortly every 10.24 seconds instead of 10Hz. And it has been doing that for a full 14 hours by now. I can connect using Mike's bluez-magic, fetch some values, disconnect and it will still advertise forever.

How long the battery lasts, I don't know. As soon as it stops flashing, I'll amend this post.

Dear TI, thank you very much for this great, cheap little device. But pleeeeeaaase, having to download proprietary compilers feels like the nineties. How about something that I can use GCC or LLVM with?

Dear OpenWRT, could you please package a more recent version of bluez than 3.36? Google told me a few people have tried it before, but it seems this never made it into trunk. I'd love to have my router query two or three SensorTags and populate some mysql-table!

Saturday, December 21, 2013

TechnoTrend TT-connect CT2-4650 CI working with Ubuntu 13.10

This is a short note for everybody wondering whether the TechnoTrend TT-connect CT2-4650 CI DVB-C receiver works with linux.

I just got the USB device delivered today. Drivers needed to be downloaded from TechnoTrend and installed, but that worked quickly and flawlessly. It took me a while to search/remember how to generate initial tuning data (w_scan) and then scan, but now everything works just fine. Haven't tried anything CI-related, though.

Great work, TechnoTrend!

Monday, December 16, 2013

Initialize (partition, format, copy) multiple USB sticks using linux and udev

Here's a set of scripts that allows you to fill hundreds of USB sticks with the same data. We used that for distributing proceedings of a conference, but I guess it might be helpful for others, too.

It should work on pretty much any recent linux distribution, as it only requires UDEV to work.

First, create or edit /etc/udev/rules.d/10-local.rules and add
ATTRS{idVendor}=="VENDOR_ID_OF_USB_STICK", ATTRS{idProduct}=="PRODUCT_ID_OF_USB_STICK", ATTR{size}=="15810560", RUN+="/usr/bin/fill_usb_stick.sh %k"
Then create /usr/bin/fill_usb_stick.sh, which reads:
#!/bin/bash { # Convert /dev/sdb into sdb UNIQUENAME=$1 DEVICEFILE="/dev/$1" PARTITION="/dev/$11" if [[ "$ACTION" = "remove" ]] then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME was removed." >> /tmp/log.txt exit 0 fi if [[ "$ACTION" != "add" ]] then exit 0 fi LOCKFILE="/tmp/lock_usb_$UNIQUENAME" test -e $LOCKFILE && echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: already running, exiting" >> /tmp/log.txt && exit 1 touch $LOCKFILE if [[ "$UNIQUENAME" == "sda" ]] then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: Will not write to SDA, exiting" >> /tmp/log.txt exit 1 fi /usr/bin/logger "setting up usb stick $UNIQUENAME due to action $ACTION" echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: creating new partition table, filesystem and proceedings on usb stick" >> /tmp/log.txt cat <> /tmp/log.txt # Create a FS on the first partition /sbin/mkfs.vfat -n MFI_2012 $PARTITION if [[ -d /mnt/$UNIQUENAME ]] ; then /bin/rmdir /mnt/$UNIQUENAME if [[ $? != 0 ]] ; then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: EEEEE could not delete /mnt/$UNIQUENAME before mounting, seems its not empty, exiting" >> /tmp/log.txt exit 1 fi fi /bin/mkdir -p /mnt/$UNIQUENAME #echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: mounting usb stick to: /mnt/$UNIQUENAME" >> /tmp/log.txt /bin/mount $PARTITION /mnt/$UNIQUENAME if [[ $? != 0 ]] ; then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: EEEEE could not mount usb sticks new filesystem, exiting." >> /tmp/log.txt exit 1 fi #echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: copying files to usb stick $UNIQUENAME" >> /tmp/log.txt /bin/cp -r /stuff/to/copy/on/stick/* /mnt/$UNIQUENAME/ if [[ $? != 0 ]] ; then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: EEEEE could not copy files, exiting." >> /tmp/log.txt && exit 1 exit 1 fi /bin/sleep 10 #/bin/umount $PARTITION || echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: EEEEE could not remount usb stick, exiting." >> /tmp/log.txt ; exit 1 #echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: remounting and checksumming usb stick $UNIQUENAME...." >> /tmp/log.txt /bin/mount -o remount $PARTITION /mnt/$UNIQUENAME if [[ $? != 0 ]] ; then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: EEEEE could not remount $PARTITION to /mnt/$UNIQUENAME, exiting" >> /tmp/log.txt && exit 1 exit 1 fi echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: checksum of usb stick $UNIQUENAME is $(md5deep -rbs /mnt/$UNIQUENAME | sort | md5sum)" >> /tmp/log.txt /bin/sleep 10 /bin/umount $PARTITION if [[ $? != 0 ]] ; then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: EEEEE could not unmount usb stick, exiting." >> /tmp/log.txt exit 1 fi /bin/rmdir "/mnt/$UNIQUENAME" if [[ $? != 0 ]] ; then echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: EEEEE could not delete /mnt/$UNIQUENAME after checksumming, seems its not empty, exiting." >> /tmp/log.txt exit 1 fi echo "$(date +%Y%m%d-%H:%M:%S): $UNIQUENAME: done." >> /tmp/log.txt /bin/rm $LOCKFILE /usr/bin/logger "done setting up USB stick $UNIQUENAME" } &

Now grab some USB hubs, plug in as many USB sticks as you can and see how things in /stuff/to/copy/on/stick/ are replicated to all the USB sticks.

Oh, you know what's most important? DISABLE THE SCRIPT WHEN YOU'RE DONE! I missed that part and ruined a colleague's USB-stick after he had started using it for stuff other than the conference's proceeedings :(

Wednesday, April 13, 2011

bluetooth-keyboard with bluez 4.91

I've searched a lot and found nothing on this topic. Last year, i managed to get the keyboard connected, but it always lost keypresses or, even worse, key-releases, making the whole thing unusable.

Today, jhe in #bluez-users helped me to get it working:

he: first, forget about hidd. (its an old, unused daemon)

he: ensure that bluetoothd has loaded its input plugin
(this should happen as long as you don't have "input"
in the DisablePlugins value in /etc/bluetooth/main.conf

he: once you've done the above, pairing and connecting
your keyboard is just two command line commands (one for
pairing and another for connecting)

he: to pair: "bluez-simple-agent hci0 <keyboard address>"
(the script is called test/simple-agent in the bluez
source tree)

he: to connect: "bluez-test-input connect <keyboard
address>" (test/test-input in the source tree) and you
should of course follow your keyboards instructions on
how to make it pairable & connectable and how/when to
enter the PIN on it

he: once you've got all that successfully done you can
do one more optional step to make sure the keyboard can
reconnect to your PC without the need of explicitly
authorizing the connect request from some GUI: "bluez-
test-device trusted yes"



me: why is the "test" in those names?

he: test is in those names since they are considered
test scripts for bluez



me: so, my distribution just needs to make sure that
bluetoothd will be started on bootup, right?

he: yes



me: oh, because bluez is supposed to be used by some gui, ok

he: bluez comes neither with an official GUI client nor
with an official CLI client. So there's really no "supposed"
as far as bluez is concerned



me: gentoo's bluetooth start-script only calls "udevadm
trigger --subsystem-match=bluetooth --action=add". Do you
now how that starts bluetoothd?

he: not for sure, but I can guess: it tells udev to start
bluetoothd when it detects a hardware device that matches
subsystem=bluetooth



me: ok, sounds good. But there is no "bluetoothd" anywhere
in /etc/udev/rules.d/*.

he: I'm not familiar with the gentoo system configuration
so you'll have to figure this part out by yourself

And using those commands, I actually got it to work. Wheeeee!

Friday, July 9, 2010

Posting code

It seems really weird that blogger doesn't have facilities for posting code honoring linebreaks, whitespace and maybe even syntax highlighting. There are some workarounds where you paste code in some textfield and get html that you're supposed to paste into your blog. I think that sucks.

I edited my template and changed .post-body blockquote to read:

.post-body blockquote {
border:1px dashed #dddddd;
background-color:#eeeeee;
padding:10px;
font-size:9pt;
white-space: pre;
font-family: monospace;
}

Now you can paste your code even using the HTML editor using the quote-button. Of course, it still doesn't do syntax highlighting and neither does it work for HTML, but its a start.

gsmlib

I got a huawei e620 / vodafone K3520 umts usb stick to send and receive sms using linux.

Unfortunately, gsmlib-1.11_pre041028 seems to be unmaintained right now. Gentoo already carries patches to enable compilation with gcc3.4, 4.1 and 4.3, and Mr. Hofmann doesn't seem to reply to emails. At least reading his code turned out to be easy - thanks for that!

Anyway, when I tried

gsmsendsms -d /dev/ttyUSB0 -C +491710760000 +49160<...> Wheeeeeeeee


all I got was

gsmsendsms[ERROR]: expected parameter (at position 1 of string ',1,1,1')


I wondered for quite a while where the heck gsmsendsms thought I had passed such a parameter, but later found out that the string ",1,1,1" was actually a reply to an AT-command issued to the huawei stick.

To keep a longer story of recompiling, reading, replugging the stick, and looking up AT commands short, here's the patch - hopefully self-explanatory:

--- /tmp/gsm_me_ta.cc 2010-07-09 12:07:13.091842964 +0200
+++ /usr/src/gsmlib-1.11/gsmlib/gsm_me_ta.cc 2010-07-09 12:10:08.105440813 +0200
@@ -120,7 +120,22 @@

// find out whether we are supposed to send an acknowledgment
Parser p(_at->chat("+CSMS?", "+CSMS:"));
- _capabilities._sendAck = p.parseInt() >= 1;
+
+ try
+ {
+ _capabilities._sendAck = p.parseInt() >= 1;
+ }
+ catch (GsmException)
+ {
+ // Some huawei usb gsm/umts sticks reply to AT+CSMS? with "+CSMS: ,1,1,1"
+ // There probably should be a 0 or 1 before the first comma, indicating
+ // whether an incoming SMS should be acknowledged (by issuing +CNMA).
+ // This causes parseInt() -> checkEmptyParameter() to throw an exception
+ // I suppose that if the huaweis needed this, they would have taken care
+ // to set the first bit to 1.
+ // Confirmed so far only for E620 / Vodafone K3520
+ _capabilities._sendAck = false;
+ }

// set GSM default character set
try


gsmsmsd throws a "303 unsupported" error when used with "--direct" (enable direct routing of SMSs). I might have to look into that next.

I remeber typing those ATDTs back in the nineties and just love how they made it into the new milennium :)