2019-05-20 09:19:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-03-10 15:23:47 -08:00
/*
* pps - ldisc . c - - PPS line discipline
*
* Copyright ( C ) 2008 Rodolfo Giometti < giometti @ linux . it >
*/
2011-01-12 17:00:52 -08:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-03-10 15:23:47 -08:00
# include <linux/module.h>
# include <linux/serial_core.h>
# include <linux/tty.h>
# include <linux/pps_kernel.h>
2013-02-10 04:43:41 -05:00
# include <linux/bug.h>
2010-03-10 15:23:47 -08:00
# define PPS_TTY_MAGIC 0x0001
2013-02-12 02:00:43 -05:00
static void pps_tty_dcd_change ( struct tty_struct * tty , unsigned int status )
2010-03-10 15:23:47 -08:00
{
2013-02-12 02:00:43 -05:00
struct pps_device * pps ;
struct pps_event_time ts ;
pps_get_ts ( & ts ) ;
2010-03-10 15:23:47 -08:00
2013-02-12 02:00:43 -05:00
pps = pps_lookup_dev ( tty ) ;
2013-02-10 04:43:41 -05:00
/*
* This should never fail , but the ldisc locking is very
* convoluted , so don ' t crash just in case .
*/
if ( WARN_ON_ONCE ( pps = = NULL ) )
return ;
2011-01-12 17:00:51 -08:00
2010-03-10 15:23:47 -08:00
/* Now do the PPS event report */
2013-02-12 02:00:43 -05:00
pps_event ( pps , & ts , status ? PPS_CAPTUREASSERT :
2011-01-12 17:00:51 -08:00
PPS_CAPTURECLEAR , NULL ) ;
2010-03-10 15:23:47 -08:00
2011-01-12 17:00:51 -08:00
dev_dbg ( pps - > dev , " PPS %s at %lu \n " ,
status ? " assert " : " clear " , jiffies ) ;
2010-03-10 15:23:47 -08:00
}
static int ( * alias_n_tty_open ) ( struct tty_struct * tty ) ;
static int pps_tty_open ( struct tty_struct * tty )
{
struct pps_source_info info ;
struct tty_driver * drv = tty - > driver ;
int index = tty - > index + drv - > name_base ;
2011-01-12 17:00:51 -08:00
struct pps_device * pps ;
2010-03-10 15:23:47 -08:00
int ret ;
info . owner = THIS_MODULE ;
info . dev = NULL ;
snprintf ( info . name , PPS_MAX_NAME_LEN , " %s%d " , drv - > driver_name , index ) ;
snprintf ( info . path , PPS_MAX_NAME_LEN , " /dev/%s%d " , drv - > name , index ) ;
info . mode = PPS_CAPTUREBOTH | \
PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
PPS_CANWAIT | PPS_TSFMT_TSPEC ;
2011-01-12 17:00:51 -08:00
pps = pps_register_source ( & info , PPS_CAPTUREBOTH | \
2010-03-10 15:23:47 -08:00
PPS_OFFSETASSERT | PPS_OFFSETCLEAR ) ;
2018-11-26 18:24:22 +08:00
if ( IS_ERR ( pps ) ) {
2010-03-10 15:23:47 -08:00
pr_err ( " cannot register PPS source \" %s \" \n " , info . path ) ;
2018-11-26 18:24:22 +08:00
return PTR_ERR ( pps ) ;
2010-03-10 15:23:47 -08:00
}
2013-02-10 04:41:56 -05:00
pps - > lookup_cookie = tty ;
2010-03-10 15:23:47 -08:00
2013-02-10 04:41:56 -05:00
/* Now open the base class N_TTY ldisc */
2010-03-10 15:23:47 -08:00
ret = alias_n_tty_open ( tty ) ;
2011-01-12 17:00:51 -08:00
if ( ret < 0 ) {
pr_err ( " cannot open tty ldisc \" %s \" \n " , info . path ) ;
goto err_unregister ;
}
2010-03-10 15:23:47 -08:00
2011-01-12 17:00:51 -08:00
dev_info ( pps - > dev , " source \" %s \" added \n " , info . path ) ;
2010-03-10 15:23:47 -08:00
return 0 ;
2011-01-12 17:00:51 -08:00
err_unregister :
pps_unregister_source ( pps ) ;
return ret ;
2010-03-10 15:23:47 -08:00
}
static void ( * alias_n_tty_close ) ( struct tty_struct * tty ) ;
static void pps_tty_close ( struct tty_struct * tty )
{
2013-02-10 04:41:56 -05:00
struct pps_device * pps = pps_lookup_dev ( tty ) ;
2010-03-10 15:23:47 -08:00
alias_n_tty_close ( tty ) ;
2013-02-10 04:43:41 -05:00
if ( WARN_ON ( ! pps ) )
return ;
2011-01-12 17:00:51 -08:00
dev_info ( pps - > dev , " removed \n " ) ;
pps_unregister_source ( pps ) ;
2010-03-10 15:23:47 -08:00
}
static struct tty_ldisc_ops pps_ldisc_ops ;
/*
* Module stuff
*/
static int __init pps_tty_init ( void )
{
int err ;
/* Inherit the N_TTY's ops */
n_tty_inherit_ops ( & pps_ldisc_ops ) ;
/* Save N_TTY's open()/close() methods */
alias_n_tty_open = pps_ldisc_ops . open ;
alias_n_tty_close = pps_ldisc_ops . close ;
/* Init PPS_TTY data */
pps_ldisc_ops . owner = THIS_MODULE ;
pps_ldisc_ops . magic = PPS_TTY_MAGIC ;
pps_ldisc_ops . name = " pps_tty " ;
pps_ldisc_ops . dcd_change = pps_tty_dcd_change ;
pps_ldisc_ops . open = pps_tty_open ;
pps_ldisc_ops . close = pps_tty_close ;
err = tty_register_ldisc ( N_PPS , & pps_ldisc_ops ) ;
if ( err )
pr_err ( " can't register PPS line discipline \n " ) ;
else
pr_info ( " PPS line discipline registered \n " ) ;
return err ;
}
static void __exit pps_tty_cleanup ( void )
{
int err ;
err = tty_unregister_ldisc ( N_PPS ) ;
if ( err )
pr_err ( " can't unregister PPS line discipline \n " ) ;
else
pr_info ( " PPS line discipline removed \n " ) ;
}
module_init ( pps_tty_init ) ;
module_exit ( pps_tty_cleanup ) ;
MODULE_ALIAS_LDISC ( N_PPS ) ;
MODULE_AUTHOR ( " Rodolfo Giometti <giometti@linux.it> " ) ;
MODULE_DESCRIPTION ( " PPS TTY device driver " ) ;
MODULE_LICENSE ( " GPL " ) ;