2009-12-16 20:08:15 +03:00
/*
* Toshiba Bluetooth Enable Driver
*
* Copyright ( C ) 2009 Jes Sorensen < Jes . Sorensen @ gmail . com >
2015-03-26 23:56:07 +03:00
* Copyright ( C ) 2015 Azael Avalos < coproscefalo @ gmail . com >
2009-12-16 20:08:15 +03:00
*
* 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 .
*/
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>
2015-05-04 02:42:07 +03:00
# include <linux/rfkill.h>
2009-12-16 20:08:15 +03:00
2015-03-26 23:56:07 +03:00
# define BT_KILLSWITCH_MASK 0x01
# define BT_PLUGGED_MASK 0x40
# define BT_POWER_MASK 0x80
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 " ) ;
2015-05-04 02:42:06 +03:00
struct toshiba_bluetooth_dev {
struct acpi_device * acpi_dev ;
2015-05-04 02:42:07 +03:00
struct rfkill * rfk ;
2015-05-04 02:42:06 +03:00
bool killswitch ;
bool plugged ;
bool powered ;
} ;
2009-12-16 20:08:15 +03:00
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 ) ) {
2015-11-16 06:33:47 +03:00
pr_err ( " ACPI call to query Bluetooth presence failed \n " ) ;
2015-03-26 23:56:05 +03:00
return - ENXIO ;
2016-09-07 18:28:15 +03:00
}
if ( ! bt_present ) {
2015-03-26 23:56:05 +03:00
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 ;
}
return status ;
}
2009-12-16 20:08:15 +03:00
static int toshiba_bluetooth_enable ( acpi_handle handle )
{
2015-03-26 23:56:07 +03:00
acpi_status result ;
2009-12-16 20:08:15 +03:00
2015-03-26 23:56:07 +03:00
result = acpi_evaluate_object ( handle , " AUSB " , NULL , NULL ) ;
if ( ACPI_FAILURE ( result ) ) {
pr_err ( " Could not attach USB Bluetooth device \n " ) ;
return - ENXIO ;
}
2009-12-16 20:08:15 +03:00
2015-03-26 23:56:07 +03:00
result = acpi_evaluate_object ( handle , " BTPO " , NULL , NULL ) ;
if ( ACPI_FAILURE ( result ) ) {
pr_err ( " Could not power ON Bluetooth device \n " ) ;
return - ENXIO ;
}
return 0 ;
2009-12-16 20:08:15 +03:00
}
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 ;
}
2015-05-04 02:42:06 +03:00
/* Helper function */
static int toshiba_bluetooth_sync_status ( struct toshiba_bluetooth_dev * bt_dev )
{
int status ;
status = toshiba_bluetooth_status ( bt_dev - > acpi_dev - > handle ) ;
if ( status < 0 ) {
pr_err ( " Could not sync bluetooth device status \n " ) ;
return status ;
}
bt_dev - > killswitch = ( status & BT_KILLSWITCH_MASK ) ? true : false ;
bt_dev - > plugged = ( status & BT_PLUGGED_MASK ) ? true : false ;
bt_dev - > powered = ( status & BT_POWER_MASK ) ? true : false ;
2015-05-04 02:42:09 +03:00
pr_debug ( " Bluetooth status %d killswitch %d plugged %d powered %d \n " ,
status , bt_dev - > killswitch , bt_dev - > plugged , bt_dev - > powered ) ;
2015-05-04 02:42:06 +03:00
return 0 ;
}
2015-05-04 02:42:07 +03:00
/* RFKill handlers */
static int bt_rfkill_set_block ( void * data , bool blocked )
{
struct toshiba_bluetooth_dev * bt_dev = data ;
int ret ;
ret = toshiba_bluetooth_sync_status ( bt_dev ) ;
if ( ret )
return ret ;
if ( ! bt_dev - > killswitch )
return 0 ;
if ( blocked )
ret = toshiba_bluetooth_disable ( bt_dev - > acpi_dev - > handle ) ;
else
ret = toshiba_bluetooth_enable ( bt_dev - > acpi_dev - > handle ) ;
return ret ;
}
static void bt_rfkill_poll ( struct rfkill * rfkill , void * data )
{
struct toshiba_bluetooth_dev * bt_dev = data ;
if ( toshiba_bluetooth_sync_status ( bt_dev ) )
return ;
/*
* 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 .
*/
rfkill_set_hw_state ( bt_dev - > rfk , ! bt_dev - > killswitch ) ;
}
static const struct rfkill_ops rfk_ops = {
. set_block = bt_rfkill_set_block ,
. poll = bt_rfkill_poll ,
} ;
2015-05-04 02:42:06 +03:00
/* ACPI driver functions */
2009-12-16 20:08:15 +03:00
static void toshiba_bt_rfkill_notify ( struct acpi_device * device , u32 event )
{
2015-05-04 02:42:08 +03:00
struct toshiba_bluetooth_dev * bt_dev = acpi_driver_data ( device ) ;
if ( toshiba_bluetooth_sync_status ( bt_dev ) )
return ;
rfkill_set_hw_state ( bt_dev - > rfk , ! bt_dev - > killswitch ) ;
2009-12-16 20:08:15 +03:00
}
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
{
2015-05-04 02:42:08 +03:00
struct toshiba_bluetooth_dev * bt_dev ;
int ret ;
bt_dev = acpi_driver_data ( to_acpi_device ( dev ) ) ;
ret = toshiba_bluetooth_sync_status ( bt_dev ) ;
if ( ret )
return ret ;
rfkill_set_hw_state ( bt_dev - > rfk , ! bt_dev - > killswitch ) ;
return 0 ;
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-05-04 02:42:06 +03:00
struct toshiba_bluetooth_dev * bt_dev ;
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 " ) ;
2015-05-04 02:42:06 +03:00
bt_dev = kzalloc ( sizeof ( * bt_dev ) , GFP_KERNEL ) ;
if ( ! bt_dev )
return - ENOMEM ;
bt_dev - > acpi_dev = device ;
device - > driver_data = bt_dev ;
dev_set_drvdata ( & device - > dev , bt_dev ) ;
result = toshiba_bluetooth_sync_status ( bt_dev ) ;
if ( result ) {
kfree ( bt_dev ) ;
return result ;
}
2015-05-04 02:42:07 +03:00
bt_dev - > rfk = rfkill_alloc ( " Toshiba Bluetooth " ,
& device - > dev ,
RFKILL_TYPE_BLUETOOTH ,
& rfk_ops ,
bt_dev ) ;
if ( ! bt_dev - > rfk ) {
pr_err ( " Unable to allocate rfkill device \n " ) ;
kfree ( bt_dev ) ;
return - ENOMEM ;
}
rfkill_set_hw_state ( bt_dev - > rfk , ! bt_dev - > killswitch ) ;
result = rfkill_register ( bt_dev - > rfk ) ;
if ( result ) {
pr_err ( " Unable to register rfkill device \n " ) ;
rfkill_destroy ( bt_dev - > rfk ) ;
2015-05-04 02:42:06 +03:00
kfree ( bt_dev ) ;
2015-05-04 02:42:07 +03:00
}
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
{
2015-05-04 02:42:06 +03:00
struct toshiba_bluetooth_dev * bt_dev = acpi_driver_data ( device ) ;
2009-12-16 20:08:15 +03:00
/* clean up */
2015-05-04 02:42:07 +03:00
if ( bt_dev - > rfk ) {
rfkill_unregister ( bt_dev - > rfk ) ;
rfkill_destroy ( bt_dev - > rfk ) ;
}
2015-05-04 02:42:06 +03:00
kfree ( bt_dev ) ;
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 ) ;