Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e196d34 authored by Alexander Gordeev's avatar Alexander Gordeev Committed by Linus Torvalds
Browse files

pps: access pps device by direct pointer



Using device index as a pointer needs some unnecessary work to be done
every time the pointer is needed (in irq handler for example).  Using a
direct pointer is much more easy (and safe as well).

Signed-off-by: default avatarAlexander Gordeev <lasaine@lvk.cs.msu.su>
Acked-by: default avatarRodolfo Giometti <giometti@linux.it>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 6f4229b5
Loading
Loading
Loading
Loading
+13 −17
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@
 * Global variables
 */

static int source;
static struct pps_device *pps;
static struct timer_list ktimer;

/*
@@ -47,7 +47,7 @@ static void pps_ktimer_event(unsigned long ptr)

	pr_info("PPS event at %lu\n", jiffies);

	pps_event(source, &ts, PPS_CAPTUREASSERT, NULL);
	pps_event(pps, &ts, PPS_CAPTUREASSERT, NULL);

	mod_timer(&ktimer, jiffies + HZ);
}
@@ -56,12 +56,11 @@ static void pps_ktimer_event(unsigned long ptr)
 * The echo function
 */

static void pps_ktimer_echo(int source, int event, void *data)
static void pps_ktimer_echo(struct pps_device *pps, int event, void *data)
{
	pr_info("echo %s %s for source %d\n",
	dev_info(pps->dev, "echo %s %s\n",
		event & PPS_CAPTUREASSERT ? "assert" : "",
		event & PPS_CAPTURECLEAR ? "clear" : "",
		source);
		event & PPS_CAPTURECLEAR ? "clear" : "");
}

/*
@@ -84,28 +83,25 @@ static struct pps_source_info pps_ktimer_info = {

static void __exit pps_ktimer_exit(void)
{
	del_timer_sync(&ktimer);
	pps_unregister_source(source);
	dev_info(pps->dev, "ktimer PPS source unregistered\n");

	pr_info("ktimer PPS source unregistered\n");
	del_timer_sync(&ktimer);
	pps_unregister_source(pps);
}

static int __init pps_ktimer_init(void)
{
	int ret;

	ret = pps_register_source(&pps_ktimer_info,
	pps = pps_register_source(&pps_ktimer_info,
				PPS_CAPTUREASSERT | PPS_OFFSETASSERT);
	if (ret < 0) {
	if (pps == NULL) {
		printk(KERN_ERR "cannot register ktimer source\n");
		return ret;
		return -ENOMEM;
	}
	source = ret;

	setup_timer(&ktimer, pps_ktimer_event, 0);
	mod_timer(&ktimer, jiffies + HZ);

	pr_info("ktimer PPS source registered at %d\n", source);
	dev_info(pps->dev, "ktimer PPS source registered\n");

	return 0;
}
+26 −15
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@
static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
				struct pps_event_time *ts)
{
	int id = (long)tty->disc_data;
	struct pps_device *pps = (struct pps_device *)tty->disc_data;
	struct pps_event_time __ts;

	/* First of all we get the time stamp... */
@@ -39,12 +39,14 @@ static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
	if (!ts)	/* No. Do it ourself! */
		ts = &__ts;

	BUG_ON(pps == NULL);

	/* Now do the PPS event report */
	pps_event(id, ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR,
			NULL);
	pps_event(pps, ts, status ? PPS_CAPTUREASSERT :
			PPS_CAPTURECLEAR, NULL);

	pr_debug("PPS %s at %lu on source #%d\n",
			status ? "assert" : "clear", jiffies, id);
	dev_dbg(pps->dev, "PPS %s at %lu\n",
			status ? "assert" : "clear", jiffies);
}

static int (*alias_n_tty_open)(struct tty_struct *tty);
@@ -54,6 +56,7 @@ 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;
	struct pps_device *pps;
	int ret;

	info.owner = THIS_MODULE;
@@ -64,34 +67,42 @@ static int pps_tty_open(struct tty_struct *tty)
			PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \
			PPS_CANWAIT | PPS_TSFMT_TSPEC;

	ret = pps_register_source(&info, PPS_CAPTUREBOTH | \
	pps = pps_register_source(&info, PPS_CAPTUREBOTH | \
				PPS_OFFSETASSERT | PPS_OFFSETCLEAR);
	if (ret < 0) {
	if (pps == NULL) {
		pr_err("cannot register PPS source \"%s\"\n", info.path);
		return ret;
		return -ENOMEM;
	}
	tty->disc_data = (void *)(long)ret;
	tty->disc_data = pps;

	/* Should open N_TTY ldisc too */
	ret = alias_n_tty_open(tty);
	if (ret < 0)
		pps_unregister_source((long)tty->disc_data);
	if (ret < 0) {
		pr_err("cannot open tty ldisc \"%s\"\n", info.path);
		goto err_unregister;
	}

	pr_info("PPS source #%d \"%s\" added\n", ret, info.path);
	dev_info(pps->dev, "source \"%s\" added\n", info.path);

	return 0;

err_unregister:
	tty->disc_data = NULL;
	pps_unregister_source(pps);
	return ret;
}

static void (*alias_n_tty_close)(struct tty_struct *tty);

static void pps_tty_close(struct tty_struct *tty)
{
	int id = (long)tty->disc_data;
	struct pps_device *pps = (struct pps_device *)tty->disc_data;

	pps_unregister_source(id);
	alias_n_tty_close(tty);

	pr_info("PPS source #%d removed\n", id);
	tty->disc_data = NULL;
	dev_info(pps->dev, "removed\n");
	pps_unregister_source(pps);
}

static struct tty_ldisc_ops pps_ldisc_ops;
+28 −96
Original line number Diff line number Diff line
@@ -32,11 +32,11 @@
#include <linux/slab.h>

/*
 * Global variables
 * Local variables
 */

DEFINE_SPINLOCK(pps_idr_lock);
DEFINE_IDR(pps_idr);
static DEFINE_SPINLOCK(pps_idr_lock);
static DEFINE_IDR(pps_idr);

/*
 * Local functions
@@ -60,60 +60,6 @@ static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset)
 * Exported functions
 */

/* pps_get_source - find a PPS source
 * @source: the PPS source ID.
 *
 * This function is used to find an already registered PPS source into the
 * system.
 *
 * The function returns NULL if found nothing, otherwise it returns a pointer
 * to the PPS source data struct (the refcounter is incremented by 1).
 */

struct pps_device *pps_get_source(int source)
{
	struct pps_device *pps;
	unsigned long flags;

	spin_lock_irqsave(&pps_idr_lock, flags);

	pps = idr_find(&pps_idr, source);
	if (pps != NULL)
		atomic_inc(&pps->usage);

	spin_unlock_irqrestore(&pps_idr_lock, flags);

	return pps;
}

/* pps_put_source - free the PPS source data
 * @pps: a pointer to the PPS source.
 *
 * This function is used to free a PPS data struct if its refcount is 0.
 */

void pps_put_source(struct pps_device *pps)
{
	unsigned long flags;

	spin_lock_irqsave(&pps_idr_lock, flags);
	BUG_ON(atomic_read(&pps->usage) == 0);

	if (!atomic_dec_and_test(&pps->usage)) {
		pps = NULL;
		goto exit;
	}

	/* No more reference to the PPS source. We can safely remove the
	 * PPS data struct.
	 */
	idr_remove(&pps_idr, pps->id);

exit:
	spin_unlock_irqrestore(&pps_idr_lock, flags);
	kfree(pps);
}

/* pps_register_source - add a PPS source in the system
 * @info: the PPS info struct
 * @default_params: the default PPS parameters of the new source
@@ -122,10 +68,11 @@ void pps_put_source(struct pps_device *pps)
 * source is described by info's fields and it will have, as default PPS
 * parameters, the ones specified into default_params.
 *
 * The function returns, in case of success, the PPS source ID.
 * The function returns, in case of success, the PPS device. Otherwise NULL.
 */

int pps_register_source(struct pps_source_info *info, int default_params)
struct pps_device *pps_register_source(struct pps_source_info *info,
		int default_params)
{
	struct pps_device *pps;
	int id;
@@ -168,7 +115,6 @@ int pps_register_source(struct pps_source_info *info, int default_params)

	init_waitqueue_head(&pps->queue);
	spin_lock_init(&pps->lock);
	atomic_set(&pps->usage, 1);

	/* Get new ID for the new PPS source */
	if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) {
@@ -211,7 +157,7 @@ int pps_register_source(struct pps_source_info *info, int default_params)

	pr_info("new PPS source %s at ID %d\n", info->name, id);

	return id;
	return pps;

free_idr:
	spin_lock_irq(&pps_idr_lock);
@@ -224,38 +170,33 @@ int pps_register_source(struct pps_source_info *info, int default_params)
pps_register_source_exit:
	printk(KERN_ERR "pps: %s: unable to register source\n", info->name);

	return err;
	return NULL;
}
EXPORT_SYMBOL(pps_register_source);

/* pps_unregister_source - remove a PPS source from the system
 * @source: the PPS source ID
 * @pps: the PPS source
 *
 * This function is used to remove a previously registered PPS source from
 * the system.
 */

void pps_unregister_source(int source)
void pps_unregister_source(struct pps_device *pps)
{
	struct pps_device *pps;
	unsigned int id = pps->id;

	spin_lock_irq(&pps_idr_lock);
	pps = idr_find(&pps_idr, source);
	pps_unregister_cdev(pps);

	if (!pps) {
		BUG();
		spin_unlock_irq(&pps_idr_lock);
		return;
	}
	spin_lock_irq(&pps_idr_lock);
	idr_remove(&pps_idr, pps->id);
	spin_unlock_irq(&pps_idr_lock);

	pps_unregister_cdev(pps);
	pps_put_source(pps);
	kfree(pps);
}
EXPORT_SYMBOL(pps_unregister_source);

/* pps_event - register a PPS event into the system
 * @source: the PPS source ID
 * @pps: the PPS device
 * @ts: the event timestamp
 * @event: the event type
 * @data: userdef pointer
@@ -263,30 +204,24 @@ EXPORT_SYMBOL(pps_unregister_source);
 * This function is used by each PPS client in order to register a new
 * PPS event into the system (it's usually called inside an IRQ handler).
 *
 * If an echo function is associated with the PPS source it will be called
 * If an echo function is associated with the PPS device it will be called
 * as:
 *	pps->info.echo(source, event, data);
 *	pps->info.echo(pps, event, data);
 */

void pps_event(int source, struct pps_event_time *ts, int event, void *data)
void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,
		void *data)
{
	struct pps_device *pps;
	unsigned long flags;
	int captured = 0;
	struct pps_ktime ts_real;

	if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) {
		printk(KERN_ERR "pps: unknown event (%x) for source %d\n",
			event, source);
		dev_err(pps->dev, "unknown event (%x)\n", event);
		return;
	}

	pps = pps_get_source(source);
	if (!pps)
		return;

	pr_debug("PPS event on source %d at %ld.%09ld\n",
			pps->id, ts->ts_real.tv_sec, ts->ts_real.tv_nsec);
	dev_dbg(pps->dev, "PPS event at %ld.%09ld\n",
			ts->ts_real.tv_sec, ts->ts_real.tv_nsec);

	timespec_to_pps_ktime(&ts_real, ts->ts_real);

@@ -294,7 +229,7 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data)

	/* Must call the echo function? */
	if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))
		pps->info.echo(source, event, data);
		pps->info.echo(pps, event, data);

	/* Check the event */
	pps->current_mode = pps->params.mode;
@@ -308,8 +243,8 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data)
		/* Save the time stamp */
		pps->assert_tu = ts_real;
		pps->assert_sequence++;
		pr_debug("capture assert seq #%u for source %d\n",
			pps->assert_sequence, source);
		dev_dbg(pps->dev, "capture assert seq #%u\n",
			pps->assert_sequence);

		captured = ~0;
	}
@@ -323,8 +258,8 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data)
		/* Save the time stamp */
		pps->clear_tu = ts_real;
		pps->clear_sequence++;
		pr_debug("capture clear seq #%u for source %d\n",
			pps->clear_sequence, source);
		dev_dbg(pps->dev, "capture clear seq #%u\n",
			pps->clear_sequence);

		captured = ~0;
	}
@@ -338,8 +273,5 @@ void pps_event(int source, struct pps_event_time *ts, int event, void *data)
	}

	spin_unlock_irqrestore(&pps->lock, flags);

	/* Now we can release the PPS source for (possible) deregistration */
	pps_put_source(pps);
}
EXPORT_SYMBOL(pps_event);
+6 −16
Original line number Diff line number Diff line
@@ -204,12 +204,6 @@ static int pps_cdev_open(struct inode *inode, struct file *file)
{
	struct pps_device *pps = container_of(inode->i_cdev,
						struct pps_device, cdev);
	int found;

	found = pps_get_source(pps->id) != 0;
	if (!found)
		return -ENODEV;

	file->private_data = pps;

	return 0;
@@ -217,11 +211,6 @@ static int pps_cdev_open(struct inode *inode, struct file *file)

static int pps_cdev_release(struct inode *inode, struct file *file)
{
	struct pps_device *pps = file->private_data;

	/* Free the PPS source and wake up (possible) deregistration */
	pps_put_source(pps);

	return 0;
}

@@ -242,22 +231,23 @@ static const struct file_operations pps_cdev_fops = {
int pps_register_cdev(struct pps_device *pps)
{
	int err;
	dev_t devt;

	devt = MKDEV(MAJOR(pps_devt), pps->id);

	pps->devno = MKDEV(MAJOR(pps_devt), pps->id);
	cdev_init(&pps->cdev, &pps_cdev_fops);
	pps->cdev.owner = pps->info.owner;

	err = cdev_add(&pps->cdev, pps->devno, 1);
	err = cdev_add(&pps->cdev, devt, 1);
	if (err) {
		printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n",
				pps->info.name, MAJOR(pps_devt), pps->id);
		return err;
	}
	pps->dev = device_create(pps_class, pps->info.dev, pps->devno, NULL,
	pps->dev = device_create(pps_class, pps->info.dev, devt, pps,
							"pps%d", pps->id);
	if (IS_ERR(pps->dev))
		goto del_cdev;
	dev_set_drvdata(pps->dev, pps);

	pr_debug("source %s got cdev (%d:%d)\n", pps->info.name,
			MAJOR(pps_devt), pps->id);
@@ -272,7 +262,7 @@ int pps_register_cdev(struct pps_device *pps)

void pps_unregister_cdev(struct pps_device *pps)
{
	device_destroy(pps_class, pps->devno);
	device_destroy(pps_class, pps->dev->devt);
	cdev_del(&pps->cdev);
}

+9 −14
Original line number Diff line number Diff line
@@ -31,13 +31,16 @@
 * Global defines
 */

struct pps_device;

/* The specific PPS source info */
struct pps_source_info {
	char name[PPS_MAX_NAME_LEN];		/* simbolic name */
	char path[PPS_MAX_NAME_LEN];		/* path of connected device */
	int mode;				/* PPS's allowed mode */

	void (*echo)(int source, int event, void *data); /* PPS echo function */
	void (*echo)(struct pps_device *pps,
			int event, void *data);	/* PPS echo function */

	struct module *owner;
	struct device *dev;
@@ -65,35 +68,27 @@ struct pps_device {
	unsigned int id;			/* PPS source unique ID */
	struct cdev cdev;
	struct device *dev;
	int devno;
	struct fasync_struct *async_queue;	/* fasync method */
	spinlock_t lock;

	atomic_t usage;				/* usage count */
};

/*
 * Global variables
 */

extern spinlock_t pps_idr_lock;
extern struct idr pps_idr;

extern struct device_attribute pps_attrs[];

/*
 * Exported functions
 */

struct pps_device *pps_get_source(int source);
extern void pps_put_source(struct pps_device *pps);
extern int pps_register_source(struct pps_source_info *info,
				int default_params);
extern void pps_unregister_source(int source);
extern struct pps_device *pps_register_source(
		struct pps_source_info *info, int default_params);
extern void pps_unregister_source(struct pps_device *pps);
extern int pps_register_cdev(struct pps_device *pps);
extern void pps_unregister_cdev(struct pps_device *pps);
extern void pps_event(int source, struct pps_event_time *ts, int event,
		void *data);
extern void pps_event(struct pps_device *pps,
		struct pps_event_time *ts, int event, void *data);

static inline void timespec_to_pps_ktime(struct pps_ktime *kt,
		struct timespec ts)