1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a0880df0SRodolfo Giometti /* 3a0880df0SRodolfo Giometti * pps-ldisc.c -- PPS line discipline 4a0880df0SRodolfo Giometti * 5a0880df0SRodolfo Giometti * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> 6a0880df0SRodolfo Giometti */ 7a0880df0SRodolfo Giometti 87f7cce74SAlexander Gordeev #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 97f7cce74SAlexander Gordeev 10a0880df0SRodolfo Giometti #include <linux/module.h> 11a0880df0SRodolfo Giometti #include <linux/serial_core.h> 12a0880df0SRodolfo Giometti #include <linux/tty.h> 13a0880df0SRodolfo Giometti #include <linux/pps_kernel.h> 14ce3da1a6SGeorge Spelvin #include <linux/bug.h> 15a0880df0SRodolfo Giometti 16a0880df0SRodolfo Giometti #define PPS_TTY_MAGIC 0x0001 17a0880df0SRodolfo Giometti 18593fb1aeSGeorge Spelvin static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status) 19a0880df0SRodolfo Giometti { 20593fb1aeSGeorge Spelvin struct pps_device *pps; 21593fb1aeSGeorge Spelvin struct pps_event_time ts; 22a0880df0SRodolfo Giometti 23593fb1aeSGeorge Spelvin pps_get_ts(&ts); 24593fb1aeSGeorge Spelvin 25593fb1aeSGeorge Spelvin pps = pps_lookup_dev(tty); 26ce3da1a6SGeorge Spelvin /* 27ce3da1a6SGeorge Spelvin * This should never fail, but the ldisc locking is very 28ce3da1a6SGeorge Spelvin * convoluted, so don't crash just in case. 29ce3da1a6SGeorge Spelvin */ 30ce3da1a6SGeorge Spelvin if (WARN_ON_ONCE(pps == NULL)) 31ce3da1a6SGeorge Spelvin return; 32a0880df0SRodolfo Giometti 335e196d34SAlexander Gordeev /* Now do the PPS event report */ 34593fb1aeSGeorge Spelvin pps_event(pps, &ts, status ? PPS_CAPTUREASSERT : 355e196d34SAlexander Gordeev PPS_CAPTURECLEAR, NULL); 365e196d34SAlexander Gordeev 375e196d34SAlexander Gordeev dev_dbg(pps->dev, "PPS %s at %lu\n", 385e196d34SAlexander Gordeev status ? "assert" : "clear", jiffies); 39a0880df0SRodolfo Giometti } 40a0880df0SRodolfo Giometti 41a0880df0SRodolfo Giometti static int (*alias_n_tty_open)(struct tty_struct *tty); 42a0880df0SRodolfo Giometti 43a0880df0SRodolfo Giometti static int pps_tty_open(struct tty_struct *tty) 44a0880df0SRodolfo Giometti { 45a0880df0SRodolfo Giometti struct pps_source_info info; 46a0880df0SRodolfo Giometti struct tty_driver *drv = tty->driver; 47a0880df0SRodolfo Giometti int index = tty->index + drv->name_base; 485e196d34SAlexander Gordeev struct pps_device *pps; 49a0880df0SRodolfo Giometti int ret; 50a0880df0SRodolfo Giometti 51a0880df0SRodolfo Giometti info.owner = THIS_MODULE; 52a0880df0SRodolfo Giometti info.dev = NULL; 53a0880df0SRodolfo Giometti snprintf(info.name, PPS_MAX_NAME_LEN, "%s%d", drv->driver_name, index); 54a0880df0SRodolfo Giometti snprintf(info.path, PPS_MAX_NAME_LEN, "/dev/%s%d", drv->name, index); 55a0880df0SRodolfo Giometti info.mode = PPS_CAPTUREBOTH | \ 56a0880df0SRodolfo Giometti PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \ 57a0880df0SRodolfo Giometti PPS_CANWAIT | PPS_TSFMT_TSPEC; 58a0880df0SRodolfo Giometti 595e196d34SAlexander Gordeev pps = pps_register_source(&info, PPS_CAPTUREBOTH | \ 60a0880df0SRodolfo Giometti PPS_OFFSETASSERT | PPS_OFFSETCLEAR); 613b1ad360SYueHaibing if (IS_ERR(pps)) { 62a0880df0SRodolfo Giometti pr_err("cannot register PPS source \"%s\"\n", info.path); 633b1ad360SYueHaibing return PTR_ERR(pps); 64a0880df0SRodolfo Giometti } 6503a7ffe4SGeorge Spelvin pps->lookup_cookie = tty; 66a0880df0SRodolfo Giometti 6703a7ffe4SGeorge Spelvin /* Now open the base class N_TTY ldisc */ 68a0880df0SRodolfo Giometti ret = alias_n_tty_open(tty); 695e196d34SAlexander Gordeev if (ret < 0) { 705e196d34SAlexander Gordeev pr_err("cannot open tty ldisc \"%s\"\n", info.path); 715e196d34SAlexander Gordeev goto err_unregister; 725e196d34SAlexander Gordeev } 73a0880df0SRodolfo Giometti 745e196d34SAlexander Gordeev dev_info(pps->dev, "source \"%s\" added\n", info.path); 75a0880df0SRodolfo Giometti 76a0880df0SRodolfo Giometti return 0; 775e196d34SAlexander Gordeev 785e196d34SAlexander Gordeev err_unregister: 795e196d34SAlexander Gordeev pps_unregister_source(pps); 805e196d34SAlexander Gordeev return ret; 81a0880df0SRodolfo Giometti } 82a0880df0SRodolfo Giometti 83a0880df0SRodolfo Giometti static void (*alias_n_tty_close)(struct tty_struct *tty); 84a0880df0SRodolfo Giometti 85a0880df0SRodolfo Giometti static void pps_tty_close(struct tty_struct *tty) 86a0880df0SRodolfo Giometti { 8703a7ffe4SGeorge Spelvin struct pps_device *pps = pps_lookup_dev(tty); 88a0880df0SRodolfo Giometti 89a0880df0SRodolfo Giometti alias_n_tty_close(tty); 90a0880df0SRodolfo Giometti 91ce3da1a6SGeorge Spelvin if (WARN_ON(!pps)) 92ce3da1a6SGeorge Spelvin return; 93ce3da1a6SGeorge Spelvin 945e196d34SAlexander Gordeev dev_info(pps->dev, "removed\n"); 955e196d34SAlexander Gordeev pps_unregister_source(pps); 96a0880df0SRodolfo Giometti } 97a0880df0SRodolfo Giometti 98a0880df0SRodolfo Giometti static struct tty_ldisc_ops pps_ldisc_ops; 99a0880df0SRodolfo Giometti 100a0880df0SRodolfo Giometti /* 101a0880df0SRodolfo Giometti * Module stuff 102a0880df0SRodolfo Giometti */ 103a0880df0SRodolfo Giometti 104a0880df0SRodolfo Giometti static int __init pps_tty_init(void) 105a0880df0SRodolfo Giometti { 106a0880df0SRodolfo Giometti int err; 107a0880df0SRodolfo Giometti 108a0880df0SRodolfo Giometti /* Inherit the N_TTY's ops */ 109a0880df0SRodolfo Giometti n_tty_inherit_ops(&pps_ldisc_ops); 110a0880df0SRodolfo Giometti 111a0880df0SRodolfo Giometti /* Save N_TTY's open()/close() methods */ 112a0880df0SRodolfo Giometti alias_n_tty_open = pps_ldisc_ops.open; 113a0880df0SRodolfo Giometti alias_n_tty_close = pps_ldisc_ops.close; 114a0880df0SRodolfo Giometti 115a0880df0SRodolfo Giometti /* Init PPS_TTY data */ 116a0880df0SRodolfo Giometti pps_ldisc_ops.owner = THIS_MODULE; 117a0880df0SRodolfo Giometti pps_ldisc_ops.magic = PPS_TTY_MAGIC; 118a0880df0SRodolfo Giometti pps_ldisc_ops.name = "pps_tty"; 119a0880df0SRodolfo Giometti pps_ldisc_ops.dcd_change = pps_tty_dcd_change; 120a0880df0SRodolfo Giometti pps_ldisc_ops.open = pps_tty_open; 121a0880df0SRodolfo Giometti pps_ldisc_ops.close = pps_tty_close; 122a0880df0SRodolfo Giometti 123a0880df0SRodolfo Giometti err = tty_register_ldisc(N_PPS, &pps_ldisc_ops); 124a0880df0SRodolfo Giometti if (err) 125a0880df0SRodolfo Giometti pr_err("can't register PPS line discipline\n"); 126a0880df0SRodolfo Giometti else 127a0880df0SRodolfo Giometti pr_info("PPS line discipline registered\n"); 128a0880df0SRodolfo Giometti 129a0880df0SRodolfo Giometti return err; 130a0880df0SRodolfo Giometti } 131a0880df0SRodolfo Giometti 132a0880df0SRodolfo Giometti static void __exit pps_tty_cleanup(void) 133a0880df0SRodolfo Giometti { 134a0880df0SRodolfo Giometti int err; 135a0880df0SRodolfo Giometti 136a0880df0SRodolfo Giometti err = tty_unregister_ldisc(N_PPS); 137a0880df0SRodolfo Giometti if (err) 138a0880df0SRodolfo Giometti pr_err("can't unregister PPS line discipline\n"); 139a0880df0SRodolfo Giometti else 140a0880df0SRodolfo Giometti pr_info("PPS line discipline removed\n"); 141a0880df0SRodolfo Giometti } 142a0880df0SRodolfo Giometti 143a0880df0SRodolfo Giometti module_init(pps_tty_init); 144a0880df0SRodolfo Giometti module_exit(pps_tty_cleanup); 145a0880df0SRodolfo Giometti 146a0880df0SRodolfo Giometti MODULE_ALIAS_LDISC(N_PPS); 147a0880df0SRodolfo Giometti MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 148a0880df0SRodolfo Giometti MODULE_DESCRIPTION("PPS TTY device driver"); 149a0880df0SRodolfo Giometti MODULE_LICENSE("GPL"); 150