summaryrefslogtreecommitdiff
path: root/drivers/sipc/stty.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/sipc/stty.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/sipc/stty.c')
-rw-r--r--drivers/sipc/stty.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/drivers/sipc/stty.c b/drivers/sipc/stty.c
new file mode 100644
index 00000000..1f1d5c31
--- /dev/null
+++ b/drivers/sipc/stty.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/sipc.h>
+#include <linux/stty.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/tty.h>
+#include <linux/vt_kern.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#ifdef CONFIG_OF
+#include <linux/of_device.h>
+#endif
+
+#include <linux/compat.h>
+#include <linux/tty_flip.h>
+#include <linux/kthread.h>
+
+#ifdef CONFIG_SPRD_2351
+#include <linux/sprd_2351.h>
+#endif
+
+
+#define STTY_DEV_MAX_NR 1
+#define STTY_MAX_DATA_LEN 4096
+
+#define STTY_STATE_OPEN 1
+#define STTY_STATE_CLOSE 0
+
+struct stty_device {
+ struct stty_init_data *pdata;
+ struct tty_port *port;
+ struct tty_struct *tty;
+ struct tty_driver *driver;
+
+ /* stty state */
+ uint32_t state;
+ struct mutex stat_lock;
+};
+
+static void stty_handler (int event, void* data)
+{
+ struct stty_device *stty = data;
+ int i, cnt = 0;
+ unsigned char buf[STTY_MAX_DATA_LEN] = {0};
+
+ pr_debug("stty handler event=%d \n", event);
+
+ switch(event) {
+ case SBUF_NOTIFY_WRITE:
+ break;
+ case SBUF_NOTIFY_READ:
+ cnt = sbuf_read(stty->pdata->dst, stty->pdata->channel,
+ stty->pdata->bufid,(void *)buf, STTY_MAX_DATA_LEN, 0);
+ pr_debug("stty handler read data len =%d \n", cnt);
+ mutex_lock(&(stty->stat_lock));
+ if ((stty->state == STTY_STATE_OPEN) && (cnt > 0)) {
+ for(i = 0; i < cnt; i++) {
+ tty_insert_flip_char(stty->port, buf[i], TTY_NORMAL);
+ }
+ tty_schedule_flip(stty->port);
+ }
+ mutex_unlock(&(stty->stat_lock));
+ break;
+ default:
+ printk(KERN_ERR "Received event is invalid(event=%d)\n", event);
+ }
+
+ return;
+}
+
+static int stty_open(struct tty_struct *tty, struct file * filp)
+{
+ struct stty_device *stty = NULL;
+ struct tty_driver *driver = NULL;
+
+ if (tty == NULL) {
+ printk(KERN_ERR "stty open input tty is NULL!\n");
+ return -ENOMEM;
+ }
+ driver = tty->driver;
+ stty = (struct stty_device *)driver->driver_state;
+
+ if(stty == NULL) {
+ printk(KERN_ERR "stty open input stty NULL!\n");
+ return -ENOMEM;
+ }
+
+ if (sbuf_status(stty->pdata->dst, stty->pdata->channel) != 0) {
+ printk(KERN_ERR "stty_open sbuf not ready to open!dst=%d,channel=%d\n"
+ ,stty->pdata->dst,stty->pdata->channel);
+ return -ENODEV;
+ }
+ stty->tty = tty;
+ tty->driver_data = (void *)stty;
+
+ mutex_lock(&(stty->stat_lock));
+ stty->state = STTY_STATE_OPEN;
+ mutex_unlock(&(stty->stat_lock));
+
+#ifdef CONFIG_SPRD_2351
+ rf2351_gpio_ctrl_power_enable(1);
+#endif
+#ifdef CONFIG_ARCH_SCX20
+ rf2351_vddwpa_ctrl_power_enable(1);
+#endif
+
+ pr_debug("stty_open device success! \n");
+
+ return 0;
+}
+
+static void stty_close(struct tty_struct *tty, struct file * filp)
+{
+ struct stty_device *stty = NULL;
+
+ if (tty == NULL) {
+ printk(KERN_ERR "stty close input tty is NULL!\n");
+ return;
+ }
+ stty = (struct stty_device *) tty->driver_data;
+ if (stty == NULL) {
+ printk(KERN_ERR "stty close s tty is NULL!\n");
+ return;
+ }
+
+ mutex_lock(&(stty->stat_lock));
+ stty->state = STTY_STATE_CLOSE;
+ mutex_unlock(&(stty->stat_lock));
+
+ pr_debug("stty_close device success !\n");
+
+#ifdef CONFIG_SPRD_2351
+ rf2351_gpio_ctrl_power_enable(0);
+#endif
+#ifdef CONFIG_ARCH_SCX20
+ rf2351_vddwpa_ctrl_power_enable(0);
+#endif
+
+ return;
+}
+
+static int stty_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
+{
+ struct stty_device *stty = tty->driver_data;
+ int cnt;
+ pr_debug("stty write count=%d \n", count);
+
+ cnt = sbuf_write(stty->pdata->dst, stty->pdata->channel,
+ stty->pdata->bufid, (void *)buf, count, -1);
+ return cnt;
+}
+
+
+static void stty_flush_chars(struct tty_struct *tty)
+{
+ return;
+}
+
+static int stty_write_room(struct tty_struct *tty)
+{
+ return INT_MAX;
+}
+
+static const struct tty_operations stty_ops = {
+ .open = stty_open,
+ .close = stty_close,
+ .write = stty_write,
+ .flush_chars = stty_flush_chars,
+ .write_room = stty_write_room,
+};
+
+static struct tty_port *stty_port_init()
+{
+ struct tty_port *port = NULL;
+
+ port = kzalloc(sizeof(struct tty_port),GFP_KERNEL);
+ if (port == NULL) {
+ printk(KERN_ERR "stty_port_init Failed to allocate device!\n");
+ return NULL;
+ }
+ tty_port_init(port);
+ return port;
+}
+
+static int stty_driver_init(struct stty_device *device)
+{
+ struct tty_driver *driver;
+ int ret = 0;
+
+ mutex_init(&(device->stat_lock));
+
+ device->port = stty_port_init();
+ if (!device->port) {
+ return -ENOMEM;
+ }
+
+ driver = alloc_tty_driver(STTY_DEV_MAX_NR);
+ if (!driver)
+ return -ENOMEM;
+ /*
+ * Initialize the tty_driver structure
+ * Entries in stty_driver that are NOT initialized:
+ * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
+ */
+ driver->owner = THIS_MODULE;
+ driver->driver_name = device->pdata->name;
+ driver->name = device->pdata->name;
+ driver->major = 0;
+ driver->type = TTY_DRIVER_TYPE_SYSTEM;
+ driver->subtype = SYSTEM_TYPE_TTY;
+ driver->init_termios = tty_std_termios;
+ driver->driver_state = (void*)device;
+ device->driver = driver;
+ /* initialize the tty driver */
+ tty_set_operations(driver, &stty_ops);
+ tty_port_link_device(device->port, driver, 0);
+ ret = tty_register_driver(driver);
+ if (ret) {
+ put_tty_driver(driver);
+ tty_port_destroy(device->port);
+ return ret;
+ }
+ return ret;
+}
+
+static void stty_driver_exit(struct stty_device *device)
+{
+ struct tty_driver *driver = device->driver;
+ tty_unregister_driver(driver);
+ tty_port_destroy(device->port);
+}
+
+static int stty_parse_dt(struct stty_init_data **init, struct device *dev)
+{
+#ifdef CONFIG_OF
+ struct of_device_node *np = dev->of_node;
+ struct stty_init_data *pdata = NULL;
+ int ret;
+ uint32_t data;
+
+ pdata = kzalloc(sizeof(struct stty_init_data), GFP_KERNEL);
+ if (!pdata) {
+ return -ENOMEM;
+ }
+
+ ret = of_property_read_string(np, "sprd,name", (const char **)&pdata->name);
+ if (ret) {
+ goto error;
+ }
+
+ ret = of_property_read_u32(np, "sprd,dst", (uint32_t *)&data);
+ if (ret) {
+ goto error;
+ }
+ pdata->dst = (uint8_t)data;
+
+ ret = of_property_read_u32(np, "sprd,channel", (uint32_t *)&data);
+ if (ret) {
+ goto error;
+ }
+ pdata->channel = (uint8_t)data;
+
+ ret = of_property_read_u32(np, "sprd,bufid", (uint32_t *)&pdata->bufid);
+ if (ret) {
+ goto error;
+ }
+
+ *init = pdata;
+ return 0;
+error:
+ kfree(pdata);
+ *init = NULL;
+ return ret;
+#else
+ return -ENODEV;
+#endif
+}
+
+static inline void stty_destroy_pdata(struct stty_init_data **init)
+{
+#ifdef CONFIG_OF
+ struct stty_init_data *pdata = *init;
+
+ if (pdata) {
+ kfree(pdata);
+ }
+
+ *init = NULL;
+#else
+ return;
+#endif
+}
+
+
+static int stty_probe(struct platform_device *pdev)
+{
+ struct stty_init_data *pdata = (struct stty_init_data*)pdev->dev.platform_data;
+ struct stty_device *stty;
+ int rval= 0;
+
+ if (pdev->dev.of_node && !pdata) {
+ rval = stty_parse_dt(&pdata, &pdev->dev);
+ if (rval) {
+ printk(KERN_ERR "failed to parse styy device tree, ret=%d\n", rval);
+ return rval;
+ }
+ }
+ pr_info("stty: after parse device tree, name=%s, dst=%u, channel=%u, bufid=%u\n",
+ pdata->name, pdata->dst, pdata->channel, pdata->bufid);
+
+ stty = kzalloc(sizeof(struct stty_device),GFP_KERNEL);
+ if (stty == NULL) {
+ stty_destroy_pdata(&pdata);
+ printk(KERN_ERR "stty Failed to allocate device!\n");
+ return -ENOMEM;
+ }
+
+ stty->pdata = pdata;
+ rval = stty_driver_init(stty);
+ if (rval) {
+ kfree(stty);
+ stty_destroy_pdata(&pdata);
+ printk(KERN_ERR "stty driver init error!\n");
+ return -EINVAL;
+ }
+
+ rval = sbuf_register_notifier(pdata->dst, pdata->channel,
+ pdata->bufid, stty_handler, stty);
+ if (rval) {
+ stty_driver_exit(stty);
+ kfree(stty->port);
+ kfree(stty);
+ printk(KERN_ERR "regitster notifier failed (%d)\n", rval);
+ return rval;
+ }
+
+ printk( "stty_probe init device addr: 0x%0x\n", (void *)stty);
+ platform_set_drvdata(pdev, stty);
+
+ return 0;
+}
+
+static int stty_remove(struct platform_device *pdev)
+{
+ struct stty_device *stty = platform_get_drvdata(pdev);
+ int rval;
+
+ rval = sbuf_register_notifier(stty->pdata->dst, stty->pdata->channel,
+ stty->pdata->bufid, NULL, NULL);
+ if (rval) {
+ printk(KERN_ERR " unregitster notifier failed (%d)\n", rval);
+ return rval;
+ }
+
+ stty_driver_exit(stty);
+ kfree(stty->port);
+ stty_destroy_pdata(&stty->pdata);
+ kfree(stty);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id stty_match_table[] = {
+ { .compatible = "sprd,stty4bt", },
+ { },
+};
+
+static struct platform_driver stty_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sttybt",
+ .of_match_table = stty_match_table,
+ },
+ .probe = stty_probe,
+ .remove = stty_remove,
+};
+
+static int __init stty_init(void)
+{
+ return platform_driver_register(&stty_driver);
+}
+
+static void __exit stty_exit(void)
+{
+ platform_driver_unregister(&stty_driver);
+}
+
+late_initcall(stty_init);
+module_exit(stty_exit);
+
+MODULE_AUTHOR("Dewu Jiang");
+MODULE_DESCRIPTION("SIPC/stty driver");
+MODULE_LICENSE("GPL");