Browse Source

roba di 4 anni fa uncommitted non so nemmeno cosa sia

encrypt 3 years ago
parent
commit
c357630395

+ 2 - 0
dev-hello/.tmp_versions/hello.mod

@@ -0,0 +1,2 @@
+/home/marco/projects/kernel-playground/dev-hello/hello.ko
+/home/marco/projects/kernel-playground/dev-hello/hello.o

+ 38 - 0
dev-hello/hello.c

@@ -0,0 +1,38 @@
+#include <linux/init.h>            // Macros used to mark up functions e.g., __init __exit
+#include <linux/module.h>           // Core header for loading LKMs into the kernel
+#include <linux/kernel.h>           // Contains types, macros, functions for the kernel
+
+MODULE_LICENSE("GPL");              ///< The license type -- this affects runtime behavior
+MODULE_AUTHOR("Derek Molloy");      ///< The author -- visible when you use modinfo
+MODULE_DESCRIPTION("A simple Linux driver for the BBB.");  ///< The description -- see modinfo
+MODULE_VERSION("0.1");              ///< The version of the module
+
+static char *name = "world";        ///< An example LKM argument -- default value is "world"
+module_param(name, charp, S_IRUGO); ///< Param desc. charp = char ptr, S_IRUGO can be read/not changed
+MODULE_PARM_DESC(name, "The name to display in /var/log/kern.log");  ///< parameter description
+
+/** @brief The LKM initialization function
+ *  The static keyword restricts the visibility of the function to within this C file. The __init
+ *  macro means that for a built-in driver (not a LKM) the function is only used at initialization
+ *  time and that it can be discarded and its memory freed up after that point.
+ *  @return returns 0 if successful
+ */
+static int __init helloBBB_init(void){
+  printk(KERN_INFO "EBB: Hello %s from the BBB LKM!\n", name);
+  return 0;
+}
+
+/** @brief The LKM cleanup function
+ *  Similar to the initialization function, it is static. The __exit macro notifies that if this
+ *  code is used for a built-in driver (not a LKM) that this function is not required.
+ */
+static void __exit helloBBB_exit(void){
+  printk(KERN_INFO "EBB: Goodbye %s from the BBB LKM!\n", name);
+}
+
+/** @brief A module must use the module_init() module_exit() macros from linux/init.h, which
+ *  identify the initialization function at insertion time and the cleanup function (as
+ *  listed above)
+ */
+module_init(helloBBB_init);
+module_exit(helloBBB_exit);

+ 3 - 0
dummy-gpio/.tmp_versions/dummy-gpio.mod

@@ -0,0 +1,3 @@
+/home/marco/projects/kernel-playground/dummy-gpio/dummy-gpio.ko
+/home/marco/projects/kernel-playground/dummy-gpio/dummy-gpio.o
+

+ 74 - 0
dummy-gpio/dummy-gpio.c

@@ -0,0 +1,74 @@
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+struct dummy_gpio {
+  struct gpio_chip gc;
+  struct mutex lock;
+};
+
+
+static struct dummy_gpio dgpio;
+  
+static int dummy_gpio_direction_input(struct gpio_chip *chip, unsigned offset) {
+  printk("direction input\n");
+  return 0;
+}
+
+static void dummy_gpio_set(struct gpio_chip *chip, unsigned offset, int value) {
+  printk("set\n");
+}
+
+static int dummy_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) {
+  printk("output\n");
+  return 0;
+}
+
+static int dummy_gpio_get(struct gpio_chip *chip, unsigned offset) {
+  printk("get\n");
+  return 0;
+}
+
+static int __init dummy_gpio_init(void) {
+  int err;
+
+  mutex_init(&dgpio.lock);
+  dgpio.gc.label = "dummy_gpio";
+  dgpio.gc.direction_input = dummy_gpio_direction_input;
+  dgpio.gc.get = dummy_gpio_get;
+  dgpio.gc.direction_output = dummy_gpio_direction_output;
+  dgpio.gc.set = dummy_gpio_set;
+
+  dgpio.gc.ngpio = 8;
+  dgpio.gc.can_sleep = true;
+  // from 4.5 
+  dgpio.gc.parent = NULL;
+  // dgpio.gc.dev = NULL;
+  dgpio.gc.owner = THIS_MODULE;
+
+  //  gpiochip_add_data from 4.5
+  err = gpiochip_add(&dgpio.gc);
+  //err = gpiochip_add_data()
+  if(err)
+    goto exit_destroy;
+  return err;
+ exit_destroy:
+  mutex_destroy(&dgpio.lock);
+  return err;
+}
+
+static void __exit dummy_gpio_exit(void) {
+  gpiochip_remove(&dgpio.gc);
+  mutex_destroy(&dgpio.lock);
+  printk("dummy_gpio: Cya in another life\n");
+}
+
+module_init(dummy_gpio_init);
+module_exit(dummy_gpio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("encrypt";)
+MODULE_DESCRIPTION("Dummy gpiochip");

+ 262 - 0
iio/axp288_adc.c

@@ -0,0 +1,262 @@
+/*
+ * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/platform_device.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+#define AXP288_ADC_EN_MASK		0xF1
+#define AXP288_ADC_TS_PIN_GPADC		0xF2
+#define AXP288_ADC_TS_PIN_ON		0xF3
+
+enum axp288_adc_id {
+	AXP288_ADC_TS,
+	AXP288_ADC_PMIC,
+	AXP288_ADC_GP,
+	AXP288_ADC_BATT_CHRG_I,
+	AXP288_ADC_BATT_DISCHRG_I,
+	AXP288_ADC_BATT_V,
+	AXP288_ADC_NR_CHAN,
+};
+
+struct axp288_adc_info {
+	int irq;
+	struct regmap *regmap;
+};
+
+static const struct iio_chan_spec const axp288_adc_channels[] = {
+	{
+		.indexed = 1,
+		.type = IIO_TEMP,
+		.channel = 0,
+		.address = AXP288_TS_ADC_H,
+		.datasheet_name = "TS_PIN",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	}, {
+		.indexed = 1,
+		.type = IIO_TEMP,
+		.channel = 1,
+		.address = AXP288_PMIC_ADC_H,
+		.datasheet_name = "PMIC_TEMP",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	}, {
+		.indexed = 1,
+		.type = IIO_TEMP,
+		.channel = 2,
+		.address = AXP288_GP_ADC_H,
+		.datasheet_name = "GPADC",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	}, {
+		.indexed = 1,
+		.type = IIO_CURRENT,
+		.channel = 3,
+		.address = AXP20X_BATT_CHRG_I_H,
+		.datasheet_name = "BATT_CHG_I",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	}, {
+		.indexed = 1,
+		.type = IIO_CURRENT,
+		.channel = 4,
+		.address = AXP20X_BATT_DISCHRG_I_H,
+		.datasheet_name = "BATT_DISCHRG_I",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	}, {
+		.indexed = 1,
+		.type = IIO_VOLTAGE,
+		.channel = 5,
+		.address = AXP20X_BATT_V_H,
+		.datasheet_name = "BATT_V",
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	},
+};
+
+#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_
+		       name,					\
+		_consumer_channel)				\
+	{							\
+		.adc_channel_label = _adc_channel_label,	\
+		.consumer_dev_name = _consumer_dev_name,	\
+		.consumer_channel = _consumer_channel,		\
+	}
+
+/* for consumer drivers */
+static struct iio_map axp288_adc_default_maps[] = {
+	AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
+	AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
+	AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
+	AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
+	AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
+	AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
+	{},
+};
+
+static int axp288_adc_read_channel(int *val, unsigned long address,
+				struct regmap *regmap)
+{
+	u8 buf[2];
+
+	if (regmap_bulk_read(regmap, address, buf, 2))
+		return -EIO;
+	*val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
+
+	return IIO_VAL_INT;
+}
+
+static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
+				unsigned long address)
+{
+	/* channels other than GPADC do not need to switch TS pin */
+	if (address != AXP288_GP_ADC_H)
+		return 0;
+
+	return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
+}
+
+static int axp288_adc_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *chan,
+			int *val, int *val2, long mask)
+{
+	int ret;
+	struct axp288_adc_info *info = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
+					chan->address)) {
+			dev_err(&indio_dev->dev, "GPADC mode\n");
+			ret = -EINVAL;
+			break;
+		}
+		ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+		if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
+						chan->address))
+			dev_err(&indio_dev->dev, "TS pin restore\n");
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static int axp288_adc_set_state(struct regmap *regmap)
+{
+	/* ADC should be always enabled for internal FG to function */
+	if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
+		return -EIO;
+
+	return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
+}
+
+static const struct iio_info axp288_adc_iio_info = {
+	.read_raw = &axp288_adc_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int axp288_adc_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct axp288_adc_info *info;
+	struct iio_dev *indio_dev;
+	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	info = iio_priv(indio_dev);
+	info->irq = platform_get_irq(pdev, 0);
+	if (info->irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		return info->irq;
+	}
+	platform_set_drvdata(pdev, indio_dev);
+	info->regmap = axp20x->regmap;
+	/*
+	 * Set ADC to enabled state at all time, including system suspend.
+	 * otherwise internal fuel gauge functionality may be affected.
+	 */
+	ret = axp288_adc_set_state(axp20x->regmap);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable ADC device\n");
+		return ret;
+	}
+
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->name = pdev->name;
+	indio_dev->channels = axp288_adc_channels;
+	indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
+	indio_dev->info = &axp288_adc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to register iio device\n");
+		goto err_array_unregister;
+	}
+	return 0;
+
+err_array_unregister:
+	iio_map_array_unregister(indio_dev);
+
+	return ret;
+}
+
+static int axp288_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	iio_device_unregister(indio_dev);
+	iio_map_array_unregister(indio_dev);
+
+	return 0;
+}
+
+static const struct platform_device_id axp288_adc_id_table[] = {
+	{ .name = "axp288_adc" },
+	{},
+};
+
+static struct platform_driver axp288_adc_driver = {
+	.probe = axp288_adc_probe,
+	.remove = axp288_adc_remove,
+	.id_table = axp288_adc_id_table,
+	.driver = {
+		.name = "axp288_adc",
+	},
+};
+
+MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
+
+module_platform_driver(axp288_adc_driver);
+
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
+MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
+MODULE_LICENSE("GPL");

+ 3 - 0
iio/dummy-adc/.tmp_versions/dummy-adc.mod

@@ -0,0 +1,3 @@
+/home/marco/projects/kernel-playground/iio/dummy-adc/dummy-adc.ko
+/home/marco/projects/kernel-playground/iio/dummy-adc/dummy-adc.o
+

+ 417 - 0
iio/dummy-adc/mcp320x.c

@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
+ * Copyright (C) 2014 Rose Technology
+ * 	   Allan Bendorff Jensen <abj@rosetechnology.dk>
+ *	   Soren Andersen <san@rosetechnology.dk>
+ *
+ * Driver for following ADC chips from Microchip Technology's:
+ * 10 Bit converter
+ * MCP3001
+ * MCP3002
+ * MCP3004
+ * MCP3008
+ * ------------
+ * 12 bit converter
+ * MCP3201
+ * MCP3202
+ * MCP3204
+ * MCP3208
+ * ------------
+ *
+ * Datasheet can be found here:
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf  mcp3001
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf  mcp3002
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf  mcp3004/08
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf  mcp3201
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf  mcp3202
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf  mcp3204/08
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf  mcp3301
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+
+enum {
+	mcp3001,
+	mcp3002,
+	mcp3004,
+	mcp3008,
+	mcp3201,
+	mcp3202,
+	mcp3204,
+	mcp3208,
+	mcp3301,
+};
+
+struct mcp320x_chip_info {
+	const struct iio_chan_spec *channels;
+	unsigned int num_channels;
+	unsigned int resolution;
+};
+
+struct mcp320x {
+	struct spi_device *spi;
+	struct spi_message msg;
+	struct spi_transfer transfer[2];
+
+	struct regulator *reg;
+	struct mutex lock;
+	const struct mcp320x_chip_info *chip_info;
+
+	u8 tx_buf ____cacheline_aligned;
+	u8 rx_buf[2];
+};
+
+static int mcp320x_channel_to_tx_data(int device_index,
+			const unsigned int channel, bool differential)
+{
+	int start_bit = 1;
+
+	switch (device_index) {
+	case mcp3001:
+	case mcp3201:
+	case mcp3301:
+		return 0;
+	case mcp3002:
+	case mcp3202:
+		return ((start_bit << 4) | (!differential << 3) |
+							(channel << 2));
+	case mcp3004:
+	case mcp3204:
+	case mcp3008:
+	case mcp3208:
+		return ((start_bit << 6) | (!differential << 5) |
+							(channel << 2));
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
+				  bool differential, int device_index)
+{
+	int ret;
+
+	adc->rx_buf[0] = 0;
+	adc->rx_buf[1] = 0;
+	adc->tx_buf = mcp320x_channel_to_tx_data(device_index,
+						channel, differential);
+
+	if (device_index != mcp3001 && device_index != mcp3201 && device_index != mcp3301) {
+		ret = spi_sync(adc->spi, &adc->msg);
+		if (ret < 0)
+			return ret;
+	} else {
+		ret = spi_read(adc->spi, &adc->rx_buf, sizeof(adc->rx_buf));
+		if (ret < 0)
+			return ret;
+	}
+
+	switch (device_index) {
+	case mcp3001:
+		return (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+	case mcp3002:
+	case mcp3004:
+	case mcp3008:
+		return (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+	case mcp3201:
+		return (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+	case mcp3202:
+	case mcp3204:
+	case mcp3208:
+		return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+	case mcp3301:
+		return sign_extend32((adc->rx_buf[0] & 0x1f) << 8 | adc->rx_buf[1], 12);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mcp320x_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *channel, int *val,
+			    int *val2, long mask)
+{
+	struct mcp320x *adc = iio_priv(indio_dev);
+	int ret = -EINVAL;
+	int device_index = 0;
+
+	mutex_lock(&adc->lock);
+
+	device_index = spi_get_device_id(adc->spi)->driver_data;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = mcp320x_adc_conversion(adc, channel->address,
+			channel->differential, device_index);
+
+		if (ret < 0)
+			goto out;
+
+		*val = ret;
+		ret = IIO_VAL_INT;
+		break;
+
+	case IIO_CHAN_INFO_SCALE:
+		ret = regulator_get_voltage(adc->reg);
+		if (ret < 0)
+			goto out;
+
+		/* convert regulator output voltage to mV */
+		*val = ret / 1000;
+		*val2 = adc->chip_info->resolution;
+		ret = IIO_VAL_FRACTIONAL_LOG2;
+		break;
+	}
+
+out:
+	mutex_unlock(&adc->lock);
+
+	return ret;
+}
+
+#define MCP320X_VOLTAGE_CHANNEL(num)				\
+	{							\
+		.type = IIO_VOLTAGE,				\
+		.indexed = 1,					\
+		.channel = (num),				\
+		.address = (num),				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+	}
+
+#define MCP320X_VOLTAGE_CHANNEL_DIFF(num)			\
+	{							\
+		.type = IIO_VOLTAGE,				\
+		.indexed = 1,					\
+		.channel = (num * 2),				\
+		.channel2 = (num * 2 + 1),			\
+		.address = (num * 2),				\
+		.differential = 1,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+	}
+
+static const struct iio_chan_spec mcp3201_channels[] = {
+	MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+};
+
+static const struct iio_chan_spec mcp3202_channels[] = {
+	MCP320X_VOLTAGE_CHANNEL(0),
+	MCP320X_VOLTAGE_CHANNEL(1),
+	MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+};
+
+static const struct iio_chan_spec mcp3204_channels[] = {
+	MCP320X_VOLTAGE_CHANNEL(0),
+	MCP320X_VOLTAGE_CHANNEL(1),
+	MCP320X_VOLTAGE_CHANNEL(2),
+	MCP320X_VOLTAGE_CHANNEL(3),
+	MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+	MCP320X_VOLTAGE_CHANNEL_DIFF(1),
+};
+
+static const struct iio_chan_spec mcp3208_channels[] = {
+	MCP320X_VOLTAGE_CHANNEL(0),
+	MCP320X_VOLTAGE_CHANNEL(1),
+	MCP320X_VOLTAGE_CHANNEL(2),
+	MCP320X_VOLTAGE_CHANNEL(3),
+	MCP320X_VOLTAGE_CHANNEL(4),
+	MCP320X_VOLTAGE_CHANNEL(5),
+	MCP320X_VOLTAGE_CHANNEL(6),
+	MCP320X_VOLTAGE_CHANNEL(7),
+	MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+	MCP320X_VOLTAGE_CHANNEL_DIFF(1),
+	MCP320X_VOLTAGE_CHANNEL_DIFF(2),
+	MCP320X_VOLTAGE_CHANNEL_DIFF(3),
+};
+
+static const struct iio_info mcp320x_info = {
+	.read_raw = mcp320x_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
+	[mcp3001] = {
+		.channels = mcp3201_channels,
+		.num_channels = ARRAY_SIZE(mcp3201_channels),
+		.resolution = 10
+	},
+	[mcp3002] = {
+		.channels = mcp3202_channels,
+		.num_channels = ARRAY_SIZE(mcp3202_channels),
+		.resolution = 10
+	},
+	[mcp3004] = {
+		.channels = mcp3204_channels,
+		.num_channels = ARRAY_SIZE(mcp3204_channels),
+		.resolution = 10
+	},
+	[mcp3008] = {
+		.channels = mcp3208_channels,
+		.num_channels = ARRAY_SIZE(mcp3208_channels),
+		.resolution = 10
+	},
+	[mcp3201] = {
+		.channels = mcp3201_channels,
+		.num_channels = ARRAY_SIZE(mcp3201_channels),
+		.resolution = 12
+	},
+	[mcp3202] = {
+		.channels = mcp3202_channels,
+		.num_channels = ARRAY_SIZE(mcp3202_channels),
+		.resolution = 12
+	},
+	[mcp3204] = {
+		.channels = mcp3204_channels,
+		.num_channels = ARRAY_SIZE(mcp3204_channels),
+		.resolution = 12
+	},
+	[mcp3208] = {
+		.channels = mcp3208_channels,
+		.num_channels = ARRAY_SIZE(mcp3208_channels),
+		.resolution = 12
+	},
+	[mcp3301] = {
+		.channels = mcp3201_channels,
+		.num_channels = ARRAY_SIZE(mcp3201_channels),
+		.resolution = 13
+	},
+};
+
+static int mcp320x_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct mcp320x *adc;
+	const struct mcp320x_chip_info *chip_info;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	adc = iio_priv(indio_dev);
+	adc->spi = spi;
+
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &mcp320x_info;
+
+	chip_info = &mcp320x_chip_infos[spi_get_device_id(spi)->driver_data];
+	indio_dev->channels = chip_info->channels;
+	indio_dev->num_channels = chip_info->num_channels;
+
+	adc->chip_info = chip_info;
+
+	adc->transfer[0].tx_buf = &adc->tx_buf;
+	adc->transfer[0].len = sizeof(adc->tx_buf);
+	adc->transfer[1].rx_buf = adc->rx_buf;
+	adc->transfer[1].len = sizeof(adc->rx_buf);
+
+	spi_message_init_with_transfers(&adc->msg, adc->transfer,
+					ARRAY_SIZE(adc->transfer));
+
+	adc->reg = devm_regulator_get(&spi->dev, "vref");
+	if (IS_ERR(adc->reg))
+		return PTR_ERR(adc->reg);
+
+	ret = regulator_enable(adc->reg);
+	if (ret < 0)
+		return ret;
+
+	mutex_init(&adc->lock);
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto reg_disable;
+
+	return 0;
+
+reg_disable:
+	regulator_disable(adc->reg);
+
+	return ret;
+}
+
+static int mcp320x_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct mcp320x *adc = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	regulator_disable(adc->reg);
+
+	return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id mcp320x_dt_ids[] = {
+	{
+		.compatible = "mcp3001",
+		.data = &mcp320x_chip_infos[mcp3001],
+	}, {
+		.compatible = "mcp3002",
+		.data = &mcp320x_chip_infos[mcp3002],
+	}, {
+		.compatible = "mcp3004",
+		.data = &mcp320x_chip_infos[mcp3004],
+	}, {
+		.compatible = "mcp3008",
+		.data = &mcp320x_chip_infos[mcp3008],
+	}, {
+		.compatible = "mcp3201",
+		.data = &mcp320x_chip_infos[mcp3201],
+	}, {
+		.compatible = "mcp3202",
+		.data = &mcp320x_chip_infos[mcp3202],
+	}, {
+		.compatible = "mcp3204",
+		.data = &mcp320x_chip_infos[mcp3204],
+	}, {
+		.compatible = "mcp3208",
+		.data = &mcp320x_chip_infos[mcp3208],
+	}, {
+		.compatible = "mcp3301",
+		.data = &mcp320x_chip_infos[mcp3301],
+	}, {
+	}
+};
+MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
+#endif
+
+static const struct spi_device_id mcp320x_id[] = {
+	{ "mcp3001", mcp3001 },
+	{ "mcp3002", mcp3002 },
+	{ "mcp3004", mcp3004 },
+	{ "mcp3008", mcp3008 },
+	{ "mcp3201", mcp3201 },
+	{ "mcp3202", mcp3202 },
+	{ "mcp3204", mcp3204 },
+	{ "mcp3208", mcp3208 },
+	{ "mcp3301", mcp3301 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, mcp320x_id);
+
+static struct spi_driver mcp320x_driver = {
+	.driver = {
+		.name = "mcp320x",
+		.of_match_table = of_match_ptr(mcp320x_dt_ids),
+	},
+	.probe = mcp320x_probe,
+	.remove = mcp320x_remove,
+	.id_table = mcp320x_id,
+};
+module_spi_driver(mcp320x_driver);
+
+MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
+MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08");
+MODULE_LICENSE("GPL v2");

+ 3 - 0
iio/dummy-dac/.tmp_versions/dummy-dac.mod

@@ -0,0 +1,3 @@
+/home/marco/projects/kernel-playground/iio/dummy-dac/dummy-dac.ko
+/home/marco/projects/kernel-playground/iio/dummy-dac/dummy-dac.o
+

+ 141 - 0
iio/dummy-dac/dummy-dac.c

@@ -0,0 +1,141 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/sw_device.h>
+
+static struct config_item_type iio_dummy_dac_type = {
+  .ct_owner = THIS_MODULE,
+};
+
+struct iio_dummy_dac_state {
+  int dac_val;
+  struct mutex lock;
+};
+
+static const struct iio_chan_spec iio_dummy_dac_channels[] = {
+  {
+    .type = IIO_VOLTAGE,
+    .output = 1,
+    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+  },
+};
+
+static int dummy_dac_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2, long mask)
+{
+  struct iio_dummy_dac_state *st = iio_priv(indio_dev);
+  mutex_lock(&st->lock);
+  *val = st->dac_val;
+  mutex_unlock(&st->lock);
+  return IIO_VAL_INT;
+}
+
+static int dummy_dac_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val, int val2, long mask)
+{
+  
+  struct iio_dummy_dac_state *st = iio_priv(indio_dev);
+  
+  switch(mask) {
+  case IIO_CHAN_INFO_RAW:
+    if(val < 0 || val > 255)
+      return -EINVAL;
+
+    mutex_lock(&st->lock);
+    st->dac_val = val;
+    printk(KERN_INFO "dummy_dac: %i\n",val);
+    mutex_unlock(&st->lock);
+    return 0;    
+  }
+  return -EINVAL;
+}
+
+static const struct iio_info iio_dummy_dac_info = {
+  .read_raw = &dummy_dac_read_raw,
+  .write_raw = &dummy_dac_write_raw,
+  .driver_module = THIS_MODULE,
+};
+
+static struct iio_sw_device *iio_dummy_dac_probe(const char *name)
+{
+  int ret;
+  struct iio_dev *indio_dev;
+  struct iio_dummy_dac_state *st;
+  struct iio_sw_device *swd;
+
+  printk(KERN_INFO "HIMODA\n");
+  swd = kzalloc(sizeof(*swd), GFP_KERNEL);
+  if(!swd) {
+    ret = -ENOMEM;
+    goto error_kzalloc;
+  }
+
+  indio_dev = iio_device_alloc(sizeof(*st));
+  if(!indio_dev) {
+    ret = -ENOMEM;
+    goto error_ret;
+  }
+  st = iio_priv(indio_dev);
+  mutex_init(&st->lock);
+  st->dac_val = 0;
+  swd->device = indio_dev;
+
+  indio_dev->name = kstrdup(name, GFP_KERNEL);
+
+  indio_dev->channels  = iio_dummy_dac_channels;
+  indio_dev->num_channels = ARRAY_SIZE(iio_dummy_dac_channels);
+
+  indio_dev->info = &iio_dummy_dac_info;
+
+  indio_dev->modes = INDIO_DIRECT_MODE;
+  
+  ret = iio_device_register(indio_dev);
+  
+  if(ret < 0)
+    goto error_free_device;
+
+  iio_swd_group_init_type_name(swd, name, &iio_dummy_dac_type);
+
+  return swd;
+ error_free_device:
+  iio_device_free(indio_dev);
+ error_ret:
+  kfree(swd);      
+ error_kzalloc:
+  return ERR_PTR(ret);
+}
+
+static int iio_dummy_dac_remove(struct iio_sw_device *swd)
+{
+  struct iio_dev *indio_dev = swd->device;
+
+  iio_device_unregister(indio_dev);
+
+  kfree(indio_dev->name);
+  iio_device_free(indio_dev);
+
+  return 0;
+}
+
+static const struct iio_sw_device_ops iio_dummy_dac_ops =  {
+  .probe = iio_dummy_dac_probe,
+  .remove = iio_dummy_dac_remove,
+};
+
+
+static struct iio_sw_device_type iio_dummy_dac = {
+  .name = "dummy_dac",
+  .owner = THIS_MODULE,
+  .ops = &iio_dummy_dac_ops,
+};
+
+module_iio_sw_device_driver(iio_dummy_dac);
+
+MODULE_AUTHOR("encrypt <encrypt@labr.xyz>");
+MODULE_DESCRIPTION("IIO DAC dummy driver");
+MODULE_LICENSE("GPL v3");

+ 4 - 0
iio/dummy-dac/errors.log

@@ -0,0 +1,4 @@
+/home/marco/projects/kernel-playground/iio/dummy-dac/dummy-dac.c: In function 'iio_dummy_dac_probe':
+/home/marco/projects/kernel-playground/iio/dummy-dac/dummy-dac.c:100:2: warning: label 'error_ret' defined but not used [-Wunused-label]
+  error_ret:
+  ^

+ 2 - 0
usb-test/.tmp_versions/usb-test.mod

@@ -0,0 +1,2 @@
+/home/marco/projects/kernel-playground/usb-test/usb-test.ko
+/home/marco/projects/kernel-playground/usb-test/usb-test.o

+ 44 - 0
usb-test/usb-test-hid.c

@@ -0,0 +1,44 @@
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/hid.h>
+#include <linux/hidraw.h>
+
+#define USB_VENDOR_ID_MICROCHIP         0x04d8
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("encrypt");
+MODULE_DESCRIPTION("Dummy led");
+
+
+static int usbio_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+  u8 buf[5] = {0x80, 0x80 };
+  printk("Yo");
+  hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+  hid_hw_raw_request(hdev, buf[0], buf, 2, HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+  return 0;
+}
+
+static void usbio_remove(struct hid_device *hdev)
+{
+  
+}
+
+static const struct hid_device_id usbio_table[] = {
+  { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, 0x0003) },
+  {}
+};
+
+MODULE_DEVICE_TABLE(hid, usbio_table);
+
+static struct hid_driver usbio_driver = {
+  .name = "usbio",
+  .probe = usbio_probe,
+  .remove = usbio_remove,
+  .id_table = usbio_table
+};
+
+module_hid_driver(usbio_driver);

+ 95 - 0
usb-test/usb-test.c

@@ -0,0 +1,95 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+
+#define USB_VENDOR_ID_MICROCHIP 0x04d8
+#define USB_PRODUCT_ID_USBIO    0x003f
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("encrypt");
+MODULE_DESCRIPTION("Dummy led");
+
+struct usbio_led {
+  struct usb_device *udev;
+  int status;
+};
+
+static struct usb_device_id usbio_id_table[] = {
+  { USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_PRODUCT_ID_USBIO) },
+  {}
+};
+
+MODULE_DEVICE_TABLE(usb, usbio_id_table);
+
+static int usbio_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+  struct usb_device *udev = interface_to_usbdev(interface);
+  struct usbio_led *dev = NULL;
+  int retval = -ENOMEM;
+  int actual_len = 1;
+  u8 data[] = { 0x80 };
+
+  printk("SCONTRI");
+  dev = kmalloc(sizeof(struct usbio_led), GFP_KERNEL);
+  if(dev == NULL) {
+    dev_err(&interface->dev, "Out of memory\n");
+    goto error;
+  }
+  memset(dev, 0x00, sizeof(*dev));
+  dev->udev = usb_get_dev(udev);
+  usb_set_intfdata(interface, dev);
+  dev_info(&interface->dev, "USBIO LED device attacched\n");
+  usb_interrupt_msg(dev->udev,
+		    usb_sndintpipe(dev->udev, 1),
+		    data, sizeof(data), &actual_len, USB_CTRL_SET_TIMEOUT);
+  /* usb_interrupt_msg(dev->udev, */
+  /* 		    usb_sndintpipe(dev->udev, 0x80), */
+  /* 		    NULL, sizeof(NULL), &actual_len, USB_CTRL_SET_TIMEOUT); */
+
+  return 0;
+  
+ error:
+  kfree(dev);
+  return retval;
+  
+}
+
+static void usbio_disconnect(struct usb_interface *interface)
+{
+  struct usbio_led *dev;
+  dev = usb_get_intfdata(interface);
+  usb_set_intfdata(interface, NULL);
+  usb_put_dev(dev->udev);
+  kfree(dev);
+  dev_info(&interface->dev, "USBIO disconnected\n");
+}
+
+static struct usb_driver usbio_driver = {
+  .name  = "usbio",
+  .probe = usbio_probe,
+  .disconnect = usbio_disconnect,
+  .id_table = usbio_id_table
+};
+
+//module_usb_driver(usbio_driver);
+
+static int __init usb_led_init(void)
+{
+  int retval = 0;
+  
+  retval = usb_register(&usbio_driver);
+  if (retval)
+    printk("usb_register failed. Error number %d", retval);
+  return retval;
+}
+
+static void __exit usb_led_exit(void)
+{
+  usb_deregister(&usbio_driver);
+}
+
+module_init (usb_led_init);
+module_exit (usb_led_exit);

+ 43 - 0
usb-test/usb-test2.c

@@ -0,0 +1,43 @@
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/hid.h>
+#include <linux/hidraw.h>
+
+#define USB_VENDOR_ID_MICROCHIP         0x04d8
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("encrypt");
+MODULE_DESCRIPTION("Dummy led");
+
+
+static int usbio_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+  u8 buf[5] = {0x80, 0x80 };
+  hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+  hid_hw_raw_request(hdev, buf[0], buf, 2, HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+  return 0;
+}
+
+static void usbio_remove(struct hid_device *hdev)
+{
+  hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id usbio_table[] = {
+  { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, 0x0003) },
+  {}
+};
+
+MODULE_DEVICE_TABLE(hid, usbio_table);
+
+static struct hid_driver usbio_driver = {
+  .name = "usbio",
+  .probe = usbio_probe,
+  .remove = usbio_remove,
+  .id_table = usbio_table
+};
+
+module_hid_driver(usbio_driver);