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>
29 #define PPS_TTY_MAGIC 0x0001
31 static void pps_tty_dcd_change(struct tty_struct
*tty
, unsigned int status
,
32 struct pps_event_time
*ts
)
34 struct pps_device
*pps
= (struct pps_device
*)tty
->disc_data
;
38 /* Now do the PPS event report */
39 pps_event(pps
, ts
, status
? PPS_CAPTUREASSERT
:
40 PPS_CAPTURECLEAR
, NULL
);
42 dev_dbg(pps
->dev
, "PPS %s at %lu\n",
43 status
? "assert" : "clear", jiffies
);
46 static int (*alias_n_tty_open
)(struct tty_struct
*tty
);
48 static int pps_tty_open(struct tty_struct
*tty
)
50 struct pps_source_info info
;
51 struct tty_driver
*drv
= tty
->driver
;
52 int index
= tty
->index
+ drv
->name_base
;
53 struct pps_device
*pps
;
56 info
.owner
= THIS_MODULE
;
58 snprintf(info
.name
, PPS_MAX_NAME_LEN
, "%s%d", drv
->driver_name
, index
);
59 snprintf(info
.path
, PPS_MAX_NAME_LEN
, "/dev/%s%d", drv
->name
, index
);
60 info
.mode
= PPS_CAPTUREBOTH
| \
61 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
| \
62 PPS_CANWAIT
| PPS_TSFMT_TSPEC
;
64 pps
= pps_register_source(&info
, PPS_CAPTUREBOTH
| \
65 PPS_OFFSETASSERT
| PPS_OFFSETCLEAR
);
67 pr_err("cannot register PPS source \"%s\"\n", info
.path
);
72 /* Should open N_TTY ldisc too */
73 ret
= alias_n_tty_open(tty
);
75 pr_err("cannot open tty ldisc \"%s\"\n", info
.path
);
79 dev_info(pps
->dev
, "source \"%s\" added\n", info
.path
);
84 tty
->disc_data
= NULL
;
85 pps_unregister_source(pps
);
89 static void (*alias_n_tty_close
)(struct tty_struct
*tty
);
91 static void pps_tty_close(struct tty_struct
*tty
)
93 struct pps_device
*pps
= (struct pps_device
*)tty
->disc_data
;
95 alias_n_tty_close(tty
);
97 tty
->disc_data
= NULL
;
98 dev_info(pps
->dev
, "removed\n");
99 pps_unregister_source(pps
);
102 static struct tty_ldisc_ops pps_ldisc_ops
;
108 static int __init
pps_tty_init(void)
112 /* Inherit the N_TTY's ops */
113 n_tty_inherit_ops(&pps_ldisc_ops
);
115 /* Save N_TTY's open()/close() methods */
116 alias_n_tty_open
= pps_ldisc_ops
.open
;
117 alias_n_tty_close
= pps_ldisc_ops
.close
;
119 /* Init PPS_TTY data */
120 pps_ldisc_ops
.owner
= THIS_MODULE
;
121 pps_ldisc_ops
.magic
= PPS_TTY_MAGIC
;
122 pps_ldisc_ops
.name
= "pps_tty";
123 pps_ldisc_ops
.dcd_change
= pps_tty_dcd_change
;
124 pps_ldisc_ops
.open
= pps_tty_open
;
125 pps_ldisc_ops
.close
= pps_tty_close
;
127 err
= tty_register_ldisc(N_PPS
, &pps_ldisc_ops
);
129 pr_err("can't register PPS line discipline\n");
131 pr_info("PPS line discipline registered\n");
136 static void __exit
pps_tty_cleanup(void)
140 err
= tty_unregister_ldisc(N_PPS
);
142 pr_err("can't unregister PPS line discipline\n");
144 pr_info("PPS line discipline removed\n");
147 module_init(pps_tty_init
);
148 module_exit(pps_tty_cleanup
);
150 MODULE_ALIAS_LDISC(N_PPS
);
151 MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
152 MODULE_DESCRIPTION("PPS TTY device driver");
153 MODULE_LICENSE("GPL");