SOC: Zedboard: Driver question

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We are working on a school project in which we are trying to develop a
audio mixer
on Zedboard (Development board from Digilent). We have developed the IP and have
integrated it with the overall hardware using Programmable logic. This
board has ARM
core. We have a Digilent pre-configured Linux source which we cross-compiled
for ARM board, device tree blob and bootloader for Zync(BOOT.BIN). The system
boots fine with Linux, but now to expose the recently added hardware
implementation
of audio mixer, we are trying to develop the driver using the platform
driver API.
Currently, In our reconfigurable hardware, we have 2 channels and a mixer and we
want to access those individually as some file nodes under /proc FS. The sample
code is shown below:

<snip from myiir.c>
/* device match table to match with device node in device tree
 * These are the list of devices that we want to expose as platform device
 */
static const struct of_device_id myiir_of_match[] __devinitconst = {
        {.compatible = "dglnt,myiir-audio-ch0"},
        {.compatible = "dglnt,myiir-audio-ch1"},
        {.compatible = "dglnt,myiir-audio-mix0"},
        {},
};

MODULE_DEVICE_TABLE(of, myiir_of_match);

/* platform driver structure for myiir driver */
static struct platform_driver myiir_driver = {
        .driver = {
                .name = DRIVER_NAME,
                .owner = THIS_MODULE,
                .of_match_table = myiir_of_match},
        .probe = myiir_probe,
        .remove = __devexit_p(myiir_remove),
        .shutdown = __devexit_p(myiir_shutdown)
};

/* Register myiir platform driver */
module_platform_driver(myiir_driver);
<myiir.c>

Now, inside the probe routine (myiir_probe), can we create proc
entries by calling
create_proc for each of these nodes and setting the appropriate read and write
methods(file_operations) ?

<snip from the myiir_probe>
struct proc_dir_entry *myiir_proc_entry[3];

myiir_proc_entry[0] = proc_create("myiir-audio-ch0", 0, NULL,
                &proc_myiir_ch0_operations);

myiir_proc_entry[1] = proc_create("myiir-audio-ch1", 0, NULL,
                &proc_myiir_ch1_operations);

myiir_proc_entry[2] = proc_create("myiir-audio-mix0", 0, NULL,
                &proc_myiir_mix0_operations);

<snip from the myiir_probe>

While browsing the Internet, we found some sample driver code, which we are
also using as a template. I've attached the driver that is based on
the same template.

<snip from device tree file>
myiir-aud-ch0 {
            compatible = "dglnt,myiir-audio-ch0";
            reg = <0x74200000 0x10000>;
        };
        myiir-aud-ch1 {
            compatible = "dglnt,myiir-audio-ch1";
            reg = <0x74220000 0x10000>;
        };
        myiir-aud-mix0 {
            compatible = "dglnt,myiir-audio-mix0";
            reg = <0x68600000 0x10000>;
        };
<snip from device tree file>

The driver is far from complete, but as of now the compilation woks fine.
<snip>
user@fpga4v:~/tutorial/IIRdriver$ make ARCH=arm
CROSS_COMPILE=arm-xilinx-linux-gnueabi-
make -C ../linux-digilent-3.6-digilent-13.01/
M=/home/user/tutorial/IIRdriver modules
make[1]: Entering directory
`/home/user/tutorial/linux-digilent-3.6-digilent-13.01'
  CC [M]  /home/user/tutorial/IIRdriver/myiir.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/user/tutorial/IIRdriver/myiir.mod.o
  LD [M]  /home/user/tutorial/IIRdriver/myiir.ko
make[1]: Leaving directory
`/home/user/tutorial/linux-digilent-3.6-digilent-13.01'
<snip>

Thanks,
Kumar
#include <linux/kernel.h> 
#include <linux/module.h>
#include <asm/uaccess.h> 		/*Needed for copy_from_user */
#include <asm/io.h>	 		/*Needed for IO Read/Write Functions */
#include <linux/proc_fs.h>		/*Needed for Proc File System Functions */
#include <linux/seq_file.h>		/*Needed for Sequence File Operations */
#include <linux/platform_device.h>	/*Needed for Platform Driver Functions */

/* Define Driver Name */
#define DRIVER_NAME "myiir"

unsigned long *base_addr;	/* Vitual Base Address */
struct resource *res;		/* Device Resource Structure */
unsigned long remap_size;	/* Device Memory Size */

/* Write operation for /proc/myiir
* -----------------------------------
* When user cat a string to /proc/myiir file, the string will be stored in
* const char __user *buf. This function will copy the string from user
* space into kernel space, and change it to an unsigned long value.
* It will then write the value to the register of myiir controller,
* and turn on the corresponding LEDs eventually.
*/

static ssize_t proc_myiir_write(struct file *file, const char __user * buf,
	size_t count, loff_t * ppos)
{
	char myiir_phrase[16];
	u32 myiir_value;

	if (count < 11) {
		if (copy_from_user(myiir_phrase, buf, count))
			return -EFAULT;

		myiir_phrase[count] = '\0';
	}

	myiir_value = simple_strtoul(myiir_phrase, NULL, 0);
	wmb();
	iowrite32(myiir_value, base_addr);
	return count;
}

/* Callback function when opening file /proc/myiir
* ------------------------------------------------------
* Read the register value of myiir controller, print the value to
* the sequence file struct seq_file *p. In file open operation for /proc/myiir
* this callback function will be called first to fill up the seq_file,
* and seq_read function will print whatever in seq_file to the terminal.
*/
static int proc_myiir_show(struct seq_file *p, void *v)
{
	u32 myiir_value;
	myiir_value = ioread32(base_addr);
	seq_printf(p, "0x%x", myiir_value);
	return 0;
}

/* Open function for /proc/myiir
* ------------------------------------
* When user want to read /proc/myiir (i.e. cat /proc/myiir), the open function
* will be called first. In the open function, a seq_file will be prepared and the
* status of myiir will be filled into the seq_file by proc_myiir_show function.
*/

static int proc_myiir_open(struct inode *inode, struct file *file)
{
	unsigned int size = 16;
	char *buf;
	struct seq_file *m;
	int res;

	buf = (char *)kmalloc(size * sizeof(char), GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	res = single_open(file, proc_myiir_show, NULL);

	if (!res) {
		m = file->private_data;
		m->buf = buf;
		m->size = size;
	} else {
		kfree(buf);
	}

	return res;
}

/* File Operations for /proc/myiir */
static const struct file_operations proc_myiir_ch0_operations = {
	.open = proc_myiir_open,
	.read = seq_read,
	.write = proc_myiir_write,
	.llseek = seq_lseek,
	.release = single_release
};

static const struct file_operations proc_myiir_ch1_operations = {
	.read = seq_read,
	.write = proc_myiir_write,
};

static const struct file_operations proc_myiir_mix0_operations = {
	.read = seq_read,
	.write = proc_myiir_write,
};
/* Shutdown function for myiir
* -----------------------------------
* Before myiir shutdown, turn-off all the leds
*/
static void myiir_shutdown(struct platform_device *pdev)
{
	iowrite32(0, base_addr);
}

/* Remove function for myiir
* ----------------------------------
* When myiir module is removed, turn off all the leds first,
* release virtual address and the memory region requested.
*/
static int myiir_remove(struct platform_device *pdev)
{
	myiir_shutdown(pdev);

	/* Remove /proc/myiir entry */
	remove_proc_entry("myiir-audio-ch0", NULL);
	remove_proc_entry("myiir-audio-ch1", NULL);
	remove_proc_entry("myiir-audio-mix0", NULL);

	/* Release mapped virtual address */
	iounmap(base_addr);

	/* Release the region */
	release_mem_region(res->start, remap_size);

	return 0;
}

/* Device Probe function for myiir
* ------------------------------------
* Get the resource structure from the information in device tree.
* request the memory region needed for the controller, and map it into
* kernel virtual memory space. Create an entry under /proc file system
* and register file operations for that entry.
*/
static int __devinit myiir_probe(struct platform_device *pdev)
{
	struct proc_dir_entry *myiir_proc_entry[3];
	int ret = 0;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "No memory resource\n");
		return -ENODEV;
	}
	remap_size = res->end - res->start + 1;

	if (!request_mem_region(res->start, remap_size, pdev->name)) {
		dev_err(&pdev->dev, "Cannot request IO\n");
		return -ENXIO;
	}

	base_addr = ioremap(res->start, remap_size);
	if (base_addr == NULL) {
		dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n",
		(unsigned long)res->start);
		ret = -ENOMEM;
		goto err_release_region;
	}

  	/*
	 * XXX: Seperate device nodes for channels and mixer
	 */
	myiir_proc_entry[0] = proc_create("myiir-audio-ch0", 0, NULL,
		&proc_myiir_ch0_operations);

	if (myiir_proc_entry[0] == NULL) {
		dev_err(&pdev->dev, "Couldn't create proc entry\n");
		ret = -ENOMEM;
		goto err_create_proc_entry;
	}

	myiir_proc_entry[1] = proc_create("myiir-audio-ch1", 0, NULL,
		&proc_myiir_ch1_operations);

	if (myiir_proc_entry[1] == NULL) {
		dev_err(&pdev->dev, "Couldn't create proc entry\n");
		ret = -ENOMEM;
		goto err_create_proc_entry;
	}

	myiir_proc_entry[2] = proc_create("myiir-audio-mix0", 0, NULL,
		&proc_myiir_mix0_operations);

	if (myiir_proc_entry[2] == NULL) {
		dev_err(&pdev->dev, "Couldn't create proc entry\n");
		ret = -ENOMEM;
		goto err_create_proc_entry;
	}

	printk(KERN_INFO DRIVER_NAME " probed at VA 0x%08lx\n",
		(unsigned long) base_addr);

	return 0;

	err_create_proc_entry:
		iounmap(base_addr);
	err_release_region:
		release_mem_region(res->start, remap_size);

	return ret;
}

/* device match table to match with device node in device tree
 * These are the list of devices that we want to expose as platform device
 */
static const struct of_device_id myiir_of_match[] __devinitconst = {
	{.compatible = "dglnt,myiir-audio-ch0"},
	{.compatible = "dglnt,myiir-audio-ch1"},
	{.compatible = "dglnt,myiir-audio-mix0"},
	{},
};

MODULE_DEVICE_TABLE(of, myiir_of_match);

/* platform driver structure for myiir driver */
static struct platform_driver myiir_driver = {
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
		.of_match_table = myiir_of_match},
	.probe = myiir_probe,
	.remove = __devexit_p(myiir_remove),
	.shutdown = __devexit_p(myiir_shutdown)
};

/* Register myiir platform driver */
module_platform_driver(myiir_driver);

/* Module Infomations */
MODULE_LICENSE("GPL");
MODULE_ALIAS(DRIVER_NAME);
_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@xxxxxxxxxxxxxxxxx
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux