]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Add GPS Proxy Driver
authorAnna Hanna Sedlak Grinbaum <asedla@codeaurora.org>
Sun, 10 Apr 2016 06:28:15 +0000 (09:28 +0300)
committerNicolas Dechesne <nicolas.dechesne@linaro.org>
Fri, 10 Jun 2016 07:59:38 +0000 (09:59 +0200)
The GPS Proxy Driver is a layer connecting between user space application and
GPSD service.

Change-Id: I8e4dd855839a1d9bc0b8f7431063fc200f8c2ebb
Signed-off-by: Anna Hanna Sedlak Grinbaum <asedla@codeaurora.org>
drivers/soc/qcom/Kconfig
drivers/soc/qcom/Makefile
drivers/soc/qcom/gps_proxy.c [new file with mode: 0644]
include/uapi/linux/gps_proxy.h [new file with mode: 0644]

index 9f244d633f3b7d474794e9ead629908ec8a0d1b9..4138de6da18b3724cc8a8b4aff236c88279c71e7 100644 (file)
@@ -83,6 +83,15 @@ config QCOM_WCNSS_CTRL
 config MSM_BUS_SCALING
        bool "Bus scaling driver"
        default n
+
+config QTI_LNX_GPS_PROXY
+       tristate "User mode QTI_LNX_GPS_PROXY device driver support"
+       help
+         This supports user mode QTI_LNX_GPS_PROXY
+
+         Note that this application programming interface is EXPERIMENTAL
+         and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
+
        help
                This option enables bus scaling on MSM devices.  Bus scaling
                allows devices to request the clocks be set to rates sufficient
index 49e0f7c3aeda0348b00978692e9bacd112b05da6..bf6f22a47a2b6db5520e2e56148606137aab60a2 100644 (file)
@@ -13,3 +13,5 @@ obj-$(CONFIG_MFD_QCOM_SMD_RPM) += rpm_log.o
 obj-$(CONFIG_MSM_BUS_SCALING) += msm_bus/
 obj-$(CONFIG_BUS_TOPOLOGY_ADHOC) += msm_bus/
 obj-$(CONFIG_QCOM_IPCRTR_STUB) += ipcrtr_stub.o
+
+obj-$(CONFIG_QTI_LNX_GPS_PROXY) += gps_proxy.o
diff --git a/drivers/soc/qcom/gps_proxy.c b/drivers/soc/qcom/gps_proxy.c
new file mode 100644 (file)
index 0000000..6812962
--- /dev/null
@@ -0,0 +1,304 @@
+/* Copyright (c) 2016, 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.
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <uapi/linux/gps_proxy.h>
+
+/* Module information */
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL v2");
+
+#define LOC_SERVICE_SVC_ID     0x00000010
+#define LOC_SERVICE_V1                 2
+#define LOC_SERVICE_INS_ID     0
+
+#define DRIVER_VERSION                 "v1.0"
+#define DRIVER_DESC            "GPS TTY driver"
+#define MODULE_NAME            "gps_proxy"
+#define TTY_DRIVER_NAME        "gps_serial"
+#define TTY_DEV_NAME           "ttyGPS"
+#define MAX_TTY_BUFFER_SZ      0x10000
+#define GPS_TTY_MAJOR          100     /* experimental range */
+#define DEVICE_NAME            "gps_proxy_ch"
+#define CLASS_NAME             "gps_proxy_class"
+
+static struct tty_driver *gps_proxy_tty_driver;
+static struct tty_port gps_proxy_tty_port;
+static bool g_port_open = false;
+static struct semaphore g_port_sem;
+static int gps_proxy_ch_driver_major = 0;
+static struct class* gps_proxy_ch_class = 0;
+static struct device* gps_proxy_ch_dev = 0;
+
+static void serial_port_shutdown(struct tty_port *tport)
+{
+}
+
+static int serial_port_activate(struct tty_port *tport, struct tty_struct *tty)
+{
+       return 0;
+}
+
+static struct tty_port_operations serial_port_ops = {
+       .activate = serial_port_activate,
+       .shutdown = serial_port_shutdown
+};
+
+static int gps_proxy_open(struct tty_struct *tty, struct file *file)
+{
+       int rc; 
+       rc = tty_port_open(&gps_proxy_tty_port, tty, file);
+       g_port_open = true;
+       up(&g_port_sem);
+       return rc;
+}
+
+static void gps_proxy_close(struct tty_struct *tty, struct file *file)
+{
+       tty_port_close(tty->port, tty, file);
+       g_port_open = false;
+       down(&g_port_sem);
+}
+
+static void gps_proxy_tty_hangup(struct tty_struct *tty)
+{
+       tty_port_hangup(tty->port);
+}
+
+static int gps_proxy_write(struct tty_struct *tty, 
+                     const unsigned char *buffer, int count)
+{
+       return count;
+}
+
+static int gps_proxy_write_room(struct tty_struct *tty) 
+{
+       return MAX_TTY_BUFFER_SZ;
+}
+
+static int gps_proxy_tty_chars_in_buffer(struct tty_struct *tty)
+{
+       return 0;
+}
+
+
+static int gps_proxy_tiocmget(struct tty_struct *tty)
+{
+       return 0;
+}
+
+static int gps_proxy_tiocmset(struct tty_struct *tty,
+                         unsigned int set, unsigned int clear)
+{
+       return 0;
+}
+
+static int gps_proxy_ioctl(struct tty_struct *tty,
+                      unsigned int cmd, unsigned long arg)
+{
+       return 0;
+}
+
+static struct tty_operations serial_ops = {
+       .open = gps_proxy_open,
+       .close = gps_proxy_close,
+       .hangup = gps_proxy_tty_hangup,
+       .write = gps_proxy_write,
+       .write_room = gps_proxy_write_room,
+       .chars_in_buffer = gps_proxy_tty_chars_in_buffer,
+       .tiocmget = gps_proxy_tiocmget,
+       .tiocmset = gps_proxy_tiocmset,
+       .ioctl = gps_proxy_ioctl,
+};
+
+int gps_proxy_ch_driver_open(struct inode *inode, struct file *filp)
+{
+       return 0;
+}
+
+int gps_proxy_ch_driver_close(struct inode *inode, struct file *filp)
+{
+       return 0;
+}
+
+long gps_proxy_chdev_ioctl(struct file *filp, unsigned int opt, unsigned long arg)
+{
+       int rc = 0;
+       struct gps_proxy_data buff;
+       switch (opt)
+       {
+               case QGPS_REGISTER_HANDLE:
+                       /* DOWN is necessary to make client wait till port is open */
+                       down(&g_port_sem);
+                       /* UP to semaphore is necessary here for close or
+                       next register handle (for parity) */
+                       up(&g_port_sem);
+                       rc = 0;
+                       break;
+               case QGPS_SEND_NMEA:
+                       pr_debug(KERN_INFO "Received string: %s\n", 
+                               ((struct gps_proxy_data*)arg)->nmea_string);
+                       rc = access_ok(struct gps_proxy_data, (struct gps_proxy_data*)arg,
+                                       sizeof(struct gps_proxy_data));
+                       if (!rc) {
+                               pr_err(KERN_ERR "Invalid argument was received\n");
+                               return rc;
+                       }
+                       rc = copy_from_user((void*)&buff, (void*)arg, sizeof(buff));
+                       if (rc) {
+                               pr_err(KERN_ERR "Number of bytes that \
+                                                couldn't be copied: %d", rc);
+                               return -EFAULT;
+                       }
+                       if (buff.nmea_length < QMI_LOC_NMEA_STRING_MAX_LENGTH_V02 + 1) {
+                               pr_debug(KERN_INFO "Received string: %s\n",
+                                       buff.nmea_string);
+                               rc = tty_insert_flip_string(&gps_proxy_tty_port, 
+                                               buff.nmea_string,
+                                               strnlen(buff.nmea_string,
+                                               QMI_LOC_NMEA_STRING_MAX_LENGTH_V02) + 1);
+                               if (rc < 0) {
+                                       pr_err(KERN_ERR "Error flipping string");
+                                       return rc;
+                               }
+                               tty_flip_buffer_push(&gps_proxy_tty_port);
+                       }
+                       else {
+                               pr_err(KERN_ERR "Illegal message size");
+                               rc = -EFAULT;
+                       }
+                       break;
+               case QGPS_IS_ACTIVE:
+                       if (g_port_open)
+                               rc = 0;
+                       else
+                               rc = -EFAULT;
+                       break;
+               default:
+                       rc = -EFAULT;
+                       break;
+       }
+       return rc;
+}
+
+struct file_operations gps_proxy_ch_driver_ops = {
+       open: gps_proxy_ch_driver_open,
+       unlocked_ioctl: gps_proxy_chdev_ioctl,
+       release: gps_proxy_ch_driver_close
+};
+
+static int __init gps_proxy_init(void)
+{
+       int rc;
+       struct device *ttydev;
+
+       sema_init(&g_port_sem,0);
+
+       gps_proxy_ch_driver_major = register_chrdev(0, "gps_proxy_ch_dev",
+                                                       &gps_proxy_ch_driver_ops);
+       if (gps_proxy_ch_driver_major < 0) {
+               pr_err(KERN_ERR "Failed to register char device\n");
+               return -EFAULT;
+       }
+       else {
+               pr_debug(KERN_INFO "char device registered with major %d\n",
+                       gps_proxy_ch_driver_major);
+       }
+
+       /* Register the device class */
+       gps_proxy_ch_class = class_create(THIS_MODULE, CLASS_NAME);
+       if (IS_ERR(gps_proxy_ch_class)){
+               unregister_chrdev(gps_proxy_ch_driver_major, DEVICE_NAME);
+               pr_debug(KERN_ALERT "Failed to register device class\n");
+               return -EFAULT;
+       }
+       pr_debug(KERN_INFO "EBBChar: device class registered correctly\n");
+
+       /* Register the device driver */
+       gps_proxy_ch_dev = device_create(gps_proxy_ch_class, NULL,
+                               MKDEV(gps_proxy_ch_driver_major, 0), NULL, DEVICE_NAME);
+       if (IS_ERR(gps_proxy_ch_dev)){
+               class_destroy(gps_proxy_ch_class);
+               unregister_chrdev(gps_proxy_ch_driver_major, DEVICE_NAME);
+               pr_debug(KERN_ALERT "Failed to create the device\n");
+               return -EFAULT;
+       }
+
+       /* allocate the tty driver */
+       gps_proxy_tty_driver = alloc_tty_driver(1);
+       if (!gps_proxy_tty_driver)
+               return -ENOMEM;
+
+       tty_port_init(&gps_proxy_tty_port);
+       gps_proxy_tty_port.ops = &serial_port_ops;
+
+       /* initialize the tty driver */
+       gps_proxy_tty_driver->driver_name = TTY_DRIVER_NAME;
+       gps_proxy_tty_driver->name = TTY_DEV_NAME;
+       gps_proxy_tty_driver->major = GPS_TTY_MAJOR;
+       gps_proxy_tty_driver->minor_start = 0;
+       gps_proxy_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       gps_proxy_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+       gps_proxy_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       gps_proxy_tty_driver->init_termios = tty_std_termios;
+
+       tty_set_operations(gps_proxy_tty_driver, &serial_ops);
+       /* register the tty driver */
+       rc = tty_register_driver(gps_proxy_tty_driver);
+       if (rc) {
+               pr_err("Failed to register gps_proxy tty driver\n");
+               put_tty_driver(gps_proxy_tty_driver);
+               return rc;
+       }
+
+       ttydev = tty_port_register_device(&gps_proxy_tty_port, gps_proxy_tty_driver, 0, 0);
+       if (IS_ERR(ttydev)) {
+               rc = PTR_ERR(ttydev);
+               pr_err("Failed to register gps_proxy tty proxy\n");
+               return rc;
+       }
+
+       pr_debug(KERN_INFO DRIVER_DESC " \n" DRIVER_VERSION);
+       return rc;
+}
+
+static void __exit gps_proxy_exit(void)
+{
+       tty_unregister_device(gps_proxy_tty_driver, 0);
+       tty_unregister_driver(gps_proxy_tty_driver);
+       unregister_chrdev(gps_proxy_ch_driver_major, "gps_proxy_ch_dev");
+       device_destroy(gps_proxy_ch_class, MKDEV(gps_proxy_ch_driver_major, 0));
+       class_unregister(gps_proxy_ch_class);
+       class_destroy(gps_proxy_ch_class);
+}
+
+late_initcall(gps_proxy_init);
+module_exit(gps_proxy_exit);
diff --git a/include/uapi/linux/gps_proxy.h b/include/uapi/linux/gps_proxy.h
new file mode 100644 (file)
index 0000000..5ac7f6b
--- /dev/null
@@ -0,0 +1,34 @@
+/* Copyright (c) 2016, 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.
+ */
+
+#ifndef __GPS_PROXY_H__
+#define __GPS_PROXY_H__
+
+#define QMI_LOC_NMEA_STRING_MAX_LENGTH_V02 201
+
+enum QGPS_TTY_IOCTL_CMDS {
+       QGPS_REGISTER_HANDLE_IOC = 0,
+       QGPS_SEND_NMEA_IOC,
+       QGPS_IS_ACTIVE_IOC,
+};
+
+#define QGPS_IOC_MAGIC                 'q'
+#define QGPS_REGISTER_HANDLE   _IO(QGPS_IOC_MAGIC, QGPS_REGISTER_HANDLE_IOC)
+#define QGPS_SEND_NMEA                 _IO(QGPS_IOC_MAGIC, QGPS_SEND_NMEA_IOC)
+#define QGPS_IS_ACTIVE                 _IO(QGPS_IOC_MAGIC, QGPS_IS_ACTIVE_IOC)
+
+struct gps_proxy_data {
+       size_t nmea_length;
+       char nmea_string[QMI_LOC_NMEA_STRING_MAX_LENGTH_V02];
+};
+
+#endif /* __GPS_PROXY_H__ */