2009-12-16 20:08:15 +03:00
/*
* Toshiba Bluetooth Enable Driver
*
* Copyright ( C ) 2009 Jes Sorensen < Jes . Sorensen @ gmail . com >
*
* Thanks to Matthew Garrett for background info on ACPI innards which
* normal people aren ' t meant to understand : - )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Note the Toshiba Bluetooth RFKill switch seems to be a strange
* fish . It only provides a BT event when the switch is flipped to
* the ' on ' position . When flipping it to ' off ' , the USB device is
* simply pulled away underneath us , without any BT event being
* delivered .
*/
2011-03-30 02:21:52 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2009-12-16 20:08:15 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
2013-12-03 04:49:16 +04:00
# include <linux/acpi.h>
2009-12-16 20:08:15 +03:00
MODULE_AUTHOR ( " Jes Sorensen <Jes.Sorensen@gmail.com> " ) ;
MODULE_DESCRIPTION ( " Toshiba Laptop ACPI Bluetooth Enable Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int toshiba_bt_rfkill_add ( struct acpi_device * device ) ;
2013-01-24 03:24:48 +04:00
static int toshiba_bt_rfkill_remove ( struct acpi_device * device ) ;
2009-12-16 20:08:15 +03:00
static void toshiba_bt_rfkill_notify ( struct acpi_device * device , u32 event ) ;
static const struct acpi_device_id bt_device_ids [ ] = {
{ " TOS6205 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , bt_device_ids ) ;
2012-08-10 01:00:13 +04:00
# ifdef CONFIG_PM_SLEEP
2012-06-28 01:27:48 +04:00
static int toshiba_bt_resume ( struct device * dev ) ;
2012-08-10 01:00:13 +04:00
# endif
2012-06-28 01:27:48 +04:00
static SIMPLE_DEV_PM_OPS ( toshiba_bt_pm , NULL , toshiba_bt_resume ) ;
2009-12-16 20:08:15 +03:00
static struct acpi_driver toshiba_bt_rfkill_driver = {
. name = " Toshiba BT " ,
. class = " Toshiba " ,
. ids = bt_device_ids ,
. ops = {
. add = toshiba_bt_rfkill_add ,
. remove = toshiba_bt_rfkill_remove ,
. notify = toshiba_bt_rfkill_notify ,
} ,
. owner = THIS_MODULE ,
2012-06-28 01:27:48 +04:00
. drv . pm = & toshiba_bt_pm ,
2009-12-16 20:08:15 +03:00
} ;
2015-03-26 23:56:05 +03:00
static int toshiba_bluetooth_present ( acpi_handle handle )
{
acpi_status result ;
u64 bt_present ;
2015-03-26 23:56:06 +03:00
/*
* Some Toshiba laptops may have a fake TOS6205 device in
* their ACPI BIOS , so query the _STA method to see if there
* is really anything there .
*/
2015-03-26 23:56:05 +03:00
result = acpi_evaluate_integer ( handle , " _STA " , NULL , & bt_present ) ;
if ( ACPI_FAILURE ( result ) ) {
pr_err ( " ACPI call to query Bluetooth presence failed " ) ;
return - ENXIO ;
} else if ( ! bt_present ) {
pr_info ( " Bluetooth device not present \n " ) ;
return - ENODEV ;
}
return 0 ;
}
static int toshiba_bluetooth_status ( acpi_handle handle )
{
acpi_status result ;
u64 status ;
result = acpi_evaluate_integer ( handle , " BTST " , NULL , & status ) ;
if ( ACPI_FAILURE ( result ) ) {
pr_err ( " Could not get Bluetooth device status \n " ) ;
return - ENXIO ;
}
pr_info ( " Bluetooth status %llu \n " , status ) ;
return status ;
}
2009-12-16 20:08:15 +03:00
static int toshiba_bluetooth_enable ( acpi_handle handle )
{
acpi_status res1 , res2 ;
2010-01-28 05:53:19 +03:00
u64 result ;
2009-12-16 20:08:15 +03:00
/*
* Query ACPI to verify RFKill switch is set to ' on ' .
* If not , we return silently , no need to report it as
* an error .
*/
res1 = acpi_evaluate_integer ( handle , " BTST " , NULL , & result ) ;
if ( ACPI_FAILURE ( res1 ) )
return res1 ;
if ( ! ( result & 0x01 ) )
return 0 ;
2011-03-30 02:21:52 +04:00
pr_info ( " Re-enabling Toshiba Bluetooth \n " ) ;
2009-12-16 20:08:15 +03:00
res1 = acpi_evaluate_object ( handle , " AUSB " , NULL , NULL ) ;
res2 = acpi_evaluate_object ( handle , " BTPO " , NULL , NULL ) ;
if ( ! ACPI_FAILURE ( res1 ) | | ! ACPI_FAILURE ( res2 ) )
return 0 ;
2011-03-30 02:21:52 +04:00
pr_warn ( " Failed to re-enable Toshiba Bluetooth \n " ) ;
2009-12-16 20:08:15 +03:00
return - ENODEV ;
}
2015-03-26 23:56:05 +03:00
static int toshiba_bluetooth_disable ( acpi_handle handle )
{
acpi_status result ;
result = acpi_evaluate_object ( handle , " BTPF " , NULL , NULL ) ;
if ( ACPI_FAILURE ( result ) ) {
pr_err ( " Could not power OFF Bluetooth device \n " ) ;
return - ENXIO ;
}
result = acpi_evaluate_object ( handle , " DUSB " , NULL , NULL ) ;
if ( ACPI_FAILURE ( result ) ) {
pr_err ( " Could not detach USB Bluetooth device \n " ) ;
return - ENXIO ;
}
return 0 ;
}
2009-12-16 20:08:15 +03:00
static void toshiba_bt_rfkill_notify ( struct acpi_device * device , u32 event )
{
toshiba_bluetooth_enable ( device - > handle ) ;
}
2012-08-10 01:00:13 +04:00
# ifdef CONFIG_PM_SLEEP
2012-06-28 01:27:48 +04:00
static int toshiba_bt_resume ( struct device * dev )
2009-12-16 20:08:15 +03:00
{
2012-06-28 01:27:48 +04:00
return toshiba_bluetooth_enable ( to_acpi_device ( dev ) - > handle ) ;
2009-12-16 20:08:15 +03:00
}
2012-08-10 01:00:13 +04:00
# endif
2009-12-16 20:08:15 +03:00
static int toshiba_bt_rfkill_add ( struct acpi_device * device )
{
2015-03-26 23:56:06 +03:00
int result ;
2009-12-16 20:08:15 +03:00
2015-03-26 23:56:06 +03:00
result = toshiba_bluetooth_present ( device - > handle ) ;
if ( result )
return result ;
2009-12-16 20:08:15 +03:00
2015-03-26 23:56:06 +03:00
pr_info ( " Toshiba ACPI Bluetooth device driver \n " ) ;
/* Enable the BT device */
result = toshiba_bluetooth_enable ( device - > handle ) ;
if ( result )
return result ;
2009-12-16 20:08:15 +03:00
return result ;
}
2013-01-24 03:24:48 +04:00
static int toshiba_bt_rfkill_remove ( struct acpi_device * device )
2009-12-16 20:08:15 +03:00
{
/* clean up */
2015-03-26 23:56:06 +03:00
return toshiba_bluetooth_disable ( device - > handle ) ;
2009-12-16 20:08:15 +03:00
}
2012-09-07 11:31:48 +04:00
module_acpi_driver ( toshiba_bt_rfkill_driver ) ;