blob: 718df024f237c9f6cc6e87b15b5d6573a81fe8dd [file] [log] [blame]
/* Copyright (c) 2009-2010, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Bluetooth Power Switch Module
* controls power to external Bluetooth device
* with interface to power management device
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
static bool previous;
static int bluetooth_toggle_radio(void *data, bool blocked)
{
int ret = 0;
int (*power_control)(int enable);
power_control = data;
if (previous != blocked)
ret = (*power_control)(!blocked);
if (!ret)
previous = blocked;
return ret;
}
static const struct rfkill_ops bluetooth_power_rfkill_ops = {
.set_block = bluetooth_toggle_radio,
};
static int bluetooth_power_rfkill_probe(struct platform_device *pdev)
{
struct rfkill *rfkill;
int ret;
rfkill = rfkill_alloc("bt_power", &pdev->dev, RFKILL_TYPE_BLUETOOTH,
&bluetooth_power_rfkill_ops,
pdev->dev.platform_data);
if (!rfkill) {
dev_err(&pdev->dev, "rfkill allocate failed\n");
return -ENOMEM;
}
/* force Bluetooth off during init to allow for user control */
rfkill_init_sw_state(rfkill, 1);
previous = 1;
ret = rfkill_register(rfkill);
if (ret) {
dev_err(&pdev->dev, "rfkill register failed=%d\n", ret);
rfkill_destroy(rfkill);
return ret;
}
platform_set_drvdata(pdev, rfkill);
return 0;
}
static void bluetooth_power_rfkill_remove(struct platform_device *pdev)
{
struct rfkill *rfkill;
dev_dbg(&pdev->dev, "%s\n", __func__);
rfkill = platform_get_drvdata(pdev);
if (rfkill)
rfkill_unregister(rfkill);
rfkill_destroy(rfkill);
platform_set_drvdata(pdev, NULL);
}
static int __devinit bt_power_probe(struct platform_device *pdev)
{
int ret = 0;
dev_dbg(&pdev->dev, "%s\n", __func__);
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "platform data not initialized\n");
return -ENOSYS;
}
ret = bluetooth_power_rfkill_probe(pdev);
return ret;
}
static int __devexit bt_power_remove(struct platform_device *pdev)
{
dev_dbg(&pdev->dev, "%s\n", __func__);
bluetooth_power_rfkill_remove(pdev);
return 0;
}
static struct platform_driver bt_power_driver = {
.probe = bt_power_probe,
.remove = __devexit_p(bt_power_remove),
.driver = {
.name = "bt_power",
.owner = THIS_MODULE,
},
};
static int __init bluetooth_power_init(void)
{
int ret;
ret = platform_driver_register(&bt_power_driver);
return ret;
}
static void __exit bluetooth_power_exit(void)
{
platform_driver_unregister(&bt_power_driver);
}
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MSM Bluetooth power control driver");
MODULE_VERSION("1.40");
module_init(bluetooth_power_init);
module_exit(bluetooth_power_exit);