2 * pps-ldisc.c -- PPS line discipline
5 * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24 #include <linux/module.h>
25 #include <linux/serial_core.h>
26 #include <linux/tty.h>
27 #include <linux/pps_kernel.h>
28 #include <linux/bug.h>
30 #define PPS_TTY_MAGIC 0x0001
32 static void pps_tty_dcd_change(struct tty_struct
*tty
, unsigned int status
)
34 struct pps_device
*pps
;
35 struct pps_event_time ts
;
39 pps
= pps_lookup_dev(tty
);
41 * This should never fail, but the ldisc locking is very
42 * convoluted, so don't crash just in case.
44 if (WARN_ON_ONCE(pps
== NULL
))
47 /* Now do the PPS event report */
48 pps_event(pps
, &ts
, status
? PPS_CAPTUREASSERT
:
49 PPS_CAPTURECLEAR
, NULL
);
51 dev_dbg(pps
->dev
, "PPS %s at %lu\n",
52 status
? "assert" : "clear", jiffies
);
55 static int (*alias_n_tty_open
)(struct tty_struct
*tty
);
57 static int pps_tty_open(struct tty_struct
*tty
)
59 struct pps_source_info info
;
60 struct tty_driver
*drv
= tty
->driver
;
61 int index
= tty
->index
+ drv
->name_base
;
62 struct pps_device
*pps
;
65 info
.owner
= THIS_MODULE
;
67 snprintf(info
.name
, PPS_MAX_NAME_LEN
, "%s%d", drv
->driver_name
, index
);
68 snprintf(info
.path
, PPS_MAX_NAME_LEN
, "/dev/%s%d", drv
->name
, index
);
69 info
.mode
= PPS_CAPTUREBOTH
| \
70 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
| \
71 PPS_CANWAIT
| PPS_TSFMT_TSPEC
;
73 pps
= pps_register_source(&info
, PPS_CAPTUREBOTH
| \
74 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
);
76 pr_err("cannot register PPS source \"%s\"\n", info
.path
);
79 pps
->lookup_cookie
= tty
;
81 /* Now open the base class N_TTY ldisc */
82 ret
= alias_n_tty_open(tty
);
84 pr_err("cannot open tty ldisc \"%s\"\n", info
.path
);
88 dev_info(pps
->dev
, "source \"%s\" added\n", info
.path
);
93 pps_unregister_source(pps
);
97 static void (*alias_n_tty_close
)(struct tty_struct
*tty
);
99 static void pps_tty_close(struct tty_struct
*tty
)
101 struct pps_device
*pps
= pps_lookup_dev(tty
);
103 alias_n_tty_close(tty
);
108 dev_info(pps
->dev
, "removed\n");
109 pps_unregister_source(pps
);
112 static struct tty_ldisc_ops pps_ldisc_ops
;
118 static int __init
pps_tty_init(void)
122 /* Inherit the N_TTY's ops */
123 n_tty_inherit_ops(&pps_ldisc_ops
);
125 /* Save N_TTY's open()/close() methods */
126 alias_n_tty_open
= pps_ldisc_ops
.open
;
127 alias_n_tty_close
= pps_ldisc_ops
.close
;
129 /* Init PPS_TTY data */
130 pps_ldisc_ops
.owner
= THIS_MODULE
;
131 pps_ldisc_ops
.magic
= PPS_TTY_MAGIC
;
132 pps_ldisc_ops
.name
= "pps_tty";
133 pps_ldisc_ops
.dcd_change
= pps_tty_dcd_change
;
134 pps_ldisc_ops
.open
= pps_tty_open
;
135 pps_ldisc_ops
.close
= pps_tty_close
;
137 err
= tty_register_ldisc(N_PPS
, &pps_ldisc_ops
);
139 pr_err("can't register PPS line discipline\n");
141 pr_info("PPS line discipline registered\n");
146 static void __exit
pps_tty_cleanup(void)
150 err
= tty_unregister_ldisc(N_PPS
);
152 pr_err("can't unregister PPS line discipline\n");
154 pr_info("PPS line discipline removed\n");
157 module_init(pps_tty_init
);
158 module_exit(pps_tty_cleanup
);
160 MODULE_ALIAS_LDISC(N_PPS
);
161 MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
162 MODULE_DESCRIPTION("PPS TTY device driver");
163 MODULE_LICENSE("GPL");