linux/drivers/platform/x86/toshiba_bluetooth.c
<<
>>
Prefs
   1/*
   2 * Toshiba Bluetooth Enable Driver
   3 *
   4 * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com>
   5 * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>
   6 *
   7 * Thanks to Matthew Garrett for background info on ACPI innards which
   8 * normal people aren't meant to understand :-)
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 */
  14
  15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  16
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/init.h>
  20#include <linux/types.h>
  21#include <linux/acpi.h>
  22#include <linux/rfkill.h>
  23
  24#define BT_KILLSWITCH_MASK      0x01
  25#define BT_PLUGGED_MASK         0x40
  26#define BT_POWER_MASK           0x80
  27
  28MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");
  29MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");
  30MODULE_LICENSE("GPL");
  31
  32struct toshiba_bluetooth_dev {
  33        struct acpi_device *acpi_dev;
  34        struct rfkill *rfk;
  35
  36        bool killswitch;
  37        bool plugged;
  38        bool powered;
  39};
  40
  41static int toshiba_bt_rfkill_add(struct acpi_device *device);
  42static int toshiba_bt_rfkill_remove(struct acpi_device *device);
  43static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);
  44
  45static const struct acpi_device_id bt_device_ids[] = {
  46        { "TOS6205", 0},
  47        { "", 0},
  48};
  49MODULE_DEVICE_TABLE(acpi, bt_device_ids);
  50
  51#ifdef CONFIG_PM_SLEEP
  52static int toshiba_bt_resume(struct device *dev);
  53#endif
  54static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);
  55
  56static struct acpi_driver toshiba_bt_rfkill_driver = {
  57        .name =         "Toshiba BT",
  58        .class =        "Toshiba",
  59        .ids =          bt_device_ids,
  60        .ops =          {
  61                                .add =          toshiba_bt_rfkill_add,
  62                                .remove =       toshiba_bt_rfkill_remove,
  63                                .notify =       toshiba_bt_rfkill_notify,
  64                        },
  65        .owner =        THIS_MODULE,
  66        .drv.pm =       &toshiba_bt_pm,
  67};
  68
  69static int toshiba_bluetooth_present(acpi_handle handle)
  70{
  71        acpi_status result;
  72        u64 bt_present;
  73
  74        /*
  75         * Some Toshiba laptops may have a fake TOS6205 device in
  76         * their ACPI BIOS, so query the _STA method to see if there
  77         * is really anything there.
  78         */
  79        result = acpi_evaluate_integer(handle, "_STA", NULL, &bt_present);
  80        if (ACPI_FAILURE(result)) {
  81                pr_err("ACPI call to query Bluetooth presence failed\n");
  82                return -ENXIO;
  83        }
  84
  85        if (!bt_present) {
  86                pr_info("Bluetooth device not present\n");
  87                return -ENODEV;
  88        }
  89
  90        return 0;
  91}
  92
  93static int toshiba_bluetooth_status(acpi_handle handle)
  94{
  95        acpi_status result;
  96        u64 status;
  97
  98        result = acpi_evaluate_integer(handle, "BTST", NULL, &status);
  99        if (ACPI_FAILURE(result)) {
 100                pr_err("Could not get Bluetooth device status\n");
 101                return -ENXIO;
 102        }
 103
 104        return status;
 105}
 106
 107static int toshiba_bluetooth_enable(acpi_handle handle)
 108{
 109        acpi_status result;
 110
 111        result = acpi_evaluate_object(handle, "AUSB", NULL, NULL);
 112        if (ACPI_FAILURE(result)) {
 113                pr_err("Could not attach USB Bluetooth device\n");
 114                return -ENXIO;
 115        }
 116
 117        result = acpi_evaluate_object(handle, "BTPO", NULL, NULL);
 118        if (ACPI_FAILURE(result)) {
 119                pr_err("Could not power ON Bluetooth device\n");
 120                return -ENXIO;
 121        }
 122
 123        return 0;
 124}
 125
 126static int toshiba_bluetooth_disable(acpi_handle handle)
 127{
 128        acpi_status result;
 129
 130        result = acpi_evaluate_object(handle, "BTPF", NULL, NULL);
 131        if (ACPI_FAILURE(result)) {
 132                pr_err("Could not power OFF Bluetooth device\n");
 133                return -ENXIO;
 134        }
 135
 136        result = acpi_evaluate_object(handle, "DUSB", NULL, NULL);
 137        if (ACPI_FAILURE(result)) {
 138                pr_err("Could not detach USB Bluetooth device\n");
 139                return -ENXIO;
 140        }
 141
 142        return 0;
 143}
 144
 145/* Helper function */
 146static int toshiba_bluetooth_sync_status(struct toshiba_bluetooth_dev *bt_dev)
 147{
 148        int status;
 149
 150        status = toshiba_bluetooth_status(bt_dev->acpi_dev->handle);
 151        if (status < 0) {
 152                pr_err("Could not sync bluetooth device status\n");
 153                return status;
 154        }
 155
 156        bt_dev->killswitch = (status & BT_KILLSWITCH_MASK) ? true : false;
 157        bt_dev->plugged = (status & BT_PLUGGED_MASK) ? true : false;
 158        bt_dev->powered = (status & BT_POWER_MASK) ? true : false;
 159
 160        pr_debug("Bluetooth status %d killswitch %d plugged %d powered %d\n",
 161                 status, bt_dev->killswitch, bt_dev->plugged, bt_dev->powered);
 162
 163        return 0;
 164}
 165
 166/* RFKill handlers */
 167static int bt_rfkill_set_block(void *data, bool blocked)
 168{
 169        struct toshiba_bluetooth_dev *bt_dev = data;
 170        int ret;
 171
 172        ret = toshiba_bluetooth_sync_status(bt_dev);
 173        if (ret)
 174                return ret;
 175
 176        if (!bt_dev->killswitch)
 177                return 0;
 178
 179        if (blocked)
 180                ret = toshiba_bluetooth_disable(bt_dev->acpi_dev->handle);
 181        else
 182                ret = toshiba_bluetooth_enable(bt_dev->acpi_dev->handle);
 183
 184        return ret;
 185}
 186
 187static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
 188{
 189        struct toshiba_bluetooth_dev *bt_dev = data;
 190
 191        if (toshiba_bluetooth_sync_status(bt_dev))
 192                return;
 193
 194        /*
 195         * Note the Toshiba Bluetooth RFKill switch seems to be a strange
 196         * fish. It only provides a BT event when the switch is flipped to
 197         * the 'on' position. When flipping it to 'off', the USB device is
 198         * simply pulled away underneath us, without any BT event being
 199         * delivered.
 200         */
 201        rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
 202}
 203
 204static const struct rfkill_ops rfk_ops = {
 205        .set_block = bt_rfkill_set_block,
 206        .poll = bt_rfkill_poll,
 207};
 208
 209/* ACPI driver functions */
 210static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
 211{
 212        struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
 213
 214        if (toshiba_bluetooth_sync_status(bt_dev))
 215                return;
 216
 217        rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
 218}
 219
 220#ifdef CONFIG_PM_SLEEP
 221static int toshiba_bt_resume(struct device *dev)
 222{
 223        struct toshiba_bluetooth_dev *bt_dev;
 224        int ret;
 225
 226        bt_dev = acpi_driver_data(to_acpi_device(dev));
 227
 228        ret = toshiba_bluetooth_sync_status(bt_dev);
 229        if (ret)
 230                return ret;
 231
 232        rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
 233
 234        return 0;
 235}
 236#endif
 237
 238static int toshiba_bt_rfkill_add(struct acpi_device *device)
 239{
 240        struct toshiba_bluetooth_dev *bt_dev;
 241        int result;
 242
 243        result = toshiba_bluetooth_present(device->handle);
 244        if (result)
 245                return result;
 246
 247        pr_info("Toshiba ACPI Bluetooth device driver\n");
 248
 249        bt_dev = kzalloc(sizeof(*bt_dev), GFP_KERNEL);
 250        if (!bt_dev)
 251                return -ENOMEM;
 252        bt_dev->acpi_dev = device;
 253        device->driver_data = bt_dev;
 254        dev_set_drvdata(&device->dev, bt_dev);
 255
 256        result = toshiba_bluetooth_sync_status(bt_dev);
 257        if (result) {
 258                kfree(bt_dev);
 259                return result;
 260        }
 261
 262        bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth",
 263                                   &device->dev,
 264                                   RFKILL_TYPE_BLUETOOTH,
 265                                   &rfk_ops,
 266                                   bt_dev);
 267        if (!bt_dev->rfk) {
 268                pr_err("Unable to allocate rfkill device\n");
 269                kfree(bt_dev);
 270                return -ENOMEM;
 271        }
 272
 273        rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
 274
 275        result = rfkill_register(bt_dev->rfk);
 276        if (result) {
 277                pr_err("Unable to register rfkill device\n");
 278                rfkill_destroy(bt_dev->rfk);
 279                kfree(bt_dev);
 280        }
 281
 282        return result;
 283}
 284
 285static int toshiba_bt_rfkill_remove(struct acpi_device *device)
 286{
 287        struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
 288
 289        /* clean up */
 290        if (bt_dev->rfk) {
 291                rfkill_unregister(bt_dev->rfk);
 292                rfkill_destroy(bt_dev->rfk);
 293        }
 294
 295        kfree(bt_dev);
 296
 297        return toshiba_bluetooth_disable(device->handle);
 298}
 299
 300module_acpi_driver(toshiba_bt_rfkill_driver);
 301