Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * pps-ldisc.c -- PPS line discipline
0004  *
0005  * Copyright (C) 2008   Rodolfo Giometti <giometti@linux.it>
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/module.h>
0011 #include <linux/serial_core.h>
0012 #include <linux/tty.h>
0013 #include <linux/pps_kernel.h>
0014 #include <linux/bug.h>
0015 
0016 static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status)
0017 {
0018     struct pps_device *pps;
0019     struct pps_event_time ts;
0020 
0021     pps_get_ts(&ts);
0022 
0023     pps = pps_lookup_dev(tty);
0024     /*
0025      * This should never fail, but the ldisc locking is very
0026      * convoluted, so don't crash just in case.
0027      */
0028     if (WARN_ON_ONCE(pps == NULL))
0029         return;
0030 
0031     /* Now do the PPS event report */
0032     pps_event(pps, &ts, status ? PPS_CAPTUREASSERT :
0033             PPS_CAPTURECLEAR, NULL);
0034 
0035     dev_dbg(pps->dev, "PPS %s at %lu\n",
0036             status ? "assert" : "clear", jiffies);
0037 }
0038 
0039 static int (*alias_n_tty_open)(struct tty_struct *tty);
0040 
0041 static int pps_tty_open(struct tty_struct *tty)
0042 {
0043     struct pps_source_info info;
0044     struct tty_driver *drv = tty->driver;
0045     int index = tty->index + drv->name_base;
0046     struct pps_device *pps;
0047     int ret;
0048 
0049     info.owner = THIS_MODULE;
0050     info.dev = NULL;
0051     snprintf(info.name, PPS_MAX_NAME_LEN, "%s%d", drv->driver_name, index);
0052     snprintf(info.path, PPS_MAX_NAME_LEN, "/dev/%s%d", drv->name, index);
0053     info.mode = PPS_CAPTUREBOTH | \
0054             PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
0055             PPS_CANWAIT | PPS_TSFMT_TSPEC;
0056 
0057     pps = pps_register_source(&info, PPS_CAPTUREBOTH | \
0058                 PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
0059     if (IS_ERR(pps)) {
0060         pr_err("cannot register PPS source \"%s\"\n", info.path);
0061         return PTR_ERR(pps);
0062     }
0063     pps->lookup_cookie = tty;
0064 
0065     /* Now open the base class N_TTY ldisc */
0066     ret = alias_n_tty_open(tty);
0067     if (ret < 0) {
0068         pr_err("cannot open tty ldisc \"%s\"\n", info.path);
0069         goto err_unregister;
0070     }
0071 
0072     dev_info(pps->dev, "source \"%s\" added\n", info.path);
0073 
0074     return 0;
0075 
0076 err_unregister:
0077     pps_unregister_source(pps);
0078     return ret;
0079 }
0080 
0081 static void (*alias_n_tty_close)(struct tty_struct *tty);
0082 
0083 static void pps_tty_close(struct tty_struct *tty)
0084 {
0085     struct pps_device *pps = pps_lookup_dev(tty);
0086 
0087     alias_n_tty_close(tty);
0088 
0089     if (WARN_ON(!pps))
0090         return;
0091 
0092     dev_info(pps->dev, "removed\n");
0093     pps_unregister_source(pps);
0094 }
0095 
0096 static struct tty_ldisc_ops pps_ldisc_ops;
0097 
0098 /*
0099  * Module stuff
0100  */
0101 
0102 static int __init pps_tty_init(void)
0103 {
0104     int err;
0105 
0106     /* Inherit the N_TTY's ops */
0107     n_tty_inherit_ops(&pps_ldisc_ops);
0108 
0109     /* Save N_TTY's open()/close() methods */
0110     alias_n_tty_open = pps_ldisc_ops.open;
0111     alias_n_tty_close = pps_ldisc_ops.close;
0112 
0113     /* Init PPS_TTY data */
0114     pps_ldisc_ops.owner = THIS_MODULE;
0115     pps_ldisc_ops.num = N_PPS;
0116     pps_ldisc_ops.name = "pps_tty";
0117     pps_ldisc_ops.dcd_change = pps_tty_dcd_change;
0118     pps_ldisc_ops.open = pps_tty_open;
0119     pps_ldisc_ops.close = pps_tty_close;
0120 
0121     err = tty_register_ldisc(&pps_ldisc_ops);
0122     if (err)
0123         pr_err("can't register PPS line discipline\n");
0124     else
0125         pr_info("PPS line discipline registered\n");
0126 
0127     return err;
0128 }
0129 
0130 static void __exit pps_tty_cleanup(void)
0131 {
0132     tty_unregister_ldisc(&pps_ldisc_ops);
0133 }
0134 
0135 module_init(pps_tty_init);
0136 module_exit(pps_tty_cleanup);
0137 
0138 MODULE_ALIAS_LDISC(N_PPS);
0139 MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
0140 MODULE_DESCRIPTION("PPS TTY device driver");
0141 MODULE_LICENSE("GPL");