Skip to content

Commit

Permalink
Bluetooth: hci_bcm: Handle errors properly
Browse files Browse the repository at this point in the history
A significant portion of this driver lacks error handling.  As a first
step, add error paths to bcm_gpio_set_power(), bcm_open(), bcm_close(),
bcm_suspend_device(), bcm_resume_device(), bcm_resume(), bcm_probe() and
bcm_serdev_probe().  (I've also scrutinized bcm_suspend() but think it's
fine as is.)

Those are all the functions accessing the device wake and shutdown GPIO.
On Apple Macs the pins are accessed through ACPI methods, which may fail
for various reasons, hence proper error handling is necessary.  Non-Macs
access the pins directly, which may fail as well but the GPIO core does
not yet pass back errors to consumers.

Cc: Frédéric Danis <[email protected]>
Cc: Hans de Goede <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Lukas Wunner <[email protected]>
Signed-off-by: Marcel Holtmann <[email protected]>
  • Loading branch information
l1k authored and holtmann committed Jan 10, 2018
1 parent 8353b4a commit 8bfa7e1
Showing 1 changed file with 75 additions and 16 deletions.
91 changes: 75 additions & 16 deletions drivers/bluetooth/hci_bcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,18 +197,35 @@ static bool bcm_device_exists(struct bcm_device *device)

static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
{
if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled)
clk_prepare_enable(dev->clk);
int err;

dev->set_shutdown(dev, powered);
dev->set_device_wakeup(dev, powered);
if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled) {
err = clk_prepare_enable(dev->clk);
if (err)
return err;
}

err = dev->set_shutdown(dev, powered);
if (err)
goto err_clk_disable;

err = dev->set_device_wakeup(dev, powered);
if (err)
goto err_revert_shutdown;

if (!powered && !IS_ERR(dev->clk) && dev->clk_enabled)
clk_disable_unprepare(dev->clk);

dev->clk_enabled = powered;

return 0;

err_revert_shutdown:
dev->set_shutdown(dev, !powered);
err_clk_disable:
if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled)
clk_disable_unprepare(dev->clk);
return err;
}

#ifdef CONFIG_PM
Expand Down Expand Up @@ -331,6 +348,7 @@ static int bcm_open(struct hci_uart *hu)
{
struct bcm_data *bcm;
struct list_head *p;
int err;

bt_dev_dbg(hu->hdev, "hu %p", hu);

Expand All @@ -345,7 +363,10 @@ static int bcm_open(struct hci_uart *hu)
mutex_lock(&bcm_device_lock);

if (hu->serdev) {
serdev_device_open(hu->serdev);
err = serdev_device_open(hu->serdev);
if (err)
goto err_free;

bcm->dev = serdev_device_get_drvdata(hu->serdev);
goto out;
}
Expand Down Expand Up @@ -373,17 +394,30 @@ static int bcm_open(struct hci_uart *hu)
if (bcm->dev) {
hu->init_speed = bcm->dev->init_speed;
hu->oper_speed = bcm->dev->oper_speed;
bcm_gpio_set_power(bcm->dev, true);
err = bcm_gpio_set_power(bcm->dev, true);
if (err)
goto err_unset_hu;
}

mutex_unlock(&bcm_device_lock);
return 0;

err_unset_hu:
#ifdef CONFIG_PM
bcm->dev->hu = NULL;
#endif
err_free:
mutex_unlock(&bcm_device_lock);
hu->priv = NULL;
kfree(bcm);
return err;
}

static int bcm_close(struct hci_uart *hu)
{
struct bcm_data *bcm = hu->priv;
struct bcm_device *bdev = NULL;
int err;

bt_dev_dbg(hu->hdev, "hu %p", hu);

Expand All @@ -407,8 +441,11 @@ static int bcm_close(struct hci_uart *hu)
pm_runtime_disable(bdev->dev);
}

bcm_gpio_set_power(bdev, false);
pm_runtime_set_suspended(bdev->dev);
err = bcm_gpio_set_power(bdev, false);
if (err)
bt_dev_err(hu->hdev, "Failed to power down");
else
pm_runtime_set_suspended(bdev->dev);
}
mutex_unlock(&bcm_device_lock);

Expand Down Expand Up @@ -588,6 +625,7 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
static int bcm_suspend_device(struct device *dev)
{
struct bcm_device *bdev = dev_get_drvdata(dev);
int err;

bt_dev_dbg(bdev, "");

Expand All @@ -599,7 +637,15 @@ static int bcm_suspend_device(struct device *dev)
}

/* Suspend the device */
bdev->set_device_wakeup(bdev, false);
err = bdev->set_device_wakeup(bdev, false);
if (err) {
if (bdev->is_suspended && bdev->hu) {
bdev->is_suspended = false;
hci_uart_set_flow_control(bdev->hu, false);
}
return -EBUSY;
}

bt_dev_dbg(bdev, "suspend, delaying 15 ms");
mdelay(15);

Expand All @@ -609,10 +655,16 @@ static int bcm_suspend_device(struct device *dev)
static int bcm_resume_device(struct device *dev)
{
struct bcm_device *bdev = dev_get_drvdata(dev);
int err;

bt_dev_dbg(bdev, "");

bdev->set_device_wakeup(bdev, true);
err = bdev->set_device_wakeup(bdev, true);
if (err) {
dev_err(dev, "Failed to power up\n");
return err;
}

bt_dev_dbg(bdev, "resume, delaying 15 ms");
mdelay(15);

Expand Down Expand Up @@ -666,6 +718,7 @@ static int bcm_suspend(struct device *dev)
static int bcm_resume(struct device *dev)
{
struct bcm_device *bdev = dev_get_drvdata(dev);
int err = 0;

bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended);

Expand All @@ -685,14 +738,16 @@ static int bcm_resume(struct device *dev)
bt_dev_dbg(bdev, "BCM irq: disabled");
}

bcm_resume_device(dev);
err = bcm_resume_device(dev);

unlock:
mutex_unlock(&bcm_device_lock);

pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
if (!err) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}

return 0;
}
Expand Down Expand Up @@ -923,7 +978,9 @@ static int bcm_probe(struct platform_device *pdev)
list_add_tail(&dev->list, &bcm_device_list);
mutex_unlock(&bcm_device_lock);

bcm_gpio_set_power(dev, false);
ret = bcm_gpio_set_power(dev, false);
if (ret)
dev_err(&pdev->dev, "Failed to power down\n");

return 0;
}
Expand Down Expand Up @@ -1025,7 +1082,9 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
if (err)
return err;

bcm_gpio_set_power(bcmdev, false);
err = bcm_gpio_set_power(bcmdev, false);
if (err)
dev_err(&serdev->dev, "Failed to power down\n");

return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto);
}
Expand Down

0 comments on commit 8bfa7e1

Please sign in to comment.