interacting user space during system call - linux-kernel

I'm trying to write block device driver that implements read/write operations.
The tricky thing is that the information is not in the hardware, but in a user space process. Therefore, during the read/write system call I would like to interact the user space (i.e. sendign signal to the user space).
However, my user space process catching the signal only after the read/write system call returned. adding wait in the system call implementation seems to be ignored somehow.
I used this code at the read system call:
ssize_t sleepy_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
struct siginfo info;
struct task_struct *t;
int ret;
#define SIG_TEST 44
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIG_TEST;
info.si_code = SI_QUEUE;
info.si_int = 1234;
rcu_read_lock();
t = pid_task(find_pid_ns(current->pid, &init_pid_ns), PIDTYPE_PID);
if(t == NULL){
printk(KERN_ERR "no such pid\n");
rcu_read_unlock();
return -ENODEV;
}
rcu_read_unlock();
ret = send_sig_info(SIG_TEST, &info, t); //send the signal
if (ret < 0) {
printk("error sending signal\n");
return ret;
}
wait_event_interruptible(wq, flag != 0);
msleep(10000);
return (0);
}
and this code at user space:
#define SIG_TEST 44
int g_devFile = -1;
void receiveData(int n, siginfo_t *info, void *unused)
{
printf("received value %i\n", info->si_int);
}
int main(void)
{
struct sigaction sig;
sig.sa_sigaction = receiveData;
sig.sa_flags = SA_SIGINFO;
sigaction(SIG_TEST, &sig, NULL);
g_devFile = open(devname, O_RDWR);
if ( g_devFile < 0 ) {
fprintf(stderr,"Error opening device[%s] file err[%s]\n",devname,strerror(errno));
return -1;
} else {
fprintf (stderr, "device opened. ptr=%p\n", (void*)g_devFile);
}
i = read(g_devFile, &buff, 11);
}
Currently I'm catching my signal (in user space) only after the 10 seconds sleep expieres (the wait seems to be ignored).
Any idea will be appriceated. Thanks.

Related

Problem stopping a child loop using sigaction and intercepting Ctrl*C

I'm trying to write a program that intercepts Ctrl^C using sigaction, and then terminates the child of a fork.
Code:
static void usrHandler(int sig, siginfo_t * si, void * ignore) {
printf("Interrupt Worked");
}
int main(int argc, char * argv[]) {
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset( & sa.sa_mask);
sa.sa_sigaction = usrHandler;
sigaction(SIGINT, & sa, NULL);
int currentFile = 1;
int fd;
int forkChild;
forkChild = fork();
if (forkChild == 0) {
sleep(100);
} else if (forkChild > 0) {
sa.sa_sigaction = SIG_IGN;
sigaction(SIGUSR1, & sa, NULL);
}
}
I tried to remove all non necessary code for my example. For some reason I can not get the interrupt to work when I press Ctrl^C. Eventually I would like to be able to close the child and continue in the parent. Am I doing something wrong here?
For some reason I can not get the interrupt to work when I press Ctrl^C.
Because your data in IO buffer, so change printf("Interrupt Worked"); to printf("Interrupt Worked\n"); (add \n), you will get data.
For IO buffer, see https://stackoverflow.com/a/53083985/7671328

There is no entry for device under /dev even after class_create and device_create

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

how to alloc user space memory in kernel space?

I hook a syscall(open) on Linux, and want to print this opened filename.
then I call syscall(getcwd) to get the absolute path.
this is source code:
void *memndup_from_user(const void __user *src, long len)
{
void *kbuf = NULL;
if(src == NULL) {
return kbuf;
}
kbuf = kmalloc(len + 1, GFP_KERNEL);
if(kbuf != NULL) {
if (copy_from_user(kbuf, src, len)) {
printk(KERN_ALERT "%s\n", "copy_from_user failed.");
kfree(kbuf);
kbuf = NULL;
}
else {
((char *)kbuf)[len] = '\0';
}
} else {
printk(KERN_ALERT "%s\n", "kmalloc failed.");
}
return kbuf;
}
void *memdup_from_user(const void __user *src)
{
long len = 0;
if(src == NULL) {
return NULL;
}
len = strlen_user(src);
return memndup_from_user(src, len);
}
asmlinkage long fake_getcwd(char __user *buf, unsigned long size)
{
return real_getcwd(buf, size);
}
asmlinkage long
fake_open(const char __user *filename, int flags, umode_t mode)
{
if(flags & O_CREAT) {
char *k_filename = (char *)memdup_from_user(filename);
char *u_path = (char *)kmalloc(PAGE_SIZE, GFP_USER);
if(k_filename != NULL) {
printk(KERN_ALERT "ano_fake_open pid:%ld create : %s\n", ano_fake_getpid(), k_filename);
kfree(k_filename);
}
if(u_path != NULL) {
long retv;
retv = fake_getcwd(u_path, PAGE_SIZE);
if(retv > 0) {
printk(KERN_ALERT "getcwd ret val: %ld, path: %s\n", retv, u_path);
} else {
printk(KERN_ALERT "getcwd ret val: %ld, error...\n", retv);
}
kfree(u_path);
}
}
return real_open(filename, flags, mode);
}
the sys_getcwd requires an user space memory, and I call kmalloc with GFP_USER.
but sys_getcwd always return -EFAULT(Bad Address)...
this is dmesg logs:
[344897.726061] fake_open pid:70393 create : sssssssssssssssss
[344897.726065] getcwd ret val: -14, error...
[344897.727431] fake_open pid:695 create : /var/lib/rsyslog/imjournal.state.tmp
[344897.727440] getcwd ret val: -14, error...
so I find the implement in sys_getcwd, he does
# define __user __attribute__((noderef, address_space(1)))
# define __kernel __attribute__((address_space(0)))
#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL)
SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
{
char *page = __getname();
get_fs_root_and_pwd_rcu(current->fs, &root, &pwd);
...
// char *cwd = page + xxx; (xxx < PAGE_SIZE)
// len = PAGE_SIZE + page - cwd;
...
if (len <= size) {
error = len;
if (copy_to_user(buf, cwd, len))
error = -EFAULT;
}
}
obviously, getcwd alloc memory with flag GFP_KERNEL, then copy to my buffer( __user *buf ) from (GFP_KERNEL) !!!
isn't __user MACRO be GFP_USER ?
the flag GFP_USER brief is https://elixir.bootlin.com/linux/v4.4/source/include/linux/gfp.h#L208:
/* GFP_USER is for userspace allocations that also need to be directly
* accessibly by the kernel or hardware. It is typically used by hardware
* for buffers that are mapped to userspace (e.g. graphics) that hardware
* still must DMA to. cpuset limits are enforced for these allocations.
*/
what's wrong ?
This is wrong on at least two accounts:
syscall hijacking (let alone for something like open) is just a bad idea. the only sensible method to catch all possible open path is through using LSM hooks. it also happens to deal with the actual file being opened avoiding the race: you read the path in your routine, wrapped opens reads it again. but by that time malicious userspace could have changed it and you ended up looking at the wrong file.
it should be clear getcwd has to have a method of resolving a name in order to put it into the userspace buffer. you should dig in into the call and see what can be changed to put it in a kernel buffer.
Why are you doing this to begin with?

polling sysfs and kernel module

I have read many posts about this same topic, but I am unable to find out what is exactly wrong with my sysfs implementation in my kernel module. I am trying to make a userspace program block on a poll untill the value changes in a sysfs file. Most people seem to not get blocking, I seem to not be able to get out of my blocking. Here is the relevent code:
kernel module:
static int sysfs_test = 88;
static ssize_t test_interrupts_show(struct device* dev, struct device_attribute* attr, const char* buf)
{
return scnprintf(buf, PAGE_SIZE, "%d\n", sysfs_test);
}
static ssize_t test_interrupts_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
kstrtol(buf, 10, &sysfs_test);
return count;
}
static DEVICE_ATTR(interrupts, S_IWUSR | S_IRUGO, test_interrupts_show, test_interrupts_store);
static int __init test_init(void)
{
int result;
if(dev_major)
{
dev = MKDEV(dev_major, dev_minor);
result = register_chrdev_region(dev, NUM_DEVICES, name);
} else {
result = alloc_chrdev_region(&dev, dev_minor, NUM_DEVICES, name);
dev_major = MAJOR(dev);
dev_minor = MINOR(dev);
}
if(result < 0) {
printk(KERN_WARNING "%s: can't get major %d\n", name, dev_major);
return -1;
}
printk(KERN_NOTICE "%s: Major = %d, Minor = %d\n", name, dev_major, dev_minor);
// Register as character device
test_cdev = cdev_alloc();
cdev_init(cajun_cdev, &test_fops); // Initialize cdev structure
test_cdev->owner = THIS_MODULE; // Add owner
result = cdev_add(test_cdev, dev,1); // Tell kernel about our device
if(result)
{
printk(KERN_NOTICE "Error %d adding cdev\n", result);
goto OUT2;
}
// This stuff relates to sysfs:
ctest_class = class_create(THIS_MODULE, NAME);
if(IS_ERR(test_class))
{
printk(KERN_ALERT "Failed to register device class\n");
goto OUT2;
}
test_device = device_create(test_class, NULL, dev, NULL, NAME);
if(IS_ERR(test_device))
{
printk(KERN_ALERT "Failed to create device\n");
goto OUT3;
}
result = device_create_file(test_device, &dev_attr_interrupts);
if (result < 0)
{
printk(KERN_ALERT "failed\n");
}
OUT3:
class_unregister(test_class);
class_destroy(test_class);
OUT2:
cdev_del(test_cdev);
OUT1:
unregister_chrdev_region(dev, NUM_DEVICES);
return -1;
}
Relevent userspace code:
char interrupts_path[] = "/sys/class/test_module/test_module/interrupts";
int main()
{
struct pollfd fds;
fds.fd = open(interrupts_path, O_RDWR | O_SYNC);
char dummy_buff[1];
read(fds.fd, dummy_buff, 1);
lseek(fds.fd, 0, SEEK_SET);
fds.events = POLLPRI;
printf("Polling for interrupt\n");
poll(&fds,1,-1);
printf("Interrupt occured\n");
return 0;
}
I run my userspace code in the background (./test &) and then I echo a new value into the sysfs file for interrupts. I am hopping for my userspace program to unblock and return when the value changes. What am I doing wrong here?
edit:
struct file_operations test_fops = {
.owner = THIS_MODULE,
.llseek = test_llseek,
.read = test_read,
.write = test_write,
.unlocked_ioctl = test_ioctl,
.open = test_open,
.release = test_release
};

Associate existing Linux device structure with device file

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.

Resources