From 0e9b14d010fe2fa1eb5e1690e32e101e1adf9dab Mon Sep 17 00:00:00 2001 From: Par-Gunnar Hjalmdahl Date: Mon, 29 Aug 2011 09:29:05 +0530 Subject: cg2900: Fix crash when transport is closed This patch fixes crashes caused by transport being removed while users of the CG2900 driver still exist. ST-Ericsson Linux next: Not tested, ER 336652 ST-Ericsson ID: 336652 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I6318ce1086097a4fa63a1793b6795b01ea939715 Signed-off-by: Par-Gunnar Hjalmdahl Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21781 Reviewed-by: QATEST Reviewed-by: Lukasz RYMANOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29795 Reviewed-by: Virupax SADASHIVPETIMATH Tested-by: Virupax SADASHIVPETIMATH --- drivers/staging/cg2900/bluetooth/btcg2900.c | 1 + drivers/staging/cg2900/bluetooth/cg2900_uart.c | 37 ++++++++++++++++++++---- drivers/staging/cg2900/mfd/cg2900_audio.c | 29 ++++++++++++++++--- drivers/staging/cg2900/mfd/cg2900_char_devices.c | 21 +++++++++++--- 4 files changed, 74 insertions(+), 14 deletions(-) diff --git a/drivers/staging/cg2900/bluetooth/btcg2900.c b/drivers/staging/cg2900/bluetooth/btcg2900.c index 0eb39523c1e..e1895c9ff70 100644 --- a/drivers/staging/cg2900/bluetooth/btcg2900.c +++ b/drivers/staging/cg2900/bluetooth/btcg2900.c @@ -1061,6 +1061,7 @@ static int remove_common(struct platform_device *pdev, goto finished; BT_INFO("Unregistering CG2900"); + info->hdev->driver_data = NULL; err = hci_unregister_dev(info->hdev); if (err) BT_ERR("Can not unregister HCI device (%d)", err); diff --git a/drivers/staging/cg2900/bluetooth/cg2900_uart.c b/drivers/staging/cg2900/bluetooth/cg2900_uart.c index 4417525306a..d7a1fb1044f 100644 --- a/drivers/staging/cg2900/bluetooth/cg2900_uart.c +++ b/drivers/staging/cg2900/bluetooth/cg2900_uart.c @@ -333,9 +333,10 @@ static void wake_up_chip(struct uart_info *uart_info); */ static bool is_chip_flow_off(struct uart_info *uart_info) { - int lines; + int lines = 0; - lines = hci_uart_tiocmget(uart_info->hu); + if (uart_info->hu) + lines = hci_uart_tiocmget(uart_info->hu); if (lines & TIOCM_CTS) return false; @@ -577,6 +578,11 @@ static void wake_up_chip(struct uart_info *uart_info) if (!timeout_jiffies && uart_info->sleep_state != CHIP_RESUMING) return; + if (!uart_info->hu) { + dev_err(MAIN_DEV, "wake_up_chip: UART not open\n"); + return; + } + mutex_lock(&(uart_info->sleep_state_lock)); /* @@ -602,7 +608,7 @@ static void wake_up_chip(struct uart_info *uart_info) /* Disable IRQ only when it was enabled. */ unset_cts_irq(uart_info); (void)hci_uart_set_baudrate(uart_info->hu, - uart_info->baud_rate); + uart_info->baud_rate); enable_uart_pins(uart_info); @@ -650,6 +656,11 @@ static void set_chip_sleep_mode(struct work_struct *work) if (!timeout_jiffies) return; + if (!uart_info->hu) { + dev_err(MAIN_DEV, "set_chip_sleep_mode: UART not open\n"); + return; + } + if (uart_info->tx_in_progress || uart_info->rx_in_progress) { dev_dbg(MAIN_DEV, "Not going to sleep, TX/RX in progress\n"); return; @@ -674,13 +685,12 @@ static void set_chip_sleep_mode(struct work_struct *work) * Set baud zero. * This cause shut off UART clock as well. */ - (void)hci_uart_set_baudrate(uart_info->hu, - ZERO_BAUD_RATE); + (void)hci_uart_set_baudrate(uart_info->hu, ZERO_BAUD_RATE); err = set_cts_irq(uart_info); if (err < 0) { enable_uart_pins(uart_info); (void)hci_uart_set_baudrate(uart_info->hu, - uart_info->baud_rate); + uart_info->baud_rate); hci_uart_flow_ctrl(uart_info->hu, FLOW_ON); hci_uart_set_break(uart_info->hu, BREAK_OFF); @@ -1060,6 +1070,11 @@ static void work_do_transmit(struct work_struct *work) kfree(current_work); + if (!uart_info->hu) { + dev_err(MAIN_DEV, "work_do_transmit: UART not open\n"); + return; + } + spin_lock_bh(&(uart_info->transmission_lock)); /* Mark that there is an ongoing transfer. */ uart_info->tx_in_progress = true; @@ -1129,6 +1144,11 @@ static int set_baud_rate(struct hci_uart *hu, int baud) return -EALREADY; } + if (!uart_info->hu) { + dev_err(MAIN_DEV, "set_baud_rate: UART not open\n"); + return -EFAULT; + } + /* * Wait some time to be sure that any RX process has finished (which * flows on RTS in the end) before flowing off the RTS. @@ -1244,6 +1264,11 @@ static int uart_open(struct cg2900_chip_dev *dev) struct hci_command_hdr *cmd; struct uart_info *uart_info = dev_get_drvdata(dev->dev); + if (!uart_info->hu) { + dev_err(MAIN_DEV, "uart_open: UART not open\n"); + return -EACCES; + } + /* * Chip has just been started up. It has a system to autodetect * exact baud rate and transport to use. There are only a few commands diff --git a/drivers/staging/cg2900/mfd/cg2900_audio.c b/drivers/staging/cg2900/mfd/cg2900_audio.c index 6eadd96b2de..a7b8eda4305 100644 --- a/drivers/staging/cg2900/mfd/cg2900_audio.c +++ b/drivers/staging/cg2900/mfd/cg2900_audio.c @@ -111,6 +111,7 @@ struct endpoint_config_node { * audio channel. * @dev_fm: Device registered by this driver for the FM * audio channel. + * @filp: Current char device file pointer. * @management_mutex: Mutex for handling access to CG2900 Audio driver * management. * @bt_mutex: Mutex for handling access to BT audio channel. @@ -136,6 +137,7 @@ struct audio_info { struct device *parent; struct device *dev_bt; struct device *dev_fm; + struct file *filp; struct mutex management_mutex; struct mutex bt_mutex; struct mutex fm_mutex; @@ -2825,6 +2827,7 @@ static int audio_dev_open(struct inode *inode, struct file *filp) } filp->private_data = char_dev_info; char_dev_info->info = info; + info->filp = filp; mutex_init(&char_dev_info->management_mutex); mutex_init(&char_dev_info->rw_mutex); @@ -2861,7 +2864,14 @@ static int audio_dev_release(struct inode *inode, struct file *filp) { int err = 0; struct char_dev_info *dev = filp->private_data; - struct audio_info *info = dev->info; + struct audio_info *info; + + if (!dev) { + pr_err("audio_dev_release: Transport closed"); + return -EBADF; + } + + info = dev->info; dev_dbg(BT_DEV, "audio_dev_release\n"); @@ -2879,6 +2889,7 @@ static int audio_dev_release(struct inode *inode, struct file *filp) kfree(dev); filp->private_data = NULL; + info->filp = NULL; return err; } @@ -2907,11 +2918,18 @@ static ssize_t audio_dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct char_dev_info *dev = filp->private_data; - struct audio_info *info = dev->info; + struct audio_info *info; unsigned int bytes_to_copy; int err = 0; struct sk_buff *skb; + if (!dev) { + pr_err("audio_dev_read: Transport closed"); + return -EBADF; + } + + info = dev->info; + dev_dbg(BT_DEV, "audio_dev_read count %d\n", count); mutex_lock(&dev->rw_mutex); @@ -2988,7 +3006,7 @@ static ssize_t audio_dev_write(struct file *filp, const char __user *buf, pr_debug("audio_dev_write count %d", count); if (!dev) { - pr_err("No dev supplied in private data"); + pr_err("audio_dev_write: Transport closed"); return -EBADF; } info = dev->info; @@ -3170,7 +3188,7 @@ static unsigned int audio_dev_poll(struct file *filp, poll_table *wait) unsigned int mask = 0; if (!dev) { - pr_err("No dev supplied in private data"); + pr_err("audio_dev_poll: Transport closed"); return POLLERR | POLLRDHUP; } info = dev->info; @@ -3344,6 +3362,9 @@ static int common_remove(struct audio_info *info, struct device *dev) dev_err(dev, "Error %d deregistering misc dev\n", err); info->misc_registered = false; + if (info->filp) + info->filp->private_data = NULL; + dev_info(dev, "CG2900 Audio driver removed\n"); return err; } diff --git a/drivers/staging/cg2900/mfd/cg2900_char_devices.c b/drivers/staging/cg2900/mfd/cg2900_char_devices.c index 0f1627e82ac..10f7d38d4a0 100644 --- a/drivers/staging/cg2900/mfd/cg2900_char_devices.c +++ b/drivers/staging/cg2900/mfd/cg2900_char_devices.c @@ -37,6 +37,7 @@ * struct char_dev_user - Stores device information. * @dev: Current device. * @miscdev: Registered device struct. + * @filp: Current file pointer. * @name: Name of device. * @rx_queue: Data queue. * @rx_wait_queue: Wait queue. @@ -48,6 +49,7 @@ struct char_dev_user { struct device *dev; struct miscdevice miscdev; + struct file *filp; char *name; struct sk_buff_head rx_queue; wait_queue_head_t rx_wait_queue; @@ -147,6 +149,7 @@ static int char_dev_open(struct inode *inode, struct file *filp) } filp->private_data = dev; + dev->filp = filp; user = dev_get_platdata(dev->dev); /* First initiate wait queues for this device. */ @@ -188,7 +191,7 @@ static int char_dev_release(struct inode *inode, struct file *filp) pr_debug("char_dev_release"); if (!dev) { - pr_err("Calling with NULL pointer"); + pr_err("char_dev_release: Calling with NULL pointer"); return -EBADF; } @@ -203,6 +206,7 @@ static int char_dev_release(struct inode *inode, struct file *filp) dev_info(MAIN_DEV, "char_dev %s closed\n", dev->name); filp->private_data = NULL; + dev->filp = NULL; wake_up_interruptible(&dev->rx_wait_queue); wake_up_interruptible(&dev->reset_wait_queue); @@ -241,7 +245,7 @@ static ssize_t char_dev_read(struct file *filp, char __user *buf, size_t count, pr_debug("char_dev_read"); if (!dev) { - pr_err("Calling with NULL pointer"); + pr_err("char_dev_read: Calling with NULL pointer"); return -EBADF; } mutex_lock(&dev->read_mutex); @@ -322,7 +326,7 @@ static ssize_t char_dev_write(struct file *filp, const char __user *buf, pr_debug("char_dev_write"); if (!dev) { - pr_err("Calling with NULL pointer"); + pr_err("char_dev_write: Calling with NULL pointer"); return -EBADF; } @@ -386,6 +390,11 @@ static long char_dev_unlocked_ioctl(struct file *filp, unsigned int cmd, int ret_val; void __user *user_arg = (void __user *)arg; + if (!dev) { + pr_err("char_dev_unlocked_ioctl: Calling with NULL pointer"); + return -EBADF; + } + dev_dbg(dev->dev, "char_dev_unlocked_ioctl for %s\n" "\tDIR: %d\n" "\tTYPE: %d\n" @@ -464,7 +473,7 @@ static unsigned int char_dev_poll(struct file *filp, poll_table *wait) unsigned int mask = 0; if (!dev) { - pr_debug("Device not open"); + pr_debug("char_dev_poll: Device not open"); return POLLERR | POLLRDHUP; } @@ -526,6 +535,10 @@ static void remove_dev(struct char_dev_user *dev_usr) mutex_destroy(&dev_usr->read_mutex); mutex_destroy(&dev_usr->write_mutex); + dev_usr->dev = NULL; + if (dev_usr->filp) + dev_usr->filp->private_data = NULL; + /* Remove device node in file system. */ misc_deregister(&dev_usr->miscdev); kfree(dev_usr); -- cgit v1.2.3