无法使用来自userland函数的fread()接收缓冲区。驱动程序正在使用copy_to_user()来传递信息。我实施这个方法有没有明显的错误?
错误可以在printf中看到(“%s \ n”,缓冲区);来自用户代码的声明。
Userland代码:
#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 for 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 for write\n");
return -1;
}
fputs(str, fp);
fclose(fp);
}
设备驱动程序代码:
#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 */
#include <linux/slab.h> /*for kmalloc and 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 for /proc/accelerator
* -----------------------------------
* When user cat a string to /proc/accelerator 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 accelerator controller,
* and turn on the 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 to 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 the register value of accelerator controller, print the value to
* the sequence file struct seq_file *p. In file open operation for /proc/accelerator
* 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_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 for /proc/accelerator
* ------------------------------------
* When user want to read /proc/accelerator (i.e. cat /proc/accelerator), the open function
* will be called first. In the open function, a seq_file will be prepared and the
* status of accelerator will be filled into the seq_file by proc_accelerator_show function.
*
*p 69
int (*open) (struct inode *, struct file *);
Though this is always the first operation performed on the device file, the driver
is not required to declare a corresponding method. If this entry is NULL, opening
the device always succeeds, but your 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 for /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 this is always the first operation performed on the device file, the driver
is not required to declare a corresponding method. If this entry is NULL, opening
the device always succeeds, but your driver isn’t notified.
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
Used to retrieve data from the device. A null pointer in this position causes the
readsystem call to fail with-EINVAL(“Invalid argument”). A nonnegative return
value represents the number of bytes successfully read (the return value is a
“signed size” type, usually the native integer type for the target platform)
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
Sends data to the device. IfNULL, -EINVALis returned to the program calling the
writesystem call. The return value, if nonnegative, represents the number of
bytes successfully written.
loff_t (*llseek) (struct file *, loff_t, int);
Thellseek method is used to change the current read/write position in a file, and
the new position is returned as a (positive) return value. Theloff_tparameter is
a “long offset” and is at least 64 bits wide even on 32-bit platforms. Errors are
signaled by a negative return value. If this function pointer isNULL, seek calls will
modify the position counter in thefilestructure (described in the section “The
file Structure”) in potentially unpredictable ways.
int (*release) (struct inode *, struct file *);
This operation is invoked when thefilestructure is being released. Likeopen,
releasecan beNULL.
*
*/
/* Shutdown function for accelerator
* -----------------------------------
* Before accelerator shutdown, turn-off all the leds
*/
static void accelerator_shutdown(struct platform_device *pdev)
{
iowrite32(0, base_addr);
}
/* Remove function for accelerator
* ----------------------------------
* When accelerator module is removed, turn off all the leds first,
* release virtual address and the 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 the region */
release_mem_region(res->start, remap_size);
return 0;
}
/* Device Probe function for accelerator
* ------------------------------------
* 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 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 at 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 at 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 to match with 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 for accelerator driver */
/*
Platform devices are represented by the struct, and is found in <linux/platform_device.h>
at minimum probe() and remove() must be supplied, the others have to do with 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 for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit()
*
* Platform drivers are for HW that will not dynamically come and go into a Linux system,
* such as the video and audio controllers in a tablet. In makes sense to statically pull
* in those code necessary through the __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);
答案 0 :(得分:1)
在readAccelerator
函数中,你正在读取缓冲区中的10个字符,但缓冲区被定义为指向char的指针数组,而不是char数组,请尝试:
// char * buffer[10]; <-- problem here
char buffer[10];
内核代码中的问题:
// char *output;
// you are writing to output without initialize it, this produce UB, which may be a 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 you are copying from the address of output, not output the buffer, try
if(copy_to_user(buf, output, len)) ...
答案 1 :(得分:1)
现在它正在运作。
你是对的@fluter需要分配output [11] = {0}
。这是最后的功能,并要求参考。
阅读的驱动程序功能:
static ssize_t proc_accelerator_read(struct file *file, const char __user * buf,
size_t count, loff_t * ppos)
{
printk("READ: ");
u32 out_lo;
char output[11] = {0};
u32 out_hi;
int OUTLO_ADDR = 4;
int OUTHI_ADDR = 5;
u32 len = 11;
out_lo = ioread32(base_addr+OUTLO_ADDR);
out_hi = ioread32(base_addr+OUTHI_ADDR);
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 count;
}
呼叫的用户地址功能:
void readAccelerator(unsigned int * outlo, unsigned int * outhi)
{
char buffer[11];
size_t size = 1;
size_t nitems = 11;
FILE* fp;
fp = fopen("/proc/accelerator","r");
if (fp == NULL)
{
printf("Cannot open for read\n");
return -1;
}
/*
Expect return format:
0x00, 0x00
*/
fread(buffer, size, nitems, fp);
// read(fp, buffer, nitems);
fclose(fp);
printf("%s\n",buffer);
return;
}