Setting up my JVC XP 731

Note: After more than 10 years, I've retired my XP 731 to migrate to a Lenovo X240. Hence, this page is essentially unmaintained.

Installing Linux on the JVC XP 731 (a.k.a. Asus S200 N) used to be not a big deal -- it's a fairly standard Centrino machine. Since Debian stretch, however, hibernation and suspend is broken out of the box, and I doubt I'll investigate matters. I'm rather sure things should work fine if you manage to disable the puny “GPU” in this thing (see below). Me, I'll leave the JVC boxes I still run on jessie – they're not on any serious network, and they don't listen to many ports. Still, if you figure out the power management problems, let me know.

The only thing that may cause you headache is the installation medium. If you don't have a USB cdrom, I recommend netbooting the beast. You can access the setup menu by pressing Alt-F2 while the machine shows its BIOS screen (by default, it's some flashy picture, though there is a BIOS setting that gives you a more informative output). To let the machine netboot, you'll first have to enable the boot rom on the corresponding BIOS page, exit the setup, and the fix the boot priority by entering the BIOS a second time.

You'll find more info on the basic setup on the corresponding pages on TuxMobil.

With Debian Wheezy (or Squeeze, or Jessie), everything should run out of the box. If you want to run X servers older than 7.3, you will probably want to refer to an older version of this page.

I ran my box with a self-built mainline kernel. The last version I had running was 3.14.22 (kernel .config); distribution kernels generally work well as well, though some had major issues with the i915 driver, in particular in connection with the 3D and suspend/resume.

ACPI -- sleep, wake up, hotkey

Note: By now, everything probably works out of the box. So, in all likelihood, you will not want to jump through the hoops described here.

Hotkeys

To make ACPI work, you must have acpid installed. It is usually configured in a bunch of files in /etc/acpid. Your distribution may already have populated that directory. Unless you understand what's happening in there, if you want to run the system proposed here I advise you move that cruft out and give it a fresh start.

Let's first get the hotkeys working. They are handled by the asus_acpi module. Once you have it inserted in your kernel, acpid receives the relevant events. You just need to tell it what to do with them. Creating /etc/acpi/events/hotkeys and putting

event=hotkey.*
action=/etc/acpi/hotkey.sh %e

File: /etc/acpi-demi/events/hotkeys

in there (the syntax for the files in .../event is a bit Debian-specific, so man acpid) tells it to call /etc/acpi/hotkey.sh for each event starting with "hotkey". Some sort of keycode is passed to that script as its third argument. Thus, for the XP 731, hotkey.sh could look like this:

#!/bin/sh
case "$3" in
	0000006c)  # Sleep button
		/etc/acpi/doze.sh
		;;
	0000006d)  # Suspend to disk button
		/etc/acpi/sleep.sh
		;;
	00000031)  # decrease volume
		amixer set Master 2%-
		;;
	00000030)  # increase volume
		amixer set Master 2%+
		;;
	00000032)  # mute
		amixer set Master toggle
		;;
	00000035)  # the key with the screen
		logger "The key with the screen :-)"
		;;
	*)
#		logger "$0: Unknown hotkey: $@"
		;;
esac

File: /etc/acpi-demi/hotkey.sh

I'll give my doze.sh and sleep.sh scripts below. The rest assumes you're running ALSA for sound (you should). What this does is let the volume keys work as advertised. The "key with the screen" is bound to some deadly function by the ACPI bios; without some change to the DSDT it probably can't be used to do useful things.

Software Suspend

We want two ways of sleeping: Suspend to RAM and suspend to disk. Suspend to RAM is far more difficult, so let's look at suspend to disk first. The hotkeys above expect the script in /etc/acpi/sleep.sh:

#!/bin/bash

. /etc/acpi/utils.sh
if [ -z "$stateDir" ]
then
	logger "$0: utils.sh could not be sourced"
	exit 1
fi

if test -f $lastWakeupFile && 
   test $((`date +"%s"` - `date -r $lastWakeupFile +"%s"`)) -lt 30
then
   logger "$0: suppressing suspend request (deadtime not over)"
	 exit 1
fi

getLoadedCriticalModules
unloadCriticalModules

logger "$0: Putting machine in software suspend"

sync; sync

echo shutdown > /sys/power/disk
echo disk > /sys/power/state

touch $lastWakeupFile

logger "$0: Coming back from software suspend"

reloadCriticalModules

File: /etc/acpi-demi/sleep.sh

with the following /etc/acpi/utils.sh:

# This script defines a few functions useful for all things acpi-related


criticalModules=""
stateDir=/var/run/sleep
mkdir -p -m 755 "$stateDir"
sleepLockFile=$stateDir/lock
lastWakeupFile=$stateDir/lastWakeup


dieNoSuspend() {
	reloadCriticalModules
	if [ -f /etc/acpi/nosleep.wav ]
	then
#		amixer set Master 100
#		amixer set PCM 100
		play /etc/acpi/nosleep.wav
	fi
	logger "$0: Won't suspend: $*"
	exit 1
}

reloadCriticalModules() {
# This only works if unloadCriticalModules has been called before.
	for modName in $unloadedModules
	do
		modprobe $modName
	done
}

unloadCriticalModules() {
# You need to call getLoadedCriticalModules first, it sets the global
# loadedCriticalModules
	modStatus="ok"
	unloadedModules=""
	for modName in $loadedCriticalModules
	do
		rmmod $modName || modStatus="fail"
		unloadedModules=`echo $unloadedModules $modName`
	done
	# first attempt may have failed because someone was looking at
	# some bad file for an instant, try again once
	if [ $modStatus == "fail" ]
	then
		logger "$1: Unload modules failed, retrying once."
		reloadCriticalModules
		for modName in $loadedCriticalModules
		do
			rmmod $modName  2> /var/run/sleep/rmmod.prot || dieNoSuspend "$modName in use:" `cat /var/run/sleep/rmmod.prot`
			unloadedModules=`echo $unloadedModules $modName`
		done
	fi
}


filterLoadedModules() {
	for modname in $*
	do
		lsmod | grep "^$modname " | awk '{print $1}'
	done 
}

getLoadedCriticalModules() {
	loadedCriticalModules=`filterLoadedModules $criticalModules`
}


dieIfSleepLocked() {
	if lockfile -! -r 0 $sleepLockFile
	then
		logger "$0: Sleep processing in progress, ignoring request"
		exit 1
	fi
	trap unlockSleep EXIT
}


unlockSleep() {
	rm -f $sleepLockFile
}

File: /etc/acpi-demi/utils.sh

The functions in utils use crude locking to keep multiple instances of sleep scripts from running. I believe acpid serializes event handlers anyway, so this kind of locking may not be necessary, but I wouldn't bet. Make sure /var/run/sleep exists and is only writable by root.

There are also functions to identify which modules out of a predefined set are loaded, to remove them, and to reload removed modules. These exchange information through global variables. Yikes. Feel free to amend the list of critical modules or take some out. You shouldn't remove the usb modules from this list, though. Under certain circumstances they'll go mad on wakeup, enticing the kernel to disable all kinds of interrupts. This then basically kills most of the PCI I/O the machine does (e.g., network).

The script will refuse to suspend if any of the modules cannot be removed (e.g., because you've still mounted a file system from a usb disk). So, always check that the machine is really sleeping before you put it into bags or tight spaces with insufficient ventilation. You can drop a wav file named nosleep.wav into /etc/acpi. It will be played at full volume if the machine can't sleep, which may help warn you.

The sleep.sh itself uses the Linux software suspend rather than the ACPI one -- it's much less hassle and about as fast. Just compile support for that into your kernel. Software suspend works by writing out the RAM to a swap partition before turning the box off and then passing a kernel option when booting. The kernel option tells the machine to restore the RAM from the swap partition.

Suspend to RAM

To let the machine sleep when you close the lid, add /etc/acpi/events/lid with the contents

event=button/lid.*
action=/etc/acpi/lid.sh

File: /etc/acpi-demi/events/lid

and then say something like

#!/bin/bash

if [ -f /var/lock/ignorelid ]; then
	exit 0
fi

. /etc/acpi/utils.sh
if [ -z "$stateDir" ]
then
  looger "$0: utils.sh could not be sourced"
  exit 1
fi

if [ ! -f $sleepLockFile ]
then
  /etc/acpi/doze.sh
else
  logger "$0: Lid event while sleep in progress -- ignoring."
fi

File: /etc/acpi-demi/lid.sh

in /etc/acpi/lid.sh. With kernels starting with 2.6.20, you need the primitive state mangagement built in above -- there is a deadtime in which requests to put the machine to sleep are ignored. Otherwise, the lid event generated by opening the box would put your machine to sleep again, and that's not so nice:-).

You could also bind the software suspend to the power button in an analogous fashion.

Video

As of Debian Squeeze, don't configure anything for the default X. I do this by giving an empty file /etc/X11/xorg-internal.conf:

File: /etc/X11/xorg-internal.conf

Now say

command=/usr/X11R6/bin/X -audit 0 -config xorg-internal.conf

in the [server-Standard]-Section of /etc/gdm/gdm.conf. This makes gdm use the no-dri config for its servers, so that's what you get when you log in. Other login managers should have similar options, and it's trivial to figure out what to do if you're using startx.

In squeeze, it seems for the intel driver the default will be to have Shadow on. This inhibits direct rendering (and thus GPU usage). To have fast 3D, you'll need to turn shadow off. You can do this in a special /etc/X11/xorg.conf (see docs or ask if necessary).

So, if you actually need 3D, you start another server, e.g., like this (I'm using sawfish, so this is cheap):

# The following gives you a GL server on vt 8
startx -- :1 

Usually, you're not allowed to start servers from within an X session, which causes this to bomb out. To enable it, in Debian you have to dpkg-reconfigure xserver-common (or edit /etc/X11/Xwrapper.config by hand, saying allowed_users=anybody).

Instead of the plethora of screens and layouts I used to have in the xorg.conf, you can now use xrandr. I use this within a script that caters to a few scenarios I need now and then:

#!/bin/sh

# das neue LCD braucht evtl.
#  xrandr --output eDP1 --gamma 1.1:1.1:1.1 --brightness 1

. ~/.alias

case $1 in
	demo)
		xrandr --fb 1024x768
		xrandr --addmode eDP1 1024x768
		xrandr --addmode DP2 1024x768
		xrandr --output eDP1 --off
		xrandr --output eDP1 --mode 1024x768
		xrandr --output DP2 --mode 1024x768 --same-as eDP1
		xset -dpms s off
		;;
	dockdemo)
		xrandr --fb 1024x768
		xrandr --output eDP1 --mode 1024x768
		xrandr --output DP2-3 --mode 1024x768 --same-as eDP1
		xset -dpms s off
		;;

	hdmidemo)
		xrandr --addmode eDP1 1024x768
		xrandr --addmode HDMI1 1024x768
		xrandr --output eDP1 --mode 1024x768
		xrandr --output HDMI1 --mode 1024x768 --same-as eDP1
		xrandr --fb 1024x768
		xset -dpms s off
		;;

	single)
		killall xlogo 2> /dev/null
		killall feh 2> /dev/null
		xrandr --output DP2-1 --off
		xrandr --output HDMI1 --off --output DP2 --off --fb 1366x768
		# workaround for buster xrandr breakage: Reset eDP1 or the display
		# will be totally confused
		xrandr --output eDP1 --off
		xrandr --output eDP1 --mode 1366x768
		lid
		xset +dpms
		;;

	clone)
		xrandr --addmode DP2 "1366x768"
		xrandr --output DP2 --mode 1366x768 --same-as eDP1
		xset -dpms s off
		;;

	dockclone)
		xrandr --addmode DP2-3 "1366x768"
		xrandr --output DP2-3 --mode 1366x768 --same-as eDP1
		xset -dpms s off
		;;
	hdclone)
		xrandr --addmode HDMI1 "1366x768"
		xrandr --output HDMI1 --mode 1366x768 --same-as eDP1
		xset -dpms
		;;

	800)
		cp ~/.mplayer/config.narrow ~/.mplayer/config
		xrandr --fb 800x600
		xrandr --output eDP1 --mode 800x600
		xrandr --addmode HDMI1 "800x600"
		xrandr --output HDMI1 --mode 800x600 --same-as eDP1
		xset -dpms
		set s off
		;;
	extend)
		wmctrl -o 0,0
		xset -dpms
		xrandr --output eDP1 --auto --pos 0x0 --output DP2 --mode 1360x768 --above eDP1
		xrandr --dpi 96
		#xrandr --output eDP1 --pos 0x1200
		xmodmap ~/.Xmodmap
		sleep 2 # give nohup xlogo a chance to come up before HUP
		;;
	hdextend)
		xset -dpms
#		xrandr --output eDP1 --auto --pos 0x0 --output HDMI1 --mode 2048x1152 --above eDP1
		xrandr --output eDP1 --auto --pos 0x0 --output HDMI1 --auto --above eDP1
		xrandr --dpi 96
		xmodmap ~/.Xmodmap
		customxplanet single
		;;

	hdmi)
		wmctrl -o 0,0
		xset -dpms
		xrandr --output eDP1 --off --output HDMI1 --auto
		# xrandr --fb 2048x1152 --output eDP1 --off --output HDMI1 --mode 2048x1152
		xrandr --dpi 96
		xmodmap ~/.Xmodmap
		customxplanet single
		;;
	beamer)
		xset -dpms
		xrandr --output eDP1 --auto --pos 0x0 --output HDMI1 --auto --below eDP1
		xrandr --dpi 96
		xmodmap ~/.Xmodmap
		/home/msdemlei/mybin/blotchup
		sleep 2 # give nohup xlogo a chance to come up before HUP
		;;
	video)
		cp ~/.mplayer/config.narrow ~/.mplayer/config
		export XSTARTUP=video
		startx -- :1
		xrandr --output HDMI1 --off
		cp ~/.mplayer/config.normal ~/.mplayer/config
		;;
	dri)
		export XSTARTUP=local
		startx -- :1
		;;
	ag)
		cp ~/.mplayer/config.narrow ~/.mplayer/config
#		xrandr --addmode DP2 "1280x800"
		xrandr --output DP2 --mode 1366x768 --below eDP1
		xrandr --dpi 100
		/home/msdemlei/mybin/blotch
		xset -dpms s off
		sleep 2
		;;
	projpause)
		cp ~/.mplayer/config.narrow ~/.mplayer/config
#		xrandr --addmode DP2 "1360x768"
		xrandr --output DP2 --mode 1024x768 --below eDP1
		/home/msdemlei/mybin/blotch
		xset -dpms s off
		sleep 2
		;;
	*)
		echo "Unknown video configuration: $1"
		exit 1
esac

File: /home/msdemlei/mybin/confscreen

ACPI viewing

The JVC has two batteries, which is something most applets don't really provide for. That's why I started hacking wmacpimon. My current result is available here. I'll do a proper release some day, and I'll give it a name of its own, because it doesn't have that terribly much in common with wmacpimon any more. Do let me know if you use it. It would definitely be a motivation to develop this thing into some well-behaved nice, cpu-saving monitor applet.

Note that at least on my machine, the calibration of the external battery is way off. If claims to have about three times the capacity it really has. In consequence, the applet right now estimates the power consumption of the machine way too high while running from the external battery (about 30 Watts instead of about 12 Watts). This, in turn, means that the running time from the internal battery is grossly underestimated. When the machine switches from external to internal battery you'll note that the power drain drops and the remaining run time increases dramatically (you should get about one hour out of the internal battery). If anyone has a good suggestion on how to fix this, please let me know (I wonder how Windows copes with shit like that -- if it does at all, that is).


If you've got stuff to contribute: msdemlei@ari.uni-heidelberg.de