lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <8447d6730901080149l4146139an9fad00fd88d72fb9@mail.gmail.com>
Date:	Thu, 8 Jan 2009 10:49:12 +0100
From:	"Davide Rizzo" <elpa.rizzo@...il.com>
To:	linux-kernel@...r.kernel.org, hjk@...utronix.de, gregkh@...e.de,
	ben-linux@...ff.org
Subject: [PATCH 1/3] Driver for user access to internal clocks

This driver is for user level programs to interact with system clocks.
It allows to read and modify rates and parents, using virtual files.
It requires the implementation of 2 additional functions in the clk interface:
 clk_for_each() and clk_name().
Actually I implemented that functions only for Samsung S3C24xx platform.

Signed-off-by: Davide Rizzo <elpa-rizzo@...il.com>
---
diff -urNp linux-2.6.28/drivers/uio/clock.c
linux-2.6.28.elpa/drivers/uio/clock.c
--- linux-2.6.28/drivers/uio/clock.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.28.elpa/drivers/uio/clock.c	2009-01-07 18:52:09.000000000 +0100
@@ -0,0 +1,313 @@
+/*
+ driver/misc/clock.c
+
+ Written Feb 2008 by Davide Rizzo <davide@...a.it>
+
+ This driver allows to read and modify internal clocks' rates using
+  virtual files. User can also read and modify parents.
+
+ This driver requires implementation of clk_name() and clk_enum() functions
+  in architecture specific clock.c
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+struct clk_info {
+	struct mutex mutex;
+	struct list_head head;
+	struct device *dev;	/* here because needed by create_clock_attr */
+	int count;
+};
+
+struct attr {
+	struct list_head list;
+	struct device_attribute at;
+};
+
+static ssize_t clk_show(struct device *dev, struct device_attribute *attr,
+	char *buffer)
+{
+	unsigned long rate;
+	struct clk_info *info = dev_get_drvdata(dev);
+	struct clk *clk = clk_get(dev, attr->attr.name);
+
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	rate = 0;
+
+	if (!IS_ERR(clk)) {
+
+		mutex_lock(&info->mutex);
+
+		rate = clk_get_rate(clk);
+
+		mutex_unlock(&info->mutex);
+
+		clk_put(clk);
+
+	}
+	if (snprintf(buffer, PAGE_SIZE, "%ld\n", rate) > PAGE_SIZE)
+		buffer[PAGE_SIZE - 1] = '\0';
+	return strlen(buffer);
+}
+
+static ssize_t clk_store(struct device *dev, struct device_attribute *attr,
+	const char *buffer, size_t count)
+{
+	struct clk_info *info = dev_get_drvdata(dev);
+	unsigned long rate;
+	struct clk *clk;
+	int err = strict_strtoul(buffer, 10, &rate);
+
+	if (err)
+		return err;
+
+	clk = clk_get(dev, attr->attr.name);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	mutex_lock(&info->mutex);
+
+	if (rate != 0) {
+		clk_set_rate(clk, clk_round_rate(clk, rate));
+		clk_enable(clk);
+	} else
+		clk_disable(clk);
+
+	mutex_unlock(&info->mutex);
+
+	clk_put(clk);
+
+	return count;
+}
+
+static ssize_t clk_parent_show(struct device *dev,
+	struct device_attribute *attr, char *buffer)
+{
+	struct clk_info *info = dev_get_drvdata(dev);
+	struct clk *clk, *parent;
+	char *s = kstrdup(attr->attr.name, GFP_KERNEL);
+	if (!s)
+		return -ENOMEM;
+	s[strlen(attr->attr.name) - 7] = '\0';
+	clk = clk_get(dev, s);
+	kfree(s);
+	buffer[0] = '\0';
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+	if (!IS_ERR(clk)) {
+
+		mutex_lock(&info->mutex);
+
+		parent = clk_get_parent(clk);
+		if (parent && !IS_ERR(parent)) {
+			const char *full_name = clk_get_name(parent);
+			if (IS_ERR(full_name)) {
+				mutex_unlock(&info->mutex);
+				return PTR_ERR(full_name);
+			}
+			strlcpy(buffer, full_name, PAGE_SIZE);
+			kfree(full_name);
+		}
+		strlcat(buffer, "\n", PAGE_SIZE);
+
+		mutex_unlock(&info->mutex);
+
+		clk_put(clk);
+	}
+	return strlen(buffer);
+}
+
+static ssize_t clk_parent_store(struct device *dev,
+	struct device_attribute *attr, const char *buffer, size_t count)
+{
+	struct clk_info *info = dev_get_drvdata(dev);
+	struct clk *clk, *parent;
+	char *s;
+
+	parent = clk_get(dev, buffer);
+	if (IS_ERR(parent))
+		return PTR_ERR(parent);
+
+	s = kstrdup(attr->attr.name, GFP_KERNEL);
+	if (!s)
+		return -ENOMEM;
+	s[strlen(attr->attr.name) - 7] = '\0';
+	clk = clk_get(dev, s);
+	kfree(s);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	if (!IS_ERR(clk)) {
+
+		mutex_lock(&info->mutex);
+
+		clk_set_parent(clk, parent);
+
+		mutex_unlock(&info->mutex);
+
+		clk_put(clk);
+	}
+	clk_put(parent);
+	return count;
+}
+
+static int remove_driver(struct platform_device *pdev)
+{
+	struct clk_info *info = platform_get_drvdata(pdev);
+	struct attr *atp;
+
+	mutex_lock(&info->mutex);
+
+	/* Remove all virtual files */
+	while (!list_empty(&info->head)) {
+		atp = list_first_entry(&info->head, struct attr, list);
+		device_remove_file(&pdev->dev, &atp->at);
+		kfree(atp->at.attr.name);
+		list_del(&atp->list);
+		kfree(atp);
+	}
+	platform_set_drvdata(pdev, NULL);
+
+	mutex_unlock(&info->mutex);
+
+	kfree(info);
+	dev_notice(&pdev->dev, "removed\n");
+	return 0;
+}
+
+static int create_clock_attr(struct clk *clk, void *data)
+{
+	struct attr *atp;
+	int err, len;
+	struct clk_info *info = data;
+	const char *name = clk_get_name(clk);
+
+	/* Retrieve clock's name */
+	if (name == NULL || IS_ERR(name)) {
+		/* Don't return error, simply skip this one */
+		dev_err(info->dev, "invalid clock's name\n");
+		return PTR_ERR(name);
+	}
+
+	/* Create rate virtual file */
+	atp = kzalloc(sizeof(struct attr), GFP_KERNEL);
+	if (!atp) {
+		dev_err(info->dev, "out of kernel memory\n");
+		return -ENOMEM;
+	}
+	atp->at.attr.mode = S_IRUGO | S_IWUSR;
+	atp->at.attr.name = kstrdup(name, GFP_KERNEL);
+	if (!atp->at.attr.name) {
+		kfree(atp);
+		dev_err(info->dev, "out of kernel memory\n");
+		return -ENOMEM;
+	}
+	atp->at.show = clk_show;
+	atp->at.store = clk_store;
+	err = device_create_file(info->dev, &atp->at);
+	if (err) {
+		kfree(atp->at.attr.name);
+		kfree(atp);
+		dev_err(info->dev, "failed to create file\n");
+		return err;
+	}
+	list_add(&atp->list, &info->head);
+
+	/* Create parent virtual file */
+	atp = kzalloc(sizeof(struct attr), GFP_KERNEL);
+	if (!atp) {
+		dev_err(info->dev, "out of kernel memory\n");
+		return -ENOMEM;
+	}
+	atp->at.attr.mode = S_IRUGO | S_IWUSR;
+	len = strlen(name) + 8;
+	atp->at.attr.name = kmalloc(len, GFP_KERNEL);
+	if (!atp->at.attr.name) {
+		kfree(atp);
+		dev_err(info->dev, "out of kernel memory\n");
+		return -ENOMEM;
+	}
+	strlcpy((char *)atp->at.attr.name, name, len);
+	strlcat((char *)atp->at.attr.name, "-parent", len);
+	atp->at.show = clk_parent_show;
+	atp->at.store = clk_parent_store;
+	err = device_create_file(info->dev, &atp->at);
+	if (err) {
+		kfree(atp->at.attr.name);
+		kfree(atp);
+		dev_err(info->dev, "failed to create file\n");
+		return err;
+	}
+	list_add(&atp->list, &info->head);
+
+	info->count++;
+	return 0;
+}
+
+static int probe_driver(struct platform_device *pdev)
+{
+	struct clk_info *info;
+	int err;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		dev_err(&pdev->dev, "out of kernel memory\n");
+		return -ENOMEM;
+	}
+	mutex_init(&info->mutex);
+	INIT_LIST_HEAD(&info->head);
+	info->dev = &pdev->dev;
+	platform_set_drvdata(pdev, info);
+	err = clk_for_each(create_clock_attr, info);
+	if (err)
+		remove_driver(pdev);
+	else
+		dev_notice(&pdev->dev, "probed OK - %d clocks recognized\n",
+			info->count);
+	return err;
+}
+
+static struct platform_driver driver = {
+	.probe  = probe_driver,
+	.remove = remove_driver,
+	.driver = {
+		.name = "clocks",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init clk_init_module(void)
+{
+	return platform_driver_register(&driver);
+}
+
+static void __exit clk_cleanup_module(void)
+{
+	platform_driver_unregister(&driver);
+}
+
+module_init(clk_init_module);
+module_exit(clk_cleanup_module);
+
+MODULE_AUTHOR("Davide Rizzo <davide@...a.it>");
+MODULE_DESCRIPTION("Clock driver");
+MODULE_SUPPORTED_DEVICE("clocks");
+MODULE_LICENSE("GPL");
diff -urNp linux-2.6.28/drivers/uio/Kconfig
linux-2.6.28.elpa/drivers/uio/Kconfig
--- linux-2.6.28/drivers/uio/Kconfig	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28.elpa/drivers/uio/Kconfig	2009-01-07 18:58:10.000000000 +0100
@@ -13,6 +13,18 @@ menuconfig UIO

 if UIO

+
+config UIO_CLOCK
+	tristate "General purpose clock driver"
+	default y
+	---help---
+	  This driver allows to configure and control internal clocks'
+	   rates and parents through virtual files.
+	  This driver requires implementation of some additional functions
+	   in architecture specific low-level drivers:
+	   clk_for_each() and clk_get_name()
+	  Currently they're implemented only on Samsung S3C24xx platforms.
+
 config UIO_CIF
 	tristate "generic Hilscher CIF Card driver"
 	depends on PCI
diff -urNp linux-2.6.28/drivers/uio/Makefile
linux-2.6.28.elpa/drivers/uio/Makefile
--- linux-2.6.28/drivers/uio/Makefile	2008-12-25 00:26:37.000000000 +0100
+++ linux-2.6.28.elpa/drivers/uio/Makefile	2009-01-06 18:20:17.000000000 +0100
@@ -1,4 +1,5 @@
 obj-$(CONFIG_UIO)	+= uio.o
+obj-$(CONFIG_UIO_CLOCK) += clock.o
 obj-$(CONFIG_UIO_CIF)	+= uio_cif.o
 obj-$(CONFIG_UIO_PDRV)	+= uio_pdrv.o
 obj-$(CONFIG_UIO_PDRV_GENIRQ)	+= uio_pdrv_genirq.o
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ