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 static void pps_tty_dcd_change(struct tty_struct
*tty
, bool active
)
18 struct pps_device
*pps
;
19 struct pps_event_time ts
;
23 pps
= pps_lookup_dev(tty
);
25 * This should never fail, but the ldisc locking is very
26 * convoluted, so don't crash just in case.
28 if (WARN_ON_ONCE(pps
== NULL
))
31 /* Now do the PPS event report */
32 pps_event(pps
, &ts
, active
? PPS_CAPTUREASSERT
:
33 PPS_CAPTURECLEAR
, NULL
);
35 dev_dbg(pps
->dev
, "PPS %s at %lu\n",
36 active
? "assert" : "clear", jiffies
);
39 static int (*alias_n_tty_open
)(struct tty_struct
*tty
);
41 static int pps_tty_open(struct tty_struct
*tty
)
43 struct pps_source_info info
;
44 struct tty_driver
*drv
= tty
->driver
;
45 int index
= tty
->index
+ drv
->name_base
;
46 struct pps_device
*pps
;
49 info
.owner
= THIS_MODULE
;
51 snprintf(info
.name
, PPS_MAX_NAME_LEN
, "%s%d", drv
->driver_name
, index
);
52 snprintf(info
.path
, PPS_MAX_NAME_LEN
, "/dev/%s%d", drv
->name
, index
);
53 info
.mode
= PPS_CAPTUREBOTH
| \
54 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
| \
55 PPS_CANWAIT
| PPS_TSFMT_TSPEC
;
57 pps
= pps_register_source(&info
, PPS_CAPTUREBOTH
| \
58 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
);
60 pr_err("cannot register PPS source \"%s\"\n", info
.path
);
63 pps
->lookup_cookie
= tty
;
65 /* Now open the base class N_TTY ldisc */
66 ret
= alias_n_tty_open(tty
);
68 pr_err("cannot open tty ldisc \"%s\"\n", info
.path
);
72 dev_info(pps
->dev
, "source \"%s\" added\n", info
.path
);
77 pps_unregister_source(pps
);
81 static void (*alias_n_tty_close
)(struct tty_struct
*tty
);
83 static void pps_tty_close(struct tty_struct
*tty
)
85 struct pps_device
*pps
= pps_lookup_dev(tty
);
87 alias_n_tty_close(tty
);
92 dev_info(pps
->dev
, "removed\n");
93 pps_unregister_source(pps
);
96 static struct tty_ldisc_ops pps_ldisc_ops
;
102 static int __init
pps_tty_init(void)
106 /* Inherit the N_TTY's ops */
107 n_tty_inherit_ops(&pps_ldisc_ops
);
109 /* Save N_TTY's open()/close() methods */
110 alias_n_tty_open
= pps_ldisc_ops
.open
;
111 alias_n_tty_close
= pps_ldisc_ops
.close
;
113 /* Init PPS_TTY data */
114 pps_ldisc_ops
.owner
= THIS_MODULE
;
115 pps_ldisc_ops
.num
= N_PPS
;
116 pps_ldisc_ops
.name
= "pps_tty";
117 pps_ldisc_ops
.dcd_change
= pps_tty_dcd_change
;
118 pps_ldisc_ops
.open
= pps_tty_open
;
119 pps_ldisc_ops
.close
= pps_tty_close
;
121 err
= tty_register_ldisc(&pps_ldisc_ops
);
123 pr_err("can't register PPS line discipline\n");
125 pr_info("PPS line discipline registered\n");
130 static void __exit
pps_tty_cleanup(void)
132 tty_unregister_ldisc(&pps_ldisc_ops
);
135 module_init(pps_tty_init
);
136 module_exit(pps_tty_cleanup
);
138 MODULE_ALIAS_LDISC(N_PPS
);
139 MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
140 MODULE_DESCRIPTION("PPS TTY device driver");
141 MODULE_LICENSE("GPL");