如何在pSeudo驱动程序中只进行一次读操作?

时间:2014-04-15 13:37:48

标签: linux-kernel kernel linux-device-driver

我正在尝试编写内核驱动程序。但是,当我在cat / dev / pSeudoDrv上执行cat操作时。它只是不断打印价值。它永远不会退出。我假设cat操作应该在读取一次后退出。但是,它正在永远阅读。这有什么问题?

#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h>  /** This is for KERN_ALERT **/
#include <linux/fs.h> /** for file operations **/
#include <linux/cdev.h>  /** character device **/
#include <linux/device.h>  /** for sys device registration in /dev/ and /sys/class **/
/** for copy_to_user **/
#include <asm/uaccess.h>


/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");


static struct cdev basicCdev;
static struct class *basicDriverClass;

static int  basicMajorNumber = 0;

/** Prototype for read, this will be invoked when the read function is done on to the driver **/
/** The declaration type is file operations based function pointer - read **/
static ssize_t basicRead(struct file *filp, char *buffer, size_t length,loff_t *offset);

static int basicOspen(struct inode *inode, struct file *file);   

/** File Operations function pointer table **/
/** There are plenty of file operations  **/

static struct file_operations fops = {
  .read = basicRead,
  .write = NULL,
  .open = basicOspen,
  .release = NULL
};

static ssize_t basicRead(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
     char msg[1024] = "Hello SJ_read\0";
     printk(KERN_ALERT "The Read operation called\r\n");
     copy_to_user( buffer, msg, sizeof(msg) );
     return sizeof(msg);
}

static int basicOspen(struct inode *inode, struct file *file)
{

   printk("Kernel.Basic Driver Opened now!!\r\n");

   return 0;
}

static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
         int err = -1;
         /** MKDEV call creates a device number i.e. combination of major and minor number **/
         int devno = MKDEV(basicMajorNumber, minor);
         /** Initiliaze character dev with fops **/
         cdev_init(dev, fops);
         /**owner and operations initialized **/
         dev->owner = THIS_MODULE;
         dev->ops = fops;
         /** add the character device to the system**/
         /** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
         err = cdev_add (dev, devno, 1);

         if (err)
         {
                 printk (KERN_NOTICE "Couldn't add cdev");
         }
 }

static int chrDriverInit(void)
{

    int result;
    dev_t dev; 

    printk("Welcome!! Device Init now..");

    /** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);  **/
    /** dev -> The dev_t variable type,which will get the major number that the kernel allocates.  **/
    /**The same name will appear in /proc/devices.  **/

    /** it is registering the character device **/   
    /** a major number will be dynamically allocated here **/
    result = alloc_chrdev_region(&dev, 0, 1, "pSeudoDrv");

    if( result < 0 )
    {
      printk("Error in allocating device");
      return -1;    
    }

    /** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
    /**  creating class, and then device created removes the dependency of calling mknod  **/
    /** A good method - the mknod way is depreciated **/
    /** mknod way is -  mknod /dev/<driver_name> c <majorNumber> <minorNumber>


    /** add the driver to /sys/class/chardrv **/
    if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL)    //$ls /sys/class
    {
        unregister_chrdev_region(dev, 1);
        return -1;
    }

     /** add the driver to /dev/pSeudoDrv -- here **/
    if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
    {
        class_destroy(basicDriverClass);
        unregister_chrdev_region(dev, 1);
        return -1;
    }

     /** let's see what major number was assigned by the Kernel **/
     basicMajorNumber = MAJOR(dev);
     printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );

    /** Now setup the cdev **/
    setup_cdev(&basicCdev, 0, &fops);

    return 0;   

}


static void chrDriverExit(void)
{
    /** A reverse - destroy mechansim -- the way it was created **/
    printk("Releasing Simple Devs -- %s\r\n",  __FUNCTION__);
    /** delete the character driver added **/
    cdev_del(&basicCdev);
    /** destroy the device created **/
    device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
    /** destroy the class created **/
    class_destroy(basicDriverClass);
    /** unregister the chr dev **/
    unregister_chrdev(basicMajorNumber, "pSeudoDrv");

}


module_init(chrDriverInit);
module_exit(chrDriverExit);

2 个答案:

答案 0 :(得分:2)

你必须返回0才能让用户空间进程知道驱动程序已经复制了所有数据并且在文件末尾。

如果返回读取文件操作参数中提供的长度,则呼叫将继续进行。

要避免这种情况,必须使用在用户空间“char * buffer”中复制的字节数增加“loff_t * offset”并返回确切的数字。当下一次读取呼叫到达时,此“* offset”将保持其值。

现在您将再次接到阅读电话。您必须将“* offset”与需要复制到用户空间的“内核数据大小”进行比较。如果“* offset”大于或等于“内核数据的大小”,则应返回0.这将使用户空间程序知道文件(或设备)中没有更多可用数据,并且该过程将停止读数。

答案 1 :(得分:0)

basicRead中查看您的返回值。

来自LDD3:

  

read的返回值由调用应用程序解释   程序:

     
      
  • 如果值等于传递给read系统调用的count参数,则传输所请求的字节数。这是   最佳案例。

  •   
  • 如果值为正,但小于count,则只传输部分数据。这可能由于多种原因而发生,   取决于设备。大多数情况下,应用程序重试   阅读。例如,如果您使用fread函数读取,则   库函数重新发出系统调用,直到完成   请求数据传输。

  •   
  • 如果值为0,则表示已达到文件结尾(并且未读取任何数据)。

  •   
  • 负值表示出现错误。根据,该值指定错误是什么。返回典型值   错误包括-EINTR(中断系统调用)或-EFAULT(错误   地址)。

  •   

在你的情况下,sizeof(msg)小于length,它表示用户空间有关部分读取。 如果您不关心部分读取,您可以简单地执行return length;,这样用户空间就会知道读取已完成。

否则,您应该理解filpoffset