I'm working with a kernel driver for an I2C device and up until now I've been making simple attributes available using the sysfs DEVICE_ATTR helper. Now I need to make a long list of attributes available like /sys/bus/i2c/device/.../param0, .../param1, etc. but it seems inefficient to write a function for each one and maybe even the wrong use of the sysfs system. For example:
static DEVICE_ATTR(param0, S_IRUGO, NULL, foo_set_param0);
static DEVICE_ATTR(param1, S_IRUGO, NULL, foo_set_param1);
...
static DEVICE_ATTR(param50, S_IRUGO, NULL, foo_set_param50);
The values on the device change frequently and reading them is expensive so constantly reading them or using one function to read all of them is not really any option. I'm a bit of C newbie so maybe there is something totally obvious I'm missing, but can you use a wrapper on the sysfs show callback to take a parameter? Or is there a better system I should use for this? I looked at debugfs and it seems like I'd need to maintain the values in memory for it.
You can try container_of() macro. Simply fill your attribute data in a larger structure.
Here is an example for the creation of 100 attributes in a big structure big_kattr. The parameter is UNIT_NUM.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#define UNIT_NUM 100
typedef struct {
struct kobj_attribute k_obj;
int num;
} big_kattr;
static struct kobject *register_kobj;
// rw functions
static ssize_t __used store_value(struct kobject *kp, struct kobj_attribute *attr, const char *buf, size_t count){
big_kattr *a = container_of(attr, big_kattr, k_obj);
sscanf(buf, "%du", &a->num);
return count;
}
static ssize_t show_value(struct kobject *kp, struct kobj_attribute *attr, char *buf) {
big_kattr *a = container_of(attr, big_kattr, k_obj);
return sprintf(buf, "%d\n", a->num);
}
// put attribute to attribute group
static struct attribute * unit_attrs[UNIT_NUM + 1];
static big_kattr full_unit_attrs[UNIT_NUM];
static struct attribute_group unit_attr_group;
static int hello_init(void){
int i;
memset(full_unit_attrs, 0, sizeof(full_unit_attrs));
memset(unit_attrs, 0, sizeof(unit_attrs));
memset(&unit_attr_group, 0, sizeof(unit_attr_group));
for(i=0; i<UNIT_NUM; i++){
char * str = kmalloc(32, GFP_KERNEL);
sprintf(str, "unit-%03d",i);
full_unit_attrs[i].k_obj.attr.name = str;
full_unit_attrs[i].k_obj.attr.mode = S_IWUSR | S_IRUGO;
full_unit_attrs[i].k_obj.show = show_value;
full_unit_attrs[i].k_obj.store = store_value;
full_unit_attrs[i].num = i;
unit_attrs[i] = &(full_unit_attrs[i].k_obj.attr);
}
unit_attr_group.attrs = unit_attrs;
// create sysfs object ( /sys/kernel/many directory )
register_kobj = kobject_create_and_add("many", kernel_kobj);
if (!register_kobj)
return -ENOMEM;
//create all attributes (files)
if(sysfs_create_group(register_kobj, &unit_attr_group)){
kobject_put(register_kobj);
return -ENOMEM;
}
return 0;
}
static void hello_exit(void){
int i;
kobject_put(register_kobj);
for(i=0; i<UNIT_NUM; i++)
kfree(full_unit_attrs[i].k_obj.attr.name);
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(hello_init);
module_exit(hello_exit);
Example:
cat /sys/kernel/many/unit-077
echo 12345 > /sys/kernel/many/unit-088
cat /sys/kernel/many/unit-088
Related
I've been browsing through the Linux IOMMU code for quite a while now and couldn't find an easy approach to directly create an IOMMU entry.
I want to specify the physical address (maybe also the virtual but it is not necessary) and the device. The range should be inserted into the IOMMU and the virt address printed through printk.
I am searching for a function that lets me easily do it.
Thanks
I ended up with a pretty hacky solution, not the optimal one, but it worked for my usecase. Adjusted the function iommu_dma_map_page in dma-iommu.c to look like the following and export it.
(vanilla 5.18 except for this modification)
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
bool coherent = dev_is_dma_coherent(dev);
int prot = dma_info_to_prot(dir, coherent, attrs);
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = &cookie->iovad;
dma_addr_t iova, dma_mask = dma_get_mask(dev);
phys_addr_t phys;
if (page->flags == 0xF0F0F0F0F0F0F) {
phys = page->dma_addr;
} else {
phys = page_to_phys(page) + offset;
}
/*
* If both the physical buffer start address and size are
* page aligned, we don't need to use a bounce page.
*/
if (dev_use_swiotlb(dev) && iova_offset(iovad, phys | size)) {
void *padding_start;
size_t padding_size, aligned_size;
aligned_size = iova_align(iovad, size);
phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size,
iova_mask(iovad), dir, attrs);
if (phys == DMA_MAPPING_ERROR)
return DMA_MAPPING_ERROR;
/* Cleanup the padding area. */
padding_start = phys_to_virt(phys);
padding_size = aligned_size;
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) {
padding_start += size;
padding_size -= size;
}
memset(padding_start, 0, padding_size);
}
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
arch_sync_dma_for_device(phys, size, dir);
iova = __iommu_dma_map(dev, phys, size, prot, dma_mask);
if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
return iova;
}
EXPORT_SYMBOL(iommu_dma_map_page);
Then use the following kernel module to program the entry. This could be also extended and programmed in a more usable manner, but for prototyping, it should be enough.
#include <linux/init.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
extern dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs);
int magic_value = 0xF0F0F0F0F0F0F;
struct page page_ = {
.flags = 0xF0F0F0F0F0F0F,
.dma_addr = 0x0000002f000f0000,
};
static int my_init(void)
{
dma_addr_t dma_addr;
struct pci_dev *dummy = pci_get_device(0x10EE, 0x0666, NULL);
if (dummy != NULL)
{
printk(KERN_INFO "module loaded.\n");
dma_addr = iommu_dma_map_page(&(dummy->dev), &page_, 0, 4096, DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
printk(KERN_INFO "DMA_addr: %llx", dma_addr);
}
else
{
printk("Error getting device");
}
return 0;
}
static void my_exit(void)
{
printk(KERN_INFO "iommu_alloc unloaded.\n");
return;
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("benedict.schlueter#inf.ethz.ch");
MODULE_DESCRIPTION("Alloc IOMMU entry");
I am making one simple char driver and I learnt that there are 2 ways I can get Major number for my driver to pair with - alloc_chrdev_region(and register_chrdev_region) and register_chrdev. I initially started with register_chrdev and it gave me my major number and also created entry in /dev (class and device create used).
But when I change for register_chrdev to alloc_chrdev_region to acquire major number (using chrdev_init and chrdev_add), leaving rest of the entry function same, I don't see an entry in /dev, though when I make it manually with mknode, and run the test application to use the driver, it works fine.
Below is the code of entry point that does not produce the /dev entry
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/kernel.h>
#include<linux/slab.h>
#include<linux/uaccess.h>
#include<linux/stat.h>
#include<linux/cdev.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#define DEVICE_NAME "myCharDevice"
#define MODULE_NAME "myCharDriver"
#define CLASS_NAME "myCharClass"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YASH BHATT");
MODULE_VERSION(".01");
static char *bufferMemory;
static int bufferPointer;
static int bufferSize = 15;
static dev_t myChrDevid;
static struct cdev *myChrDevCdev;
static struct class *pmyCharClass;
static struct device *pmyCharDevice;
int majorNumber = 0;
static int charDriverOpen(struct inode *inodep, struct file *filep);
static int charDriverClose(struct inode *inodep, struct file *filep);
static ssize_t charDriverWrite(struct file *filep, const char *buffer, size_t len, loff_t *offset);
static ssize_t charDriverRead(struct file *filep, char *buffer, size_t len, loff_t *offset);
static int charDriverEntry(void);
static void charDriverExit(void);
static ssize_t attrShowData(struct device*, struct device_attribute*, char*);
static ssize_t attrStoreData(struct device*, struct device_attribute*, const char*, size_t);
static ssize_t attrShowBuffer(struct device*, struct device_attribute*, char*);
static ssize_t attrStoreBuffer(struct device*, struct device_attribute*, const char*, size_t);
/* The following function is called when the file placed on the sysfs is accessed for read*/
static ssize_t attrShowData(struct device* pDev, struct device_attribute* attr, char* buffer)
{
printk(KERN_INFO "MESG: The data has been accessed through the entry in sysfs\n");
if (bufferPointer == 0)
{
printk(KERN_WARNING "Thre is no data to read from buffer!\n");
return -1;
}
strncpy(buffer, bufferMemory, bufferPointer);
/* Note : Here we can directly use strncpy because we are already in kernel space and do not need to translate address*/
return bufferPointer;
}
static ssize_t attrStoreData(struct device* pDev, struct device_attribute* attr, const char* buffer, size_t length)
{
printk(KERN_INFO "Writing to attribute\n");
bufferPointer = length;
strncpy(bufferMemory, buffer, length);
return length;
}
static ssize_t attrShowBuffer(struct device* pDev, struct device_attribute* attr, char* buffer)
{
int counter;
int temp = bufferSize;
char bufferSizeArray[4] = {0};
counter = 3;
//printk(KERN_INFO "Buffer = %d\n",bufferSize % 10);
do
{
bufferSizeArray[counter] = '0' + (bufferSize % 10);
//printk(KERN_INFO "Character at %d is : %c\n",counter,bufferSizeArray[counter]);
bufferSize /= 10;
counter--;
}
while(counter != -1);
strncpy(buffer, bufferSizeArray, 4);
bufferSize = temp;
/* Note : Here we can directly use strncpy because we are already in kernel space and do not need to translate address*/
return 4;
}
static ssize_t attrStoreBuffer(struct device* pDev, struct device_attribute* attr, const char* buffer, size_t length)
{
int counter;
bufferPointer = length;
//printk(KERN_INFO "Length : %d With first char %c\n",length,buffer[0]);
bufferSize = 0;
for (counter = 0; counter < length-1 ; counter++)
{
bufferSize = (bufferSize * 10) + (buffer[counter] - '0') ;
}
//printk(KERN_INFO "Buffer size new : %d\n",bufferSize);
return length;
}
/* These macros converts the function in to instances dev_attr_<_name>*/
/* Defination of the macro is as follows : DEVICE_ATTR(_name, _mode, _show, _store) */
/* Note the actual implementation of the macro makes an entry in the struct device_attribute. This macro does that for us */
static DEVICE_ATTR(ShowData, S_IRWXU, attrShowData, attrStoreData); // S_IRUSR gives read access to the user
static DEVICE_ATTR(Buffer, S_IRWXU, attrShowBuffer, attrStoreBuffer); // S_IRUSR gives read access to the user
static struct file_operations fops =
{
.open = charDriverOpen,
.release = charDriverClose,
.read = charDriverRead,
.write = charDriverWrite,
};
static int __init charDriverEntry()
{
int returnValue;
//majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
returnValue = alloc_chrdev_region(&myChrDevid, 0, 1, DEVICE_NAME);
/* This function takes 4 arguments - dev_t address, start of minor number, range/count of minor number, Name; Note - unlike register_chrdev fops have not
yet been tied to the major number */
if (returnValue < 0)
{
printk(KERN_ALERT "ERROR : can not aquire major number! error %d",returnValue);
return -1;
}
printk(KERN_INFO "Aquired Major Number! : %d\n", MAJOR(myChrDevid));
//cdev_init(&myChrDevCdev,&fops);
myChrDevCdev = cdev_alloc();
if (IS_ERR(myChrDevCdev))
{
printk(KERN_ALERT "Failed to allocate space for CharDev struct\n");
unregister_chrdev_region(myChrDevid, 1);
return -1;
}
cdev_init(myChrDevCdev,&fops);
myChrDevCdev->owner = THIS_MODULE;
//myChrDevCdev->ops = &fops;/* this function inits the c_dev structure with memset 0 and then does basic konject setup and then adds fops to cdev struct*/
/* this function adds the cdev to the kernel structure so that it becomes available for the users to use it */
// Now we will create class for this device
pmyCharClass = class_create(THIS_MODULE,CLASS_NAME);
if (IS_ERR(pmyCharClass))
{
printk(KERN_ALERT "Failed to Register Class\n");
cdev_del(myChrDevCdev);
kfree(myChrDevCdev);
unregister_chrdev_region(myChrDevid, 1);
return -1;
}
printk(KERN_INFO "Class created!\n");
pmyCharDevice = device_create(pmyCharClass, NULL, MKDEV(majorNumber,0),NULL,DEVICE_NAME);
if (IS_ERR(pmyCharDevice))
{
printk(KERN_ALERT "Failed to Register Class\n");
class_unregister(pmyCharClass);
class_destroy(pmyCharClass);
cdev_del(myChrDevCdev);
kfree(myChrDevCdev);
unregister_chrdev_region(myChrDevid, 1);
return -1;
}
printk(KERN_INFO "Device created!\n");
returnValue = cdev_add(myChrDevCdev, myChrDevid, 1);
if (returnValue < 0)
{
printk(KERN_ALERT "Failed to add chdev \n");
return -1;
}
/* We now have created the class and we have aquired major numer. But we have not yet tied out created fileops with anything.
We will do that now */
//returnValue = cdev_init(cdev)
printk(KERN_INFO "Now We will create the attribute entry in sysfs\n");
/* the function used is device_create_file(struct device *, struct device_attribute*) */
device_create_file(pmyCharDevice, &dev_attr_ShowData); // The second argumnet is the structure created by the DEVICE_ATTR macro
device_create_file(pmyCharDevice, &dev_attr_Buffer);
return 0;
}
static void __exit charDriverExit()
{
device_remove_file(pmyCharDevice, &dev_attr_Buffer);
device_remove_file(pmyCharDevice, &dev_attr_ShowData);
device_destroy(pmyCharClass, MKDEV(majorNumber,0));
class_unregister(pmyCharClass);
class_destroy(pmyCharClass);
//unregister_chrdev(majorNumber,DEVICE_NAME);
cdev_del(myChrDevCdev);
unregister_chrdev_region(myChrDevid, 1);
kfree(myChrDevCdev);
printk(KERN_INFO "Unmounting module done !\n");
}
static int charDriverOpen(struct inode *inodep, struct file *filep)
{
if ((filep->f_flags & O_ACCMODE) != O_RDWR)
{
printk(KERN_ALERT "WARNING : This driver can only be opened in both read and write mode\n");
return -1;
}
printk(KERN_INFO "INFO : CHARATER DRIVER OPENED\n");
bufferMemory = kmalloc(bufferSize,GFP_KERNEL);
bufferPointer = 0;
return 0;
}
static int charDriverClose(struct inode *inodep, struct file *filep)
{
kfree(bufferMemory);
printk(KERN_INFO "INFO : CHARACTER DRIVER CLOSED\n");
return 0;
}
static ssize_t charDriverWrite(struct file *filep, const char *buffer, size_t len, loff_t *offset)
{
// Here we will only allow to write one byte of data
if (len > bufferSize)
{
printk(KERN_WARNING "Attempted to write data larger than 15 byte!\n");
return 0;
}
//bufferMemory[bufferPointer] = *buffer;
copy_from_user(bufferMemory, buffer, len);
bufferPointer += len;
return len;
}
static ssize_t charDriverRead(struct file *filep, char *buffer, size_t len, loff_t *offset)
{
if(len > bufferSize || len > bufferPointer)
{
printk(KERN_WARNING "Attempting to read more than buffer size ! Deny\n");
return 0;
}
copy_to_user(buffer, bufferMemory, len);
// buffer[0] = bufferMemory[0];
bufferPointer -= len;
return len;
}
module_init(charDriverEntry);
module_exit(charDriverExit);
module_param(bufferSize, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(bufferSize, "Buffer Memory Size [15]");
Now if I replace the while alloc_chrdev_region, cdev_init and cdev_add with just register_chrdev(), The entry in /dev pops up. I am unable to figure out what more does register_chrdev() do that the former combination does not.
Thank you
Edit : Found the issue.
it was due to using MKDEV(majorNumber, 0); Without actually storing major number in the majorNumber variable using MAJOR();
Not deleting the question as someone can find it useful
I implement mmap function, and mount it to file operation.
And create a file in /proc.
but when I insmod, it responses "mmap_example2: Unknown symbol _page_cachable_default
insmod: can't insert 'mmap_example2.ko': unknown symbol in module, or unknown parameter"
when i remove mmap function from file operations, it can be inserted.
so do i neglect something? how to make mmap work in procfs?
the code is below
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#define FILE_NAME "test"
typedef enum ALLOC_TYPE
{
KMALLOC = 0, VMALLOC, MAX_ALLOC_TYPE,
} eAllocType;
static unsigned char array[10]={0,1,2,3,4,5,6,7,8,9};
static unsigned char *buffer;
static int file_open(struct inode *pInode, struct file *pFile)
{
printk("%s\n", __FUNCTION__);
return 0;
}
static int file_release(struct inode *pInode, struct file *pFile)
{
printk("%s\n", __FUNCTION__);
return 0;
}
static int file_mmap(struct file *pFile, struct vm_area_struct* pVMA)
{
unsigned long page;
unsigned char i;
unsigned long start = (unsigned long)pVMA->vm_start;
unsigned long size = (unsigned long)(pVMA->vm_end - pVMA->vm_start);
page = virt_to_phys(buffer);
if(remap_pfn_range(pVMA,start,page>>PAGE_SHIFT,size,PAGE_SHARED))
return -1;
for(i=0;i<10;i++)
buffer[i] = array[i];
return 0;
}
struct file_operations file_ops =
{
.open = file_open,
.release = file_release,
.mmap = file_mmap,
};
static int mmap_example2_init(void)
{
struct proc_dir_entry* entry = NULL;
printk("%s init\n", __FUNCTION__);
if(!(entry = create_proc_entry(FILE_NAME,0666,NULL)))
{
printk("%s fail to create proc file\n",__FUNCTION__);
return -EINVAL;
}
entry->proc_fops = &file_ops;
buffer = kmalloc(10,GFP_KERNEL);
if (!buffer)
{
printk("allocate mem error\n");
return -1;
}
SetPageReserved(virt_to_page(buffer));
return 0;
}
static void mmap_example2_exit(void)
{
printk("%s exit\n", __FUNCTION__);
remove_proc_entry(FILE_NAME,NULL);
ClearPageReserved(virt_to_page(buffer));
kfree(buffer);
}
module_init(mmap_example2_init);
module_exit(mmap_example2_exit);
To add file_operations use proc_create instead of create_proc_entry and pass it your file_operation object
static struct file_operations myops =
{
.read = myread,
.mmap = mymmap,
};
static int simple_init(void)
{
ent=proc_create("mytest",0660,NULL,&myops);
printk(KERN_ALERT "hello, module %d...\n",irq);
return 0;
}
The function
struct device * device_create ( struct class * class,
struct device * parent,
dev_t devt,
void * drvdata,
const char * fmt,
...);
[link to documentation] takes an argument "void * drvdata". It is described as "the data to be added to the device for callbacks". What callbacks exactly are meant here? The file-operation-functions?
SysFS is one example of where this could be useful: whenever userspace communicates with the driver using SysFS (see "Reading/Writing Attribute Data" in the documentation), the kernel calls the corresponding callbacks. The void * drvdata pointer supplied during device_create can then be obtained by calling dev_get_drvdata(dev). The pointer could be used to refer to a structure containing driver's state for example.
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#include <linux/device.h>
#include <linux/err.h>
MODULE_LICENSE("GPL");
static ssize_t show_period(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t set_period(struct device* dev,
struct device_attribute* attr,
const char* buf,
size_t count);
static DEVICE_ATTR(period, S_IWUSR | S_IRUSR, show_period, set_period);
static struct device *s_pDeviceObject;
static struct class *s_pDeviceClass;
static struct pwm_device_state
{
int m_Period;
} s_DeviceState;
static int __init pwmdriver_init(void)
{
int result;
s_pDeviceClass = class_create(THIS_MODULE, "pwmdriver");
BUG_ON(IS_ERR(s_pDeviceClass));
s_pDeviceObject = device_create(s_pDeviceClass, NULL, 0, &s_DeviceState, "channel");
result = device_create_file(s_pDeviceObject, &dev_attr_period);
BUG_ON(result < 0);
return result;
}
static ssize_t show_period(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pwm_device_state *pwm_device_data;
pwm_device_data = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", pwm_device_data->m_Period);
}
static ssize_t set_period(struct device* dev,
struct device_attribute* attr,
const char* buf,
size_t count)
{
long period_value = 0;
struct pwm_device_state *pwm_device_data;
if (kstrtol(buf, 10, &period_value) < 0)
return -EINVAL;
if (period_value < 10) //Safety check
return -EINVAL;
pwm_device_data = dev_get_drvdata(dev);
pwm_device_data->m_Period = period_value;
return count;
}
static void __exit pwmdriver_exit(void)
{
device_remove_file(s_pDeviceObject, &dev_attr_period);
device_destroy(s_pDeviceClass, 0);
class_destroy(s_pDeviceClass);
}
module_init(pwmdriver_init);
module_exit(pwmdriver_exit);
I'm developing with a PowerPC 405 embedded in a Virtex4 FPGA with Linux kernel 2.6.33.
Up until now, I've been writing drivers for platform devices implemented in the FPGA in the form of kernel loadable modules. The devices are registered using the flat Open Firmware Device Tree file. To create a device file, I use the OF functions to get the device node, and then register a new miscdevice which then automatically registers a minor device number and creates the device file for me. This also creates a device that is embedded in the miscdevice (i.e. miscdevice.this_device)
The problem is now I need to perform DMA operations. I tried to call the dma_alloc_coherent() function using the miscdevice.this_device, but this device isn't associated with any bus and always returns an error. I did some digging around and it turns out that the struct of_device also has a struct device embedded in it (i.e. of_device.dev). When I tried using this with dma_alloc_coherent(), it worked just fine.
So now I have two different struct device structures, one to manage my character device file, and one to manage the underlying Open Firmware system calls, the bus and DMA transactions. These devices are not associated with each other in the sysfs of course.
My question is, is it possible to somehow request that a device file be created for the device structure I get from the OF layer and not create a new device with the Misc Device API? That way everything will be associated with a single device structure.
I think your fix about dma_alloc_coherent() is correct.
But I don't think it's not right to use the device structure embedded in structure of_device to replace the miscdevice you created. The of_device is description of objects in Open Firmware database. And according to Linux device driver model, device structure is embedded in various device objects in Linux Kernel. And I think you registe miscdevice as one character device, there should be file_operations structure associated.
In one word, they are different views, and they can not replace each other.
I wrote some experimental dma driver using miscdevice.this_device for raspberry pi
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/miscdevice.h>
#include "gpio.h"
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include "DMA.h"
#include <linux/of_irq.h>
static int my_open(struct inode *i, struct file *f)
{
printk(KERN_INFO "Driver: open() %d\n", current->pid);
return 0;
}
static int my_close(struct inode *i, struct file *f)
{
printk(KERN_INFO "Driver: close()\n");
return 0;
}
static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Driver: read()\n");
return 0;
}
char databuf[100];
static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
if(copy_from_user(databuf, buf, 100) != 0) return 0;
printk("Data from the user: %s\n", databuf);
return len;
}
static struct file_operations sample_fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_close,
.read = my_read,
.write = my_write
};
struct miscdevice sample_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "ledButton",
.fops = &sample_fops,
.mode = 0666,
};
//static struct dmadata_s *cpu_addr;
//dma_addr_t dma_addr;
struct dma_cb *virt_cb;
dma_addr_t phys_cb;
uint32_t *virt_src;
dma_addr_t phys_src;
uint32_t *virt_dst;
dma_addr_t phys_dst;
static irqreturn_t dma_irq_fn(int irq, void *dev_id)
{
printk("destAddr %u\n", *virt_dst);
dma_regs->CS.INT = 1;
return IRQ_HANDLED;
}
static struct device *dev;
int IRQ_DMA0;
static int __init ofcd_init(void) /* Constructor */
{
int error, mret;
struct device_node * np = NULL;
error = misc_register(&sample_device);
if (error) {
pr_err("can't misc_register :(\n");
return error;
}
dev = sample_device.this_device;
dev->coherent_dma_mask = ~0;
dev->dma_mask = &dev->coherent_dma_mask;
// dev_set_name(dev, "mydmadev");
// cpu_addr = (struct dmadata_s*)kmalloc(sizeof(struct dmadata_s), GFP_KERNEL | GFP_DMA);
//dma_addr = dma_map_single(dev, cpu_addr, sizeof(struct dmadata_s), DMA_BIDIRECTIONAL);
virt_cb = dma_alloc_coherent(dev, 32, &phys_cb, GFP_KERNEL | GFP_DMA);
if(virt_cb == 0 || phys_cb == 0){
printk("DMA cb error\n");
}
virt_src = dma_alloc_coherent(dev, 4, &phys_src, GFP_KERNEL | GFP_DMA);
if(virt_src == 0 || phys_src == 0){
printk("DMA src error\n");
}
virt_dst = dma_alloc_coherent(dev, 4, &phys_dst, GFP_KERNEL | GFP_DMA);
if(virt_dst == 0 || phys_dst == 0){
printk("DMA dst error\n");
}
memset(virt_cb, 0, sizeof(*virt_cb));
dma_regs = (struct dma_ch *)ioremap(DMA_BASE, sizeof(struct dma_ch));
// strcpy(cpu_addr->srcAddr, "DMA0");
*virt_src = 200;
virt_cb->TI.SRC_INC = 1;
virt_cb->TI.DEST_INC = 1;
virt_cb->TI.INTEN = 1;
virt_cb->SOURCE_AD = (uint32_t)phys_src;
virt_cb->DEST_AD = (uint32_t)phys_dst;
virt_cb->TXFR_LEN = 4;
virt_cb->reserved[0] = 0;
virt_cb->reserved[1] = 0;
printk("srcAddr %u\n", *virt_src);
printk("destAddr %u\n", *virt_dst);
//dma_regs->CS = (DMA_CS_t){.RESET = 1, .END = 1};
dma_regs->CS.RESET = 1;
udelay(10);
// dma_regs->CS = (DMA_CS_t){.END = 1, .INT = 1};
dma_regs->CS.INT = 1;
dma_regs->CS.END = 1;
dma_regs->CONBLK_AD = (uint32_t)phys_cb;
//dma_regs->DEBUG = (DMA_DEBUG_t){.READ_LAST_NOT_SET_ERROR = 1, .FIFO_ERROR = 1, .READ_ERROR = 1};
dma_regs->DEBUG.READ_LAST_NOT_SET_ERROR = 1;
dma_regs->DEBUG.FIFO_ERROR = 1;
dma_regs->DEBUG.READ_ERROR =1;
udelay(10);
// dma_regs->CS = (DMA_CS_t){.RESET = 1, .PRIORITY = 8, .PANIC_PRIORITY = 8, .ACTIVE = 1};
dma_regs->CS.RESET = 1;
udelay(10);
dma_regs->CS.PRIORITY = 8;
dma_regs->CS.PANIC_PRIORITY = 8;
dma_regs->CS.ACTIVE = 1;
if(dma_regs->CS.ERROR) printk("ERROR %d %d\n", dma_regs->CS.ACTIVE, dma_regs->CS.PANIC_PRIORITY);
//np = of_find_compatible_node(NULL,NULL,"brcm,bcm2835-system-timer");
np = of_find_node_by_path("/soc/dma#7e007000");
if (np == NULL){
printk("Error node not found\n");
}
// printk("node name %s\n", np->name);
IRQ_DMA0 = irq_of_parse_and_map(np, 0);
if (IRQ_DMA0 <= 0) {
printk("Can't parse IRQ\n");
}
mret = request_irq(IRQ_DMA0, dma_irq_fn, IRQF_SHARED, "dma", &dma_irq_fn);
if (mret < 0) printk(KERN_ALERT "%s: dma request_irg failed with %d\n", __func__, mret);
return 0;
}
static void __exit ofcd_exit(void) /* Destructor */
{
free_irq( IRQ_DMA0, &dma_irq_fn );
//dma_unmap_single(dev, dma_addr, sizeof(struct dmadata_s), DMA_BIDIRECTIONAL);
//kfree(cpu_addr);
dma_free_coherent(dev, 32, virt_cb, phys_cb);
dma_free_coherent(dev, 4, virt_src, phys_src);
dma_free_coherent(dev, 4, virt_dst, phys_dst);
iounmap(dma_regs);
// device_unregister(dev);
misc_deregister(&sample_device);
printk(KERN_INFO "Module unregistered\n");
}
module_init(ofcd_init);
module_exit(ofcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("MBajor>");
MODULE_DESCRIPTION("PiCNC driver");
MODULE_VERSION("0.1");
I hope this help.