I basically stopped using the bluetooth subsystem on my machine for a while, since it would lockup my system during sleep. Today I decided to investigate a little, here’s what I found out:

  1. Stopping the bluetooth daemon will bring down the bluetooth subsystem. You can check it’s state as follows:
    cat /sys/module/btusb/refcnt
    anything besides 0 means it’s doing something.
  2. The freeze occurs when bluetooth is paired with a device and someone trys to bring down bluetooth. Subsequently playing music through A2DP and then stopping the bluetooth daemon results in a hard freeze.
  3. To fix all these issues you need to disconnect all connected bluetooth devices before going to sleep.

Copy & paste the following script (or simply download ) in your /etc/pm/sleep.d folder to fix the freeze. Feel free to adapt it to your distribution.

#!/bin/sh
# generic bluetooth hack based on the IBM specific hack
# Reza Jelveh

. "${PM_FUNCTIONS}"

[ -f /sys/module/btusb/refcnt ] || exit $NA

disconnect_devices()
{
	# sorry not that good with bash scripting,
	# so i'll use a space as string terminator
	var="`hcitool con` "

	while test -n "$var";do
		res="${var%% *}"
		var="${var#* }"
		if [[ $res =~ ([[:alnum:]:]{17}) ]]; then sudo hcitool dc ${BASH_REMATCH[0]}; fi
	done
}

suspend_bluetooth()
{
	if grep -q 0 /sys/module/btusb/refcnt; then
		savestate bluetooth_generic disable
	else
		savestate bluetooth_generic enable
		# if we run stop while some bluetooth device is running
		# the system will freeze, disconnect all devices first
		disconnect_devices
		/usr/sbin/rc.d stop bluetooth

		# wait for up to 2 seconds for the module to actually get
		# unused
		TIMEOUT=20
		while [ $TIMEOUT -ge 0 ]; do
		    [ `cat /sys/module/btusb/refcnt` = 0 ] && break
		    TIMEOUT=$((TIMEOUT-1))
		    sleep 0.1
		done
		/sbin/modprobe -r btusb
	fi
}

resume_bluetooth()
{
	state_exists bluetooth_generic || return
	if [[ `restorestate bluetooth_generic` == "enable" ]]; then
		/sbin/modprobe btusb
		/usr/sbin/rc.d start bluetooth
	fi
}

case "$1" in
	hibernate|suspend)
		suspend_bluetooth
		;;
	thaw|resume)
		resume_bluetooth
		;;
	*) exit $NA
		;;
esac