]> Pileus Git - ~andy/linux/blobdiff - drivers/staging/android/alarm-dev.c
staging: alarm-dev: Implement compat_ioctl support
[~andy/linux] / drivers / staging / android / alarm-dev.c
index a9b293ff3cc8350b8fc1faed4f32dab3fb118873..ceb1c643753d9fef288d7d5eda97b5191b7726a7 100644 (file)
@@ -42,10 +42,6 @@ do {                                                                 \
        ANDROID_ALARM_RTC_WAKEUP_MASK | \
        ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
 
-/* support old userspace code */
-#define ANDROID_ALARM_SET_OLD               _IOW('a', 2, time_t) /* set alarm */
-#define ANDROID_ALARM_SET_AND_WAIT_OLD      _IOW('a', 3, time_t)
-
 static int alarm_opened;
 static DEFINE_SPINLOCK(alarm_slock);
 static struct wakeup_source alarm_wake_lock;
@@ -96,18 +92,116 @@ static void devalarm_cancel(struct devalarm *alrm)
                hrtimer_cancel(&alrm->u.hrt);
 }
 
+static void alarm_clear(enum android_alarm_type alarm_type)
+{
+       uint32_t alarm_type_mask = 1U << alarm_type;
+       unsigned long flags;
 
-static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+       spin_lock_irqsave(&alarm_slock, flags);
+       alarm_dbg(IO, "alarm %d clear\n", alarm_type);
+       devalarm_try_to_cancel(&alarms[alarm_type]);
+       if (alarm_pending) {
+               alarm_pending &= ~alarm_type_mask;
+               if (!alarm_pending && !wait_pending)
+                       __pm_relax(&alarm_wake_lock);
+       }
+       alarm_enabled &= ~alarm_type_mask;
+       spin_unlock_irqrestore(&alarm_slock, flags);
+
+}
+
+static void alarm_set(enum android_alarm_type alarm_type,
+                                                       struct timespec *ts)
 {
-       int rv = 0;
+       uint32_t alarm_type_mask = 1U << alarm_type;
        unsigned long flags;
-       struct timespec new_alarm_time;
-       struct timespec new_rtc_time;
-       struct timespec tmp_time;
+
+       spin_lock_irqsave(&alarm_slock, flags);
+       alarm_dbg(IO, "alarm %d set %ld.%09ld\n",
+                       alarm_type, ts->tv_sec, ts->tv_nsec);
+       alarm_enabled |= alarm_type_mask;
+       devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts));
+       spin_unlock_irqrestore(&alarm_slock, flags);
+}
+
+static int alarm_wait(void)
+{
+       unsigned long flags;
+       int rv = 0;
+
+       spin_lock_irqsave(&alarm_slock, flags);
+       alarm_dbg(IO, "alarm wait\n");
+       if (!alarm_pending && wait_pending) {
+               __pm_relax(&alarm_wake_lock);
+               wait_pending = 0;
+       }
+       spin_unlock_irqrestore(&alarm_slock, flags);
+
+       rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
+       if (rv)
+               return rv;
+
+       spin_lock_irqsave(&alarm_slock, flags);
+       rv = alarm_pending;
+       wait_pending = 1;
+       alarm_pending = 0;
+       spin_unlock_irqrestore(&alarm_slock, flags);
+
+       return rv;
+}
+
+static int alarm_set_rtc(struct timespec *ts)
+{
        struct rtc_time new_rtc_tm;
        struct rtc_device *rtc_dev;
+       unsigned long flags;
+       int rv = 0;
+
+       rtc_time_to_tm(ts->tv_sec, &new_rtc_tm);
+       rtc_dev = alarmtimer_get_rtcdev();
+       rv = do_settimeofday(ts);
+       if (rv < 0)
+               return rv;
+       if (rtc_dev)
+               rv = rtc_set_time(rtc_dev, &new_rtc_tm);
+
+       spin_lock_irqsave(&alarm_slock, flags);
+       alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
+       wake_up(&alarm_wait_queue);
+       spin_unlock_irqrestore(&alarm_slock, flags);
+
+       return rv;
+}
+
+static int alarm_get_time(enum android_alarm_type alarm_type,
+                                                       struct timespec *ts)
+{
+       int rv = 0;
+
+       switch (alarm_type) {
+       case ANDROID_ALARM_RTC_WAKEUP:
+       case ANDROID_ALARM_RTC:
+               getnstimeofday(ts);
+               break;
+       case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
+       case ANDROID_ALARM_ELAPSED_REALTIME:
+               get_monotonic_boottime(ts);
+               break;
+       case ANDROID_ALARM_SYSTEMTIME:
+               ktime_get_ts(ts);
+               break;
+       default:
+               rv = -EINVAL;
+       }
+       return rv;
+}
+
+static long alarm_do_ioctl(struct file *file, unsigned int cmd,
+                                                       struct timespec *ts)
+{
+       int rv = 0;
+       unsigned long flags;
        enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
-       uint32_t alarm_type_mask = 1U << alarm_type;
 
        if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
                return -EINVAL;
@@ -130,115 +224,89 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
        switch (ANDROID_ALARM_BASE_CMD(cmd)) {
        case ANDROID_ALARM_CLEAR(0):
-               spin_lock_irqsave(&alarm_slock, flags);
-               alarm_dbg(IO, "alarm %d clear\n", alarm_type);
-               devalarm_try_to_cancel(&alarms[alarm_type]);
-               if (alarm_pending) {
-                       alarm_pending &= ~alarm_type_mask;
-                       if (!alarm_pending && !wait_pending)
-                               __pm_relax(&alarm_wake_lock);
-               }
-               alarm_enabled &= ~alarm_type_mask;
-               spin_unlock_irqrestore(&alarm_slock, flags);
+               alarm_clear(alarm_type);
                break;
-
-       case ANDROID_ALARM_SET_OLD:
-       case ANDROID_ALARM_SET_AND_WAIT_OLD:
-               if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
-                       rv = -EFAULT;
-                       goto err1;
-               }
-               new_alarm_time.tv_nsec = 0;
-               goto from_old_alarm_set;
-
-       case ANDROID_ALARM_SET_AND_WAIT(0):
        case ANDROID_ALARM_SET(0):
-               if (copy_from_user(&new_alarm_time, (void __user *)arg,
-                   sizeof(new_alarm_time))) {
-                       rv = -EFAULT;
-                       goto err1;
-               }
-from_old_alarm_set:
-               spin_lock_irqsave(&alarm_slock, flags);
-               alarm_dbg(IO, "alarm %d set %ld.%09ld\n",
-                         alarm_type,
-                         new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
-               alarm_enabled |= alarm_type_mask;
-               devalarm_start(&alarms[alarm_type],
-                       timespec_to_ktime(new_alarm_time));
-               spin_unlock_irqrestore(&alarm_slock, flags);
-               if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
-                   && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
-                       break;
+               alarm_set(alarm_type, ts);
+               break;
+       case ANDROID_ALARM_SET_AND_WAIT(0):
+               alarm_set(alarm_type, ts);
                /* fall though */
        case ANDROID_ALARM_WAIT:
-               spin_lock_irqsave(&alarm_slock, flags);
-               alarm_dbg(IO, "alarm wait\n");
-               if (!alarm_pending && wait_pending) {
-                       __pm_relax(&alarm_wake_lock);
-                       wait_pending = 0;
-               }
-               spin_unlock_irqrestore(&alarm_slock, flags);
-               rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
-               if (rv)
-                       goto err1;
-               spin_lock_irqsave(&alarm_slock, flags);
-               rv = alarm_pending;
-               wait_pending = 1;
-               alarm_pending = 0;
-               spin_unlock_irqrestore(&alarm_slock, flags);
+               rv = alarm_wait();
                break;
        case ANDROID_ALARM_SET_RTC:
-               if (copy_from_user(&new_rtc_time, (void __user *)arg,
-                   sizeof(new_rtc_time))) {
-                       rv = -EFAULT;
-                       goto err1;
-               }
-               rtc_time_to_tm(new_rtc_time.tv_sec, &new_rtc_tm);
-               rtc_dev = alarmtimer_get_rtcdev();
-               rv = do_settimeofday(&new_rtc_time);
-               if (rv < 0)
-                       goto err1;
-               if (rtc_dev)
-                       rv = rtc_set_time(rtc_dev, &new_rtc_tm);
-               spin_lock_irqsave(&alarm_slock, flags);
-               alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
-               wake_up(&alarm_wait_queue);
-               spin_unlock_irqrestore(&alarm_slock, flags);
-               if (rv < 0)
-                       goto err1;
+               rv = alarm_set_rtc(ts);
                break;
        case ANDROID_ALARM_GET_TIME(0):
-               switch (alarm_type) {
-               case ANDROID_ALARM_RTC_WAKEUP:
-               case ANDROID_ALARM_RTC:
-                       getnstimeofday(&tmp_time);
-                       break;
-               case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
-               case ANDROID_ALARM_ELAPSED_REALTIME:
-                       get_monotonic_boottime(&tmp_time);
-                       break;
-               case ANDROID_ALARM_SYSTEMTIME:
-                       ktime_get_ts(&tmp_time);
-                       break;
-               default:
-                       rv = -EINVAL;
-                       goto err1;
-               }
-               if (copy_to_user((void __user *)arg, &tmp_time,
-                   sizeof(tmp_time))) {
-                       rv = -EFAULT;
-                       goto err1;
-               }
+               rv = alarm_get_time(alarm_type, ts);
                break;
 
        default:
                rv = -EINVAL;
        }
-err1:
        return rv;
 }
 
+static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+
+       struct timespec ts;
+       int rv;
+
+       switch (ANDROID_ALARM_BASE_CMD(cmd)) {
+       case ANDROID_ALARM_SET_AND_WAIT(0):
+       case ANDROID_ALARM_SET(0):
+       case ANDROID_ALARM_SET_RTC:
+               if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
+                       return -EFAULT;
+               break;
+       }
+
+       rv = alarm_do_ioctl(file, cmd, &ts);
+
+       switch (ANDROID_ALARM_BASE_CMD(cmd)) {
+       case ANDROID_ALARM_GET_TIME(0):
+               if (copy_to_user((void __user *)arg, &ts, sizeof(ts)))
+                       return -EFAULT;
+               break;
+       }
+
+       return rv;
+}
+#ifdef CONFIG_COMPAT
+static long alarm_compat_ioctl(struct file *file, unsigned int cmd,
+                                                       unsigned long arg)
+{
+
+       struct timespec ts;
+       int rv;
+
+       switch (ANDROID_ALARM_BASE_CMD(cmd)) {
+       case ANDROID_ALARM_SET_AND_WAIT_COMPAT(0):
+       case ANDROID_ALARM_SET_COMPAT(0):
+       case ANDROID_ALARM_SET_RTC_COMPAT:
+               if (compat_get_timespec(&ts, (void __user *)arg))
+                       return -EFAULT;
+               /* fall through */
+       case ANDROID_ALARM_GET_TIME_COMPAT(0):
+               cmd = ANDROID_ALARM_COMPAT_TO_NORM(cmd);
+               break;
+       }
+
+       rv = alarm_do_ioctl(file, cmd, &ts);
+
+       switch (ANDROID_ALARM_BASE_CMD(cmd)) {
+       case ANDROID_ALARM_GET_TIME(0): /* NOTE: we modified cmd above */
+               if (compat_put_timespec(&ts, (void __user *)arg))
+                       return -EFAULT;
+               break;
+       }
+
+       return rv;
+}
+#endif
+
 static int alarm_open(struct inode *inode, struct file *file)
 {
        file->private_data = NULL;
@@ -319,6 +387,9 @@ static const struct file_operations alarm_fops = {
        .unlocked_ioctl = alarm_ioctl,
        .open = alarm_open,
        .release = alarm_release,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = alarm_compat_ioctl,
+#endif
 };
 
 static struct miscdevice alarm_device = {