1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * pps-ldisc.c -- PPS line discipline
5 * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/module.h>
11 #include <linux/serial_core.h>
12 #include <linux/tty.h>
13 #include <linux/pps_kernel.h>
14 #include <linux/bug.h>
16 #define PPS_TTY_MAGIC 0x0001
18 static void pps_tty_dcd_change(struct tty_struct
*tty
, unsigned int status
)
20 struct pps_device
*pps
;
21 struct pps_event_time ts
;
25 pps
= pps_lookup_dev(tty
);
27 * This should never fail, but the ldisc locking is very
28 * convoluted, so don't crash just in case.
30 if (WARN_ON_ONCE(pps
== NULL
))
33 /* Now do the PPS event report */
34 pps_event(pps
, &ts
, status
? PPS_CAPTUREASSERT
:
35 PPS_CAPTURECLEAR
, NULL
);
37 dev_dbg(pps
->dev
, "PPS %s at %lu\n",
38 status
? "assert" : "clear", jiffies
);
41 static int (*alias_n_tty_open
)(struct tty_struct
*tty
);
43 static int pps_tty_open(struct tty_struct
*tty
)
45 struct pps_source_info info
;
46 struct tty_driver
*drv
= tty
->driver
;
47 int index
= tty
->index
+ drv
->name_base
;
48 struct pps_device
*pps
;
51 info
.owner
= THIS_MODULE
;
53 snprintf(info
.name
, PPS_MAX_NAME_LEN
, "%s%d", drv
->driver_name
, index
);
54 snprintf(info
.path
, PPS_MAX_NAME_LEN
, "/dev/%s%d", drv
->name
, index
);
55 info
.mode
= PPS_CAPTUREBOTH
| \
56 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
| \
57 PPS_CANWAIT
| PPS_TSFMT_TSPEC
;
59 pps
= pps_register_source(&info
, PPS_CAPTUREBOTH
| \
60 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
);
62 pr_err("cannot register PPS source \"%s\"\n", info
.path
);
65 pps
->lookup_cookie
= tty
;
67 /* Now open the base class N_TTY ldisc */
68 ret
= alias_n_tty_open(tty
);
70 pr_err("cannot open tty ldisc \"%s\"\n", info
.path
);
74 dev_info(pps
->dev
, "source \"%s\" added\n", info
.path
);
79 pps_unregister_source(pps
);
83 static void (*alias_n_tty_close
)(struct tty_struct
*tty
);
85 static void pps_tty_close(struct tty_struct
*tty
)
87 struct pps_device
*pps
= pps_lookup_dev(tty
);
89 alias_n_tty_close(tty
);
94 dev_info(pps
->dev
, "removed\n");
95 pps_unregister_source(pps
);
98 static struct tty_ldisc_ops pps_ldisc_ops
;
104 static int __init
pps_tty_init(void)
108 /* Inherit the N_TTY's ops */
109 n_tty_inherit_ops(&pps_ldisc_ops
);
111 /* Save N_TTY's open()/close() methods */
112 alias_n_tty_open
= pps_ldisc_ops
.open
;
113 alias_n_tty_close
= pps_ldisc_ops
.close
;
115 /* Init PPS_TTY data */
116 pps_ldisc_ops
.owner
= THIS_MODULE
;
117 pps_ldisc_ops
.magic
= PPS_TTY_MAGIC
;
118 pps_ldisc_ops
.name
= "pps_tty";
119 pps_ldisc_ops
.dcd_change
= pps_tty_dcd_change
;
120 pps_ldisc_ops
.open
= pps_tty_open
;
121 pps_ldisc_ops
.close
= pps_tty_close
;
123 err
= tty_register_ldisc(N_PPS
, &pps_ldisc_ops
);
125 pr_err("can't register PPS line discipline\n");
127 pr_info("PPS line discipline registered\n");
132 static void __exit
pps_tty_cleanup(void)
136 err
= tty_unregister_ldisc(N_PPS
);
138 pr_err("can't unregister PPS line discipline\n");
140 pr_info("PPS line discipline removed\n");
143 module_init(pps_tty_init
);
144 module_exit(pps_tty_cleanup
);
146 MODULE_ALIAS_LDISC(N_PPS
);
147 MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
148 MODULE_DESCRIPTION("PPS TTY device driver");
149 MODULE_LICENSE("GPL");