misc: gps_brcm4750: Adding wake up timer support for Brcm gps v17 library
authorprabhu annabathula <prabhu.annabathula@motorola.com>
Thu, 27 Jan 2011 02:56:13 +0000 (20:56 -0600)
committerMike Lockwood <lockwood@android.com>
Wed, 9 Feb 2011 15:37:14 +0000 (10:37 -0500)
broadcom gps library will schedule the wake up timer in two cases
 - when a fix interval longer than 5 seconds is requested
 - when integrating long for very weak gps signals
In these two cases brcm library will release the wakelock and schedule
timer to wake up if the system goes in to deep sleep.

Signed-off-by: prabhu annabathula <prabhu.annabathula@motorola.com>
Signed-off-by: Mike Lockwood <lockwood@android.com>
drivers/misc/gps-gpio-brcm4750.c
include/linux/gps-gpio-brcm4750.h

index 0706e1cc73ac72cc32b12528de33c2127720a97c..ad05d709cf608b57b2288e786a54cba5e66eb1b7 100755 (executable)
 #include <linux/gps-gpio-brcm4750.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
+#include <linux/wait.h>
 
 struct gps_gpio_brcm4750_platform_data *gps_gpio_data;
+/* Wakeup timer state definition */
+
+enum timer_state {
+       /* Timer is inactive */
+       TIMER_INACTIVE = 0,
+       /* Timer is active, waitting for timeout */
+       TIMER_ACTIVE,
+       /* Timer has timeout, has wokenup process, waiting for next poll */
+       TIMER_EXPIRED,
+       /* Timer is unused. */
+       TIMER_INVALID
+};
+
+static int gps_start_wakeup_timer( struct file *filp, unsigned long timer_val_msecs)
+{
+       ktime_t low_interval = ktime_set(timer_val_msecs/MSEC_PER_SEC,
+               (timer_val_msecs%MSEC_PER_SEC)*NSEC_PER_MSEC);
+       /* set the alarm expiry window to 500 msecs */
+       ktime_t slack = ktime_set(0, 500*NSEC_PER_MSEC);
+       ktime_t next = ktime_add(alarm_get_elapsed_realtime(), low_interval);
+       /* set filp structure if it is first time timer is scheduled */
+       if (filp->private_data == NULL)
+       {
+               filp->private_data = (void *)gps_gpio_data;
+       }
+       alarm_cancel(&gps_gpio_data->alarm);
+       gps_gpio_data->timer_status = TIMER_ACTIVE;
+       alarm_start_range(&gps_gpio_data->alarm, next, ktime_add(next, slack));
+       return 0;
+}
+
+static int gps_stop_wakeup_timer( struct file *filp)
+{
+       if (filp->private_data == NULL)
+               return 0;
+
+       alarm_cancel(&gps_gpio_data->alarm);
+       gps_gpio_data->timer_status = TIMER_INACTIVE;
+       return 0;
+}
+
+static void gps_brcm4750_alarm(struct alarm* alarm)
+{
+
+       struct gps_gpio_brcm4750_platform_data *gps_gpio_data =
+                                container_of(alarm, struct gps_gpio_brcm4750_platform_data, alarm);
+       gps_gpio_data->timer_status = TIMER_EXPIRED;
 
+       wake_lock_timeout(&gps_gpio_data->gps_brcm4750_wake, 5* HZ);
+       /* trigger poll wait */
+       wake_up_interruptible(&(gps_gpio_data->gps_brcm4750_wq));
+}
+
+
+static unsigned int gps_brcm_4750_poll(struct file *filp, poll_table *wait)
+{
+       unsigned int ret = 0;
+       struct gps_gpio_brcm4750_platform_data *gps_gpio_data;
+       /* If the timer is not present, do not permit this operation */
+       if (filp->private_data == NULL)
+       {
+               return -EPERM;
+       }
+
+       gps_gpio_data = (struct gps_gpio_brcm4750_platform_data *)filp->private_data;
+       if (gps_gpio_data->timer_status == TIMER_INVALID ||
+               gps_gpio_data->timer_status == TIMER_INACTIVE)
+       {
+               return -EPERM;
+       }
+
+       /* Check whether the timer has already expired */
+       if (gps_gpio_data->timer_status == TIMER_EXPIRED) {
+               gps_gpio_data->timer_status = TIMER_INACTIVE;
+               return POLLIN;
+       }
+       /* release wake lock before poll wait */
+
+       wake_unlock(&gps_gpio_data->gps_brcm4750_wake);
+       poll_wait(filp, &(gps_gpio_data->gps_brcm4750_wq), wait);
+
+       if (gps_gpio_data->timer_status == TIMER_EXPIRED) {
+               gps_gpio_data->timer_status = TIMER_INACTIVE;
+               ret = POLLIN;
+       }
+       return ret;
+}
 static long gps_brcm4750_ioctl(struct file *filp,
                               unsigned int cmd, unsigned long arg)
 {
@@ -38,26 +128,35 @@ static long gps_brcm4750_ioctl(struct file *filp,
        if (cmd <= 0)
                return -EINVAL;
 
-       if (copy_from_user((void *) &gpio_val, (void *) arg,
-                               sizeof(int)))
-               return -EFAULT;
-
-       if (!(gpio_val == 0 || gpio_val == 1))
-               return -EINVAL;
-
        switch (cmd) {
        case IOC_GPS_GPIO_RESET:
+               if (copy_from_user((void *) &gpio_val, (void *) arg,
+                               sizeof(int)))
+                       return -EFAULT;
+               if (!(gpio_val == 0 || gpio_val == 1))
+                       return -EINVAL;
                pr_info("%s: Setting gps gpio reset pin: %d\n",
-                __func__, gpio_val);
+                       __func__, gpio_val);
                if (gps_gpio_data->set_reset_gpio)
                        gps_gpio_data->set_reset_gpio(gpio_val);
                break;
        case IOC_GPS_GPIO_STANDBY:
+               if (copy_from_user((void *) &gpio_val, (void *) arg,
+                               sizeof(int)))
+                       return -EFAULT;
+               if (!(gpio_val == 0 || gpio_val == 1))
+                       return -EINVAL;
                pr_info("%s: Setting gps gpio standby pin to: %d\n",
                        __func__, gpio_val);
                if (gps_gpio_data->set_standby_gpio)
                        gps_gpio_data->set_standby_gpio(gpio_val);
                break;
+       case IOC_GPS_START_TIMER:
+               gps_start_wakeup_timer(filp, (unsigned long)arg);
+               break;
+       case IOC_GPS_STOP_TIMER:
+               gps_stop_wakeup_timer(filp);
+        break;
        default:
                pr_info("%s: Invalid GPS GPIO IOCTL command\n", __func__);
                return -EINVAL;
@@ -67,8 +166,9 @@ static long gps_brcm4750_ioctl(struct file *filp,
 }
 
 static const struct file_operations gps_brcm4750_fops = {
-       .owner          = THIS_MODULE,
-       .unlocked_ioctl         = gps_brcm4750_ioctl,
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = gps_brcm4750_ioctl,
+       .poll = gps_brcm_4750_poll,
 };
 
 static struct miscdevice gps_gpio_miscdev = {
@@ -80,6 +180,12 @@ static struct miscdevice gps_gpio_miscdev = {
 static int gps_gpio_brcm4750_probe(struct platform_device *pdev)
 {
        gps_gpio_data = pdev->dev.platform_data;
+       wake_lock_init(&gps_gpio_data->gps_brcm4750_wake, WAKE_LOCK_SUSPEND,
+                       "gps-brcm4750");
+       alarm_init(&gps_gpio_data->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+                       gps_brcm4750_alarm);
+       init_waitqueue_head(&(gps_gpio_data->gps_brcm4750_wq));
+       gps_gpio_data->timer_status = TIMER_INVALID;
        if (misc_register(&gps_gpio_miscdev)) {
                pr_info("%s: gps_brcm4750 misc_register failed\n", __func__);
                return -1;
@@ -117,5 +223,5 @@ module_init(gps_gpio_brcm4750_init);
 module_exit(gps_gpio_brcm4750_exit);
 
 MODULE_AUTHOR("Motorola");
-MODULE_DESCRIPTION("GPS GPIO Controller for BRCM 4750");
+MODULE_DESCRIPTION("GPS GPIO Controller and wake up timer for BRCM 4750");
 MODULE_LICENSE("GPL");
index d534ab7afe125c3648cd6ffd745bff23b30c91e2..8b30cd7622ebb233e3600836c8109391f9681075 100755 (executable)
 #ifndef _GPS_GPIO_BRCM4750_H_
 #define _GPS_GPIO_BRCM4750_H_
 
+#include <linux/android_alarm.h>
 #include <linux/ioctl.h>
+#include <linux/wakelock.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
 
 #define GPS_GPIO_DRIVER_NAME "gps_brcm4750"
 
 
 #define IOC_GPS_GPIO_RESET       _IOW(GPS_GPIO_IOCTL_BASE, 0x0, int)
 #define IOC_GPS_GPIO_STANDBY     _IOW(GPS_GPIO_IOCTL_BASE, 0x1, int)
+/* start single shot wake up timer, set the value in msecs */
+#define IOC_GPS_START_TIMER      _IOW(GPS_GPIO_IOCTL_BASE, 0x2, int)
+/* stop wake up timer */
+#define IOC_GPS_STOP_TIMER       _IOW(GPS_GPIO_IOCTL_BASE, 0x3, int)
 
 #ifdef __KERNEL__
 struct gps_gpio_brcm4750_platform_data {
       void (*set_reset_gpio)(unsigned int gpio_val);
       void (*set_standby_gpio)(unsigned int gpio_val);
       void (*free_gpio)(void);
+      struct alarm alarm;
+      struct wake_lock gps_brcm4750_wake;
+      wait_queue_head_t gps_brcm4750_wq;
+      int timer_status;
 } __attribute__ ((packed));
 
 #endif  /* __KERNEL__ */