c - read error device driver -
not able receive buffer using fread() userland function. driver using copy_to_user() pass info. there blatant errors in way implementing this?
the error can seen in printf("%s\n",buffer); statement userland code.
userland code:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> /* ## slave register descriptions ## reset = 0x4 ## en = 0x8 ## input = 0xc ## outlo = 0x10 ## outhi = 0x14 */ void readaccelerator(unsigned int * outlo, unsigned int * outhi); int writeaccelerator(unsigned int *data, char *address); int main (int argc, char *argv[]) { unsigned int *data; unsigned int *result; char * reset_addr = "1"; char * en_addr = "2"; char * input_addr = "3"; unsigned int * outlo = 0; unsigned int * outhi = 0; data = 0xffef; writeaccelerator(1, reset_addr); writeaccelerator(0, reset_addr); writeaccelerator(data, input_addr); writeaccelerator(1, en_addr); writeaccelerator(0, en_addr); readaccelerator(&outlo, &outhi); printf("input:%04x outlo = %02x, outhi = %02x\n", data, outlo, outhi); return 0; } void readaccelerator(unsigned int * outlo, unsigned int * outhi) { char * buffer[10]; size_t size = 1; size_t nitems = 10; file* fp; fp = fopen("/proc/accelerator","r"); if (fp == null) { printf("cannot open read\n"); return -1; } /* expect return format: 0x00, 0x00 */ fread(buffer, size, nitems, fp); fclose(fp); printf("eh?\n"); printf("%s\n",buffer); return; } int writeaccelerator(unsigned int *data, char *address) { file* fp; char str[30]; sprintf(str, "0x%08x ", data); //data strcat(str, address); //address //printf("input data: %s", str); fp = fopen("/proc/accelerator","w"); if (fp == null) { printf("cannot open write\n"); return -1; } fputs(str, fp); fclose(fp); }
device driver code:
#include <linux/kernel.h> #include <linux/module.h> #include <asm/uaccess.h> /* needed copy_from_user */ #include <asm/io.h> /* needed io read/write functions */ #include <linux/proc_fs.h> /* needed proc file system functions */ #include <linux/seq_file.h> /* needed sequence file operations */ #include <linux/platform_device.h> /* needed platform driver functions */ #include <linux/slab.h> /*for kmalloc , kfree */ #include <linux/vmalloc.h> /* define driver name */ #define driver_name "accelerator" unsigned long *base_addr; /* vitual base address */ struct resource *res; /* device resource structure */ unsigned long remap_size; /* device memory size */ /* write operation /proc/accelerator * ----------------------------------- * when user cat string /proc/accelerator file, string stored in * const char __user *buf. function copy string user * space kernel space, , change unsigned long value. * write value register of accelerator controller, * , turn on corresponding leds eventually. */ static ssize_t proc_accelerator_write(struct file *file, const char __user * buf, size_t count, loff_t * ppos) { //allocate char * myaddr_phrase; char * pend; char *buffer = vzalloc(count); myaddr_phrase = buffer; u32 myaddr_value; u32 myreg_value; //copy data if (count < 22) { if (copy_from_user(myaddr_phrase, buf, count)) return -efault; //myaddr_phrase[count] = '\0'; //printk("count = %d\n", count); //printk("%s\n",myaddr_phrase); } // use strtol parse input /* http://www.cplusplus.com/reference/cstdlib/strtol/ */ myaddr_value = simple_strtoul(myaddr_phrase, &pend, 0); //printk("myaddr_value = %08x\n", myaddr_value); pend = strsep(&myaddr_phrase," "); myreg_value = simple_strtoul(myaddr_phrase,&pend ,0); //printk("myreg_value = %08x\n", myreg_value); //printk("final_value = %08x\n", (base_addr + (myreg_value))); //printk("mult_val = %08x\n", (myreg_value)); wmb(); iowrite32(myaddr_value, (base_addr + (myreg_value))); printk("write: reg %d; value 0x%08x\n",myreg_value,myaddr_value); return count; } static ssize_t proc_accelerator_read(struct file *file, const char __user * buf, size_t count, loff_t * ppos) { printk("read: "); u32 out_lo; u32 out_hi; int outlo_addr = 4; int outhi_addr = 5; u32 len = 10; char * output; //char * buffer; out_lo = ioread32(base_addr+outlo_addr); out_hi = ioread32(base_addr+outhi_addr); //seq_printf(p, "0x%02x, 0x%02x", out_lo, out_hi); sprintf(output, "0x%02x, 0x%02x", out_lo, out_hi); // length = 10 printk("output = %s\n",output); if(copy_to_user(buf, &output, len)) return -efault; // returning total length of 11!!! printk("exiting read...\n"); return 0; } /* callback function when opening file /proc/accelerator * ------------------------------------------------------ * read register value of accelerator controller, print value * sequence file struct seq_file *p. in file open operation /proc/accelerator * callback function called first fill seq_file, * , seq_read function print whatever in seq_file terminal. */ static int proc_accelerator_show(struct seq_file *p, void *v) { u32 out_lo; u32 out_hi; int outlo_addr = 4; int outhi_addr = 5; char * output; char * buffer; out_lo = ioread32(base_addr+outlo_addr); out_hi = ioread32(base_addr+outhi_addr); seq_printf(p, "0x%02x, 0x%02x", out_lo, out_hi); sprintf(output, "0x%02x, 0x%02x", out_lo, out_hi); //copy_to_user(*buffer, output, 11); // returning total length of 14!!! return 0; } /* open function /proc/accelerator * ------------------------------------ * when user want read /proc/accelerator (i.e. cat /proc/accelerator), open function * called first. in open function, seq_file prepared , * status of accelerator filled seq_file proc_accelerator_show function. * *p 69 int (*open) (struct inode *, struct file *); though first operation performed on device file, driver not required declare corresponding method. if entry null, opening device succeeds, driver isn’t notified. open described on p76 */ static int proc_accelerator_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_accelerator_show, null); if (!res) { m = file->private_data; m->buf = buf; m->size = size; } else { kfree(buf); } return res; } /* file operations /proc/accelerator */ static const struct file_operations proc_accelerator_operations = { .open = proc_accelerator_open, // .read = seq_read, .read = proc_accelerator_read, .write = proc_accelerator_write, .llseek = seq_lseek, .release = single_release }; /* int (*open) (struct inode *, struct file *); though first operation performed on device file, driver not required declare corresponding method. if entry null, opening device succeeds, driver isn’t notified. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); used retrieve data device. null pointer in position causes readsystem call fail with-einval(“invalid argument”). nonnegative return value represents number of bytes read (the return value “signed size” type, native integer type target platform) ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); sends data device. ifnull, -einvalis returned program calling writesystem call. return value, if nonnegative, represents number of bytes written. loff_t (*llseek) (struct file *, loff_t, int); thellseek method used change current read/write position in file, , new position returned (positive) return value. theloff_tparameter “long offset” , @ least 64 bits wide on 32-bit platforms. errors signaled negative return value. if function pointer isnull, seek calls modify position counter in thefilestructure (described in section “the file structure”) in potentially unpredictable ways. int (*release) (struct inode *, struct file *); operation invoked when thefilestructure being released. likeopen, releasecan benull. * */ /* shutdown function accelerator * ----------------------------------- * before accelerator shutdown, turn-off leds */ static void accelerator_shutdown(struct platform_device *pdev) { iowrite32(0, base_addr); } /* remove function accelerator * ---------------------------------- * when accelerator module removed, turn off leds first, * release virtual address , memory region requested. */ static int accelerator_remove(struct platform_device *pdev) { accelerator_shutdown(pdev); /* remove /proc/accelerator entry */ remove_proc_entry(driver_name, null); /* release mapped virtual address */ iounmap(base_addr); /* release region */ release_mem_region(res->start, remap_size); return 0; } /* device probe function accelerator * ------------------------------------ * resource structure information in device tree. * request memory region needed controller, , map * kernel virtual memory space. create entry under /proc file system * , register file operations entry. */ static int accelerator_probe(struct platform_device *pdev) { struct proc_dir_entry *accelerator_proc_entry; int ret = 0; printk(kern_alert "probing\n"); 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 @ 0x%08lx\n", (unsigned long)res->start); ret = -enomem; goto err_release_region; } accelerator_proc_entry = proc_create(driver_name, 0, null, &proc_accelerator_operations); if (accelerator_proc_entry == null) { dev_err(&pdev->dev, "couldn't create proc entry\n"); ret = -enomem; goto err_create_proc_entry; } printk(kern_info driver_name " probed @ va 0x%08lx\n", (unsigned long) base_addr); printk(kern_alert "goodbye, probe\n"); return 0; err_create_proc_entry: iounmap(base_addr); err_release_region: release_mem_region(res->start, remap_size); return ret; } /* device match table match device node in device tree */ /* https://lwn.net/articles/448502/ */ static const struct of_device_id accelerator_of_match[] = { {.compatible = "pca,bitsplitter"}, {}, }; module_device_table(of, accelerator_of_match); /* platform driver structure accelerator driver */ /* platform devices represented struct, , found in <linux/platform_device.h> @ minimum probe() , remove() must supplied, others have power management https://lwn.net/articles/448499/ */ static struct platform_driver accelerator_driver = { .driver = { .name = driver_name, .owner = this_module, .of_match_table = accelerator_of_match}, .probe = accelerator_probe, .remove = accelerator_remove, .shutdown = accelerator_shutdown }; /* register accelerator platform driver */ /* helper macro drivers don't * special in module init/exit. eliminates lot of * boilerplate. each module may use macro once, , * calling replaces module_init() , module_exit() * * platform drivers hw not dynamically come , go linux system, * such video , audio controllers in tablet. in makes sense statically pull * in code necessary through __initcall magic discussed above. * * http://henryomd.blogspot.com/2014/11/linux-kernel-startup.html */ module_platform_driver(accelerator_driver); /* module informations */ /* discussed in 2.6 preliminaries */ module_author("digilent, inc."); module_license("gpl"); module_description(driver_name ": accelerator driver (simple version)"); module_alias(driver_name);
in readaccelerator
function, reading 10 chars buffer, buffer defined array of pointer char, not array of char, try this:
// char * buffer[10]; <-- problem here char buffer[10];
problems in kernel code:
// char *output; // writing output without initialize it, produce ub, may crash, try this: char output[1024]; sprintf(output, "0x%02x, 0x%02x", out_lo, out_hi); // length = 10 printk("output = %s\n",output); // if(copy_to_user(buf, &output, len)) // here copying address of output, not output buffer, try if(copy_to_user(buf, output, len)) ...
Comments
Post a Comment