读取错误设备驱动程序

时间:2016-04-22 02:23:24

标签: c linux-device-driver

无法使用来自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);

OUTPUT from userland and kern.log

2 个答案:

答案 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;
}