Bluetooth On SHR-u

Wiki source:

= Testing GSM <-> Bluetooth on FreeRunner =

'''Basics:''' We need to tell audio codec to route sound from GSM chip to Bluetooth chip. Codec is controlled by ALSA and mixer settings. The audio chip is described here: .

'''Prerequisite:''' Bluetooth device must be paired before the operation.

Note: 00:13:17:78:7B:A3 is Bluetooth MAC of my headset, modify accordingly.

== Step one: powering on bluetooth, setting up bluetoothd, pairing ==

Set /etc/bluetooth/audio.conf like this:

 # SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA)   
 # Defaults to HCI                                                               


 echo 1 > /sys/bus/platform/devices/neo1973-pm-bt.0/power_on
 sleep 1
 hciconfig hci0 up

Do this ONCE (the prerequisite): use simple-agent from here: to pair the device (or pair in any other valid way).

Connect to the headset:

 BTADAPTER=`dbus-send --system --dest=org.bluez --print-reply / org.bluez.Manager.DefaultAdapter | tail -1 | sed 's/^.*"\(.*\)".*$/\1/'`
 dbus-send --system --dest=org.bluez --print-reply $BTADAPTER/dev_00_13_17_78_7B_A3 org.bluez.Headset.Connect

Test: "hcitool con" should say:

     < ACL 00:13:17:78:7B:A3 handle 42 state 1 lm MASTER 

 BTADAPTER=`dbus-send --system --dest=org.bluez --print-reply / org.bluez.Manager.DefaultAdapter | tail -1 | sed 's/^.*"\(.*\)".*$/\1/'`
 dbus-send --system --dest=org.bluez --print-reply $BTADAPTER/dev_00_13_17_78_7B_A3 org.bluez.Headset.Connect

should say: Error org.bluez.Error.AlreadyConnected: Already Connected

=== More debug examples ===

 root@om-gta02 /usr/lib/python2.6/site-packages $ dbus-send --system --dest=org.bluez --print-reply $BTADAPTER/dev_00_13_17_78_7B_A3 org.bluez.Headset.Disconnect
 method return sender=:1.18 -> dest=:1.287 reply_serial=2

 root@om-gta02 /usr/lib/python2.6/site-packages $ dbus-send --system --dest=org.bluez --print-reply $BTADAPTER/dev_00_13_17_78_7B_A3 org.bluez.Headset.Connect
 Error org.bluez.Error.ConnectionAttemptFailed: Input/output error

 root@om-gta02 /usr/lib/python2.6/site-packages $ dbus-send --system --dest=org.bluez --print-reply $BTADAPTER/dev_00_13_17_78_7B_A3 org.bluez.Headset.Connect
 method return sender=:1.18 -> dest=:1.290 reply_serial=2

 root@om-gta02 /usr/lib/python2.6/site-packages $ dbus-send --system --dest=org.bluez --print-reply $BTADAPTER/dev_00_13_17_78_7B_A3 org.bluez.Headset.Play   
 method return sender=:1.18 -> dest=:1.292 reply_serial=2

== Step two: make a call ==

Call to the phone and answer (or place a call, whichever is cheaper).

== Step three: load ALSA settings to switch codec "mode" to GSM <=> Bluetooth ==

Wait till frameworkd sets its statefile and then overwrite it with your own:

 alsactl -f /usr/share/shr/scenarii/gsmbluetooth.state restore
 amixer sset "Capture Left Mixer" "Analogue Mix Right"
 amixer sset "Capture Left Mixer" "Analogue Mix Left"

(last two lines due to kernel bug).

Issue dbus call to start exchanging audio between GSM and Bluetooth:

 BTADAPTER=`dbus-send --system --dest=org.bluez --print-reply / org.bluez.Manager.DefaultAdapter | tail -1 | sed 's/^.*"\(.*\)".*$/\1/'`
 dbus-send --system --dest=org.bluez --print-reply $BTADAPTER/dev_00_13_17_78_7B_A3 org.bluez.Headset.Play

Set bluetooth IIS to 8000 rate and keep it with a sleep in Python:


Where the is:

 import alsaaudio
 import time

 pcm_play = alsaaudio.PCM( alsaaudio.PCM_PLAYBACK, alsaaudio.PCM_NONBLOCK, "hw:0,1" )

 pcm_cap = alsaaudio.PCM( alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NONBLOCK, "hw:0,1" )

 while True:
     time.sleep(999) # wait till user quits this nasty program

Now Bluetooth should work. Don't touch the on-screen sliders though, because it will change ALSA settings and you'll lose Bluetooth connection.

After that you should have the following command output:

 root@om-gta02 ~ $ hcitool con
     < SCO 00:13:17:78:7B:A3 handle 45 state 1 lm SLAVE 
     < ACL 00:13:17:78:7B:A3 handle 42 state 1 lm MASTER

IRC log:

(20:18:40) Gabrys: but do we have any docs
(20:18:44) Gabrys: on how it actually works
(20:18:52) PaulFertser: Gabrys: i can explain the idea
(20:19:10) Gabrys: PaulFertser: I'd be grateful
(20:19:18) PaulFertser: Gabrys: the high-level idea is that to get gsm sound on your BT headset the following method is used:
(20:19:51) PaulFertser: Gabrys: gsm call is established the usual way, the GSM modem inputs/outputs sound via analog lines to the wolfson codec, that's the usual thing.
(20:21:47) Gabrys: PaulFertser: and how it is routed to BT, I guess it never really goes into kernel or bluez
(20:21:48) PaulFertser: Gabrys: to actually use bluetooth: 1. bluetoothd should be given special command to "turn on and activate the headset" 2. the wolfson codec should be configured to route audio to the second audio interface (that is connected directly to bluetooth chip). 3 the wolfson codec should receive proper parameters for the second audio interface
(20:22:55) PaulFertser: 2. is taken care by loading the state file (and unfortunately the trick mentioned on the wiki is still needed, it's a kernel bug) 3. is taken care by FSO that uses some alsa API to set 8000 mono for hw:0.1 device (or something like that)
(20:23:48) PaulFertser: Gabrys: this method of using bluetooth headsets is called "SCO over PCM" because it's SCO frames flowing to/from headset and it's "PCM" interface of the bluetooth chip directly connected to the wolfson codec.
(20:24:11) Gabrys: hmm
(20:24:37) Gabrys: one quick question, can we get GSM audio in/out routed to alsa/pulseaudio/whatever-in-software
(20:24:54) Gabrys: like here
(20:24:56) PaulFertser: Gabrys: yes, with proper "statefile"
(20:24:57) mjr: in principle at least
(20:25:14) Gabrys: this way we could simplify the magic a bit
(20:25:22) PaulFertser: Gabrys: but later routing it to BT is tricky :)
(20:25:30) Gabrys: to route the audio to ALSA so we can hear it
(20:25:32) Gabrys: easy to debug
(20:25:45) Gabrys: and then from ALSA to BT, which is quite well documented
(20:25:54) PaulFertser: Gabrys: you want to get bi-directional operation, then it means you want SCO, "SCO over HCI".
(20:26:13) PaulFertser: Gabrys: with that you get a surprise, you need to modify EEPROM key in bluetooth chip to activate SCO over HCI
(20:26:43) Gabrys: and why can't we use ALSA in between?
(20:27:08) PaulFertser: Gabrys: you can, but to actually use SCO over HCI you need to tweak that EEPROM setting.
(20:27:16) Gabrys: hm
(20:27:31) PaulFertser: Gabrys: i did that (only tried playing an mp3 over SCO)
(20:27:43) Gabrys: aaah, I see
(20:27:49) Gabrys: SCO over PCM is "easy"
(20:28:02) PaulFertser: Exactly. You just do one bluetoothd dbus call to activate it.
(20:28:03) Gabrys: but SCO over HCI (means ALSA -> BT) is "hard"
(20:28:22) Gabrys: and how about the GSM -> ALSA part?
(20:28:35) Gabrys: is it quite easy?
(20:28:57) PaulFertser: Gabrys: if you get the right state file. I remember folks had difficulties crafting one, not sure why.
(20:29:09) PaulFertser: Gabrys: have you seen "Neo1973_Audio_Subsystem" wiki page?
(20:29:13) Gabrys: and what's exactly the state file?
(20:29:47) Gabrys: and the audio subsystem is the same between gta01 and gta02?
(20:29:54) mickeyl nazywa siÄ™ teraz mickey|tv
(20:30:03) PaulFertser: Gabrys: that page is mostly gta02-only anyway :)
(20:30:16) PaulFertser: Gabrys: the wolfson chip is the same. The kernel driver is a bit different.
(20:30:26) PaulFertser: Gabrys: anyway, the "pretty" diagram is for gta02.
(20:31:28) Gabrys: I regret we don't have everything going through sw somehow 
(20:31:30) Gabrys: ;-)
(20:31:34) PaulFertser: Heh
(20:31:46) Gabrys: it may not be simpler at first, but it would be easier to debug and test :-)
(20:31:56) Gabrys: as you can always easily dump things from sw
(20:32:11) PaulFertser: Gabrys: if you're really serious about doing SCO over HCI, i'll share the details, they're not secret, but not really documented in a sane way anywhere either.
(20:32:24) PaulFertser: Gabrys: but first of all, try SCO over PCM
(20:32:48) Gabrys: PaulFertser: ok, I just tried "everything" documented to get it working 
(20:32:56) PaulFertser: Gabrys: try to understand why the BluetoothIsConnected (or something) is failing.
(20:33:10) Gabrys: yeah, I wanted to test this as well :-)
(20:34:15) PaulFertser: Gabrys: in fact if you know python at least somewhat (i've never learned it but i can understand fso sources) it would be nice if you made bluetooth work.
(20:34:30) Gabrys: well, I'm a python coder, kind of ;-)
(20:35:04) Gabrys: the code lacks some in-code documentation though ;-)
(20:35:47) PaulFertser: Gabrys: come on, even if i can understand it ;)
(20:36:49) Gabrys: what about this note:
(20:36:50) Gabrys:   using Bluetooth headset with GSM  NOTE none of this works with GTA02. Neo mode has disappeared, and none of the state files are GTA02 compatible. 
(20:37:07) PaulFertser: Gabrys: some old note from whereever, ignore
(20:37:12) Gabrys: ok
(20:37:19) PaulFertser: Gabrys: there's no "modes" at all in gta02 driver.
(20:39:18) Gabrys: my understanding of state files right is now they are states of ALSA settings 
(20:39:30) Gabrys: and ALSA can set internal switches of codec 
(20:39:36) Gabrys: that controls routing of sound
(20:39:54) PaulFertser: Exactly
(20:40:03) Gabrys: so we can using ALSA switch snd chip to route sound from GSM to BT
(20:40:43) Gabrys: so in fact, I can set them all with alsamixer instead using state files?
(20:40:48) PaulFertser: Gabrys: my notion is that the statefile is all right.
(20:40:57) PaulFertser: Gabrys: many people tried it and it was ok.
(20:41:19) PaulFertser: Gabrys: of course, alsamixer will work as well. Just do not forget fso likes to load different scenarious every now and then.
(20:41:29) Gabrys: sure 
(20:41:45) PaulFertser: Gabrys: and about the dirty trick (near the end of "manually using" page) needed due to kernel bug.
(20:42:33) Gabrys: what's the trick? I can't find it 
(20:42:43) Gabrys: the disable_esco?
(20:42:47) PaulFertser: No
(20:43:08) PaulFertser: The next one :)))
(20:43:20) Gabrys: I see 
(20:43:22) Gabrys: amixer ...
(20:43:27) Gabrys: amixer sset "Capture Left Mixer" "Analogue Mix Right" amixer sset "Capture Left Mixer" "Analogue Mix Left" 
(20:43:39) Gabrys: how do I load the state file manually?
(20:44:43) PaulFertser: Gabrys: alsactl restore
(20:45:30) PaulFertser: Gabrys: and the bluetoothd call is org.bluez.Headset Play() iirc
(20:45:38) Gabrys: OK, I'm going to put in a wiki page, what I'm going to test 
(20:45:42) Gabrys: and how 
(20:46:25) PaulFertser: I really think there's some misunderstanding between fso and bluetoothd right now. FSO should "sense" automatically that bluetoothd is up and headset is there, there's fso code for that.
(20:47:47) Gabrys: well, the problem is bluetoothd is not up
(20:47:54) Gabrys: because is now handled differently 
(20:47:58) Gabrys: it's not a deamon anymore 
(20:49:26) PaulFertser: Gabrys: can't be. It was a daemon and it is still a daemon.
(20:51:11) Gabrys: what I want to do is omit frameworkd completely 
(20:51:13) Gabrys: just for test 
(20:53:30) PaulFertser: Gabrys: how will you initiate gsm call? With socat+AT commands?
(20:53:39) Gabrys: I would call :-)
(20:53:47) Gabrys: for this frameworkd work :D
(20:54:09) Gabrys: I'll let frameworkd set statefile and then load my own
(20:55:13) PaulFertser: Gabrys: have you forgotten about part 3?
(20:55:24) PaulFertser: Gabrys: to set bluetooth IIS to 8000 rate etc.
(20:55:32) PaulFertser: Gabrys: check out the relevant code in FSO.
(20:55:36) Gabrys: right 
(20:55:44) Gabrys: what happens if we don't set this right?
(20:56:04) Gabrys: do we have no sound or some "wrong" sound 
(20:56:05) PaulFertser: No idea, probably they're already sane by default, probably not.
(20:56:28) PaulFertser: I do not remember details of PCM interface. Probably you'll get "wrong" sound.
(20:56:41) Gabrys: this would work :-)
(20:56:52) Gabrys: I mean, this says something
(20:58:06) Gabrys: PaulFertser: please take a look at this:
(20:59:08) PaulFertser: Gabrys: bluetoothd dbus call is missing
(20:59:41) Gabrys: what does it do?
(21:00:06) PaulFertser: Gabrys: "activates" bluetooth chip and the headset so they start exchange SCO frames.
(21:00:21) Gabrys: ah, this may be really needed :-)
(21:00:32) Gabrys: and I forgot about mentioning /etc/bluetooth/audio.conf config
(21:00:43) PaulFertser: Gabrys: code to configure rate etc is really trivial
(21:01:16) PaulFertser: Gabrys: the setting in audio.conf will affect only ability to issue that Play() dbus call. bluetoothd will stubbornly refuse to obey until its enabled.
(21:02:40) Gabrys: ok, could you point me to the rate setting code? :>
(21:03:43) PaulFertser: Gabrys:;a=blob;f=framework/subsystems/ophoned/;h=80f5517b426026c934ea17a9c95a4e3502a40d25;hb=HEAD 
(21:03:47) PaulFertser: Gabrys: startpcm
(21:03:50) PaulFertser: blablabla
(21:03:53) PaulFertser: grep is your friend
(21:03:55) PaulFertser: :)
(21:05:27) Gabrys: but I think it's for ALSA<->BT thing 
(21:05:38) Gabrys: I mean if you later don't use the device IN CODE 
(21:05:48) Gabrys: it would have no result I guess
(21:06:02) Gabrys: PaulFertser: or am I wrong
(21:06:22) PaulFertser: Gabrys: alsa is used to configure bluetooth IIS even despite it's not connected to cpu.
(21:06:35) Gabrys: ok
(21:07:56) PaulFertser: Gabrys: have you seen this:;a=history;f=framework/subsystems/ophoned/;h=80f5517b426026c934ea17a9c95a4e3502a40d25;hb=HEAD
(21:07:56) Gabrys: setting the rate is before the dbus call or after?
(21:08:01) PaulFertser: Gabrys: doesn't matter
(21:08:37) PaulFertser: Gabrys: i guess it works for Felix, probably you want to contact him 
(21:09:09) PaulFertser: Gabrys: or you're not running recent enough code. What's the date of your fso version?
(21:09:18) Gabrys: FSO I don't know 
(21:09:24) Gabrys: I have shr-u from yesterday 
(21:09:27) Gabrys: or even today 
(21:11:16) Gabrys: how do I verify the settings for hw0:1 are oK?
(21:11:46) PaulFertser: Gabrys: hm, no idea. I wouldn't bother. Just reapply with a simply python snippet if you're not sure.
(21:12:27) Gabrys: PaulFertser: OK, please take a look now
(21:13:42) PaulFertser: Gabrys: looks good
(21:14:07) Gabrys: PaulFertser: you probably meant alsactl restore < statefile
(21:14:28) PaulFertser: Gabrys: i'm sure you can figure out the correct syntax 
(21:14:36) PaulFertser: Gabrys: iirc it didn't require redirection
(21:15:22) Gabrys: ok, so I need now proper state file and dbus call 
(21:15:26) Gabrys: and probably I can test :-)
(21:15:47) PaulFertser: Gabrys: for dbus calls you'll find plenty of info on the same bluetooth page, it says enough about bluez4 api
(21:16:02) PaulFertser: And i'm sure shr has the proper state file preinstalled.
(21:16:16) Gabrys: I guess so :-)
(21:17:42) Gabrys:
(21:18:24) PaulFertser: Do not neglect the dbus introspection ;)
(21:19:19) Gabrys: I guess this would work 
(21:19:20) Gabrys: mdbus -s org.bluez /org/bluez/`pidof bluetoothd`/hci0/dev_00_13_17_78_7B_A3 org.bluez.Headset.Play
(21:19:22) Gabrys: ;)
(21:21:06) PaulFertser: Gabrys: omg, where did you read that "pidof bluetoothd"?
(21:21:17) PaulFertser: Gabrys: i thought i corrected it everywhere, as it no longer works
(21:21:20) Gabrys: what's wrong with this? :>
(21:21:27) Gabrys: it used to work for Connecting though :D
(21:21:51) PaulFertser: bluetooth guys changed their minds and this hack no longer applies
(21:21:56) PaulFertser: Where did you read that?
(21:22:10) Gabrys:
(21:22:20) PaulFertser: Fuck
(21:22:29) PaulFertser: I've told the guys to not copy-paste stuff!
(21:22:32) PaulFertser: :(
(21:23:01) PaulFertser: Gabrys: it would probably be nice if you removed all the stuff copied from the "Manually using bluetooth" page and linked it instead.
(21:23:32) Gabrys: on the SHR_User_Manual or on my page?
(21:23:41) PaulFertser: Gabrys: on SHR_User_Manual
(21:23:49) PaulFertser: I do not give a shit about your page ;)
(21:24:02) Gabrys: nice :D
(21:26:28) Gabrys: I don't know why 
(21:26:39) Gabrys: but the "new" way works the same as the pidof way
(21:27:08) PaulFertser: Gabrys: i'm sure pidof way broke starting from some bluez revision.
(21:27:49) vanous123: PaulFertser: how does one connect/reconnect now?
(21:28:08) vanous123: this is the only bit left in shr manual
(21:28:10) PaulFertser: vanous123: $BTADAPTER
(21:28:25) PaulFertser: vanous123: ah, so you removed most of the copied stuff. Thanks :D
(21:28:33) vanous123: i did already before
(21:33:29) PaulFertser: vanous123: please do not think i'm "dissing" your work on the manual
(21:34:46) vanous123: PaulFertser: no, i understand
(21:35:02) vanous123: this is why i removed all that
(21:35:17) vanous123: i had to edit a lot of other peoples additions
(21:35:27) vanous123: removed all related to theid desktops
(21:35:34) vanous123: and leave fr related stuff only
(21:35:47) vanous123: while the wiki keeps the relations...
(21:36:33) vanous123: PaulFertser: for pairing, i am recommending emtooth in the manual
(21:36:51) Gabrys: vanous123: emtooth is broken now
(21:37:18) vanous123: Gabrys: is it?
(21:37:26) Gabrys: yeap, I tested yesterday
(21:37:35) vanous123: Gabrys: changes in bluez?
(21:38:10) Gabrys: it discovers no devices, prints lots or error (I guess) messages to stdout/stderr and segfaults when clicking on Stop discovery 
(21:39:33) vanous123: testing or unstable?
(21:39:36) Gabrys: unstable 
(21:41:37) vanous123: ok
(21:43:04) Gabrys: I upgraded testing to unstable because of GUI bug of not being able to move volume sliders left ;-)
(21:43:11) vanous123: Gabrys: seems like one has to run bluetoothd manually
(21:43:17) vanous123: then it works
(21:43:47) vanous123: i have a feeling i read something about it
(21:43:49) Gabrys: I tried that, but still no luck, but maybe I miss-did something 
(21:44:16) vanous123: bluetoothd missing init script, not sure if by intention or a mistake
(21:45:25) Gabrys: I also noted that
(21:46:40) Gabrys: (11:39:53) Gabrys: neither bluez4 nor task-base-bluetooth provide /etc/init.d/bluetooth file 
(21:46:41) Gabrys: ;-)
(21:49:53) Gabrys: PaulFertser: could I ask to make a final look on testing procedure at
(21:57:11) Gabrys: I get some noise!
(21:57:24) Gabrys: but nothing more :(
(21:57:35) Gabrys: additionally frameworkd mutes sound 
(22:11:34) Gabrys: maybe it's even ok
(22:11:47) Gabrys: 'cause it mutes internal speaker
(22:16:18) PaulFertser: Gabrys: noise but no signal?
(22:16:27) PaulFertser: Gabrys: either that means wrong wolfson routing
(22:16:43) Gabrys: when I set up bt I hear 4 (?) clicks
(22:16:43) PaulFertser: Gabrys: or your headset is the same as the one i tested :|
(22:16:55) Gabrys: and then noise 
(22:17:01) Gabrys: ok, I'll check another one
(22:17:26) PaulFertser: Gabrys: i'm not sure hciconfig will be able to do anything before bluetoothd is started, so change the order
(22:17:40) Gabrys: ah, ok
(22:17:44) Gabrys: it seems to work
(22:17:49) Gabrys: though
(22:18:16) PaulFertser: Gabrys: because it's up by default :)
(22:18:23) PaulFertser: Gabrys: are you sure your alsactl command is ok?
(22:18:57) PaulFertser: Gabrys: it should be "alsactl -f <filename> restore
(22:20:32) Gabrys: ok, fixing this
(22:20:53) Gabrys: interesting thing is 
(22:21:01) Gabrys: I have now two connections
(22:21:05) Gabrys: one SCO and one ACL
(22:21:14) Gabrys: root@om-gta02 ~ $ hcitool con
(22:21:14) Gabrys: Connections:
(22:21:14) Gabrys:     < SCO 00:13:17:78:7B:A3 handle 45 state 1 lm SLAVE 
(22:21:14) Gabrys:     < ACL 00:13:17:78:7B:A3 handle 42 state 1 lm MASTER 
(22:21:15) PaulFertser: That's good
(22:21:42) PaulFertser: Btw, you should hear some "beep" after your issue Play() and yes, SCO connection appears.
(22:22:32) Gabrys: ha!
(22:22:44) Gabrys: I hear myself in the middle of the script 
(22:22:51) Gabrys: for about 1/4 of second 
(22:23:03) PaulFertser: Heh, interesting
(22:23:13) Gabrys: probably before python is run
(22:23:14) PaulFertser: In the middles of what script?
(22:23:23) Gabrys: I'll check one by one 
(22:23:37) Gabrys: I have alsactl, amixers, python set_pcm and dbus thing 
(22:23:50) Gabrys: so I'll check this commands one by one 
(22:24:06) PaulFertser: Gabrys: probably you should keep set_pcm running, as it might set some inappropriate defaults when connection is "closed".
(22:24:21) Gabrys: hmm
(22:24:25) Gabrys: ok, I'll check that :-)
(22:30:02) Gabrys: I got it working now!
(22:30:05) Gabrys: cool man :D
(22:30:23) Gabrys: PaulFertser: thanks for your great help in sorting this thing out
(22:30:37) PaulFertser: Gabrys: :)))
(22:32:19) Gabrys: so basically I'm moving python thing to the end 
(22:32:28) Gabrys: and make sleep(999) at the end of it
(22:32:45) Gabrys: so it breaks only after Ctrl-C
(22:32:48) DocScrutinizer: heh droogies
(22:37:58) PaulFertser: Gabrys: now the only thing left is to find why FSO doesn't do it on itself ;)
(22:38:04) Gabrys: yeap
(22:38:15) radek_: and qtmoko too please :)
(22:38:18) PaulFertser: Gabrys: i guess if you sum up all the steps they might be beneficial to some tinkerers.
(22:38:51) PaulFertser: radek_: i think you can compare now known to work Gabrys's instructions to what qtmoko does ;)
(22:39:50) radek_: PaulFertser: yes, i found this discussion really usefull - got it copy pasted in my mail :)
(22:47:38) Gabrys: ok guys, thank you for your help 
(22:47:51) Gabrys: I've cleaned the page 
(22:48:04) Gabrys: it should more useful now
(22:48:15) Gabrys: feel free to move it to more proper place 
(22:48:32) Gabrys: I need to get some sleep, I'll work on this tomorrow :-)
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License