PCliangtao 发表于 2010-09-14 01:04

LDD字符驱动程序scull修改

终于把自己模仿scull写的字符驱动程序完成了... 只是和它的缓冲管理不一样... scull太麻烦了....
   搞定自己的之后... 就把scull裁减了一下... 方便以后回顾的时候可以有一个简单的字符驱动模型能够参考....
贴上代码....


scull.h文件:/*
* scull.h -- definitions for the char module
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files.The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates.   No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
* $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $
*/

#ifndef _SCULL_H_
#define _SCULL_H_

#include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */

#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0   /* dynamic major by default */
#endif

struct scull_dev {
        char data;
        unsigned int data_size;
        struct cdev cdev;          /* Char device structure                */
};

extern int scull_major;   /* main.c */


ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                   loff_t *f_pos);
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                  loff_t *f_pos);

#endif
main.c代码:/*
* main.c -- the bare scull char module
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files.The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates.   No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
*/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h>        /* printk() */
#include <linux/slab.h>                /* kmalloc() */
#include <linux/fs.h>                /* everything... */
#include <linux/errno.h>        /* error codes */
#include <linux/types.h>        /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>        /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>

#include <asm/system.h>                /* cli(), *_flags */
#include <asm/uaccess.h>        /* copy_*_user */

#include "scull.h"                /* local definitions */

/*
* Our parameters which can be set at load time.
*/

int scull_major =   SCULL_MAJOR;
int scull_minor =   0;

module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);

MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");

struct scull_dev *scull_devices;        /* allocated in scull_init_module */


int scull_open(struct inode *inode, struct file *filp)
{
        struct scull_dev *dev; /* device information */

        dev = container_of(inode->i_cdev, struct scull_dev, cdev);
        filp->private_data = dev; /* for other methods */

        return 0;          /* success */
}

int scull_release(struct inode *inode, struct file *filp)
{
        return 0;
}


ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
        struct scull_dev *dev = filp->private_data;

        void *start;
        start = dev->data;
        if(dev->data_size < count)
                count = dev->data_size;
        copy_to_user(buf, start, count);

        return count;
}

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                loff_t *f_pos)
{
        struct scull_dev *dev = filp->private_data;
        void *start;
        start = dev->data;
        if(count > 4000)
                count = 4000 - 1;
        copy_from_user(start, buf, count);
        dev->data_size = count;
       
        return count;
}


struct file_operations scull_fops = {
        .owner =    THIS_MODULE,
//        .llseek =   scull_llseek,
        .read =   scull_read,
        .write =    scull_write,
//        .ioctl =    scull_ioctl,
        .open =   scull_open,
        .release =scull_release,
};

/*
* Finally, the module stuff
*/

/*
* The cleanup function is used to handle initialization failures as well.
* Thefore, it must be careful to work correctly even if some of the items
* have not been initialized
*/
void scull_cleanup_module(void)
{

        dev_t devno = MKDEV(scull_major, scull_minor);


                        cdev_del(&scull_devices->cdev);
                        kfree(scull_devices);

        /* cleanup_module is never called if registering failed */
        unregister_chrdev_region(devno, 1);


}


/*
* Set up the char_dev structure for this device.
*/
static void scull_setup_cdev(struct scull_dev *dev)
{
        int err, devno = MKDEV(scull_major, scull_minor);
   
        cdev_init(&dev->cdev, &scull_fops);
        dev->cdev.owner = THIS_MODULE;
        dev->cdev.ops = &scull_fops;
        err = cdev_add (&dev->cdev, devno, 1);
        /* Fail gracefully if need be */
        if (err)
                printk(KERN_NOTICE "Error %d adding scull", err);
}


int scull_init_module(void)
{
        int result;
        dev_t dev = 0;

/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
        if (scull_major) {
                dev = MKDEV(scull_major, scull_minor);
                result = register_chrdev_region(dev, 1, "scull");
        } else {
                result = alloc_chrdev_region(&dev, scull_minor, 1,
                                "scull");
                scull_major = MAJOR(dev);
        }
        if (result < 0) {
                printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
                return result;
        }

      /*
       * allocate the devices -- we can't have them static, as the number
       * can be specified at load time
       */
        scull_devices = kmalloc(sizeof(struct scull_dev), GFP_KERNEL);
        if (!scull_devices) {
                result = -ENOMEM;
                goto fail;/* Make this more graceful */
        }
        memset(scull_devices, 0, sizeof(struct scull_dev));

      /* Initialize each device. */


                scull_setup_cdev(scull_devices);


        return 0; /* succeed */

fail:
        scull_cleanup_module();
        return result;
}

module_init(scull_init_module);
module_exit(scull_cleanup_module);
读和写函数都只针对一个固定的缓冲区操作...每次写都是从头开始写, 而缓冲区上有数据就直接从头开始读... 直接方便...
修改scull源码的Makefile以及加载和卸载模块脚本就可以用这个设备了...
如果向设备写入了内容...比如用echo/cp/dd等..
再用cat读设备的时候会一直读重复的数据... 因为read函数没有调整f_pos/ 无法指示是否已经读到文件尾了.. 当然这不是一个大问题...
只是演示而已...

dreamice 发表于 2010-09-14 08:53

回复 1# PCliangtao


    不错,其实我也觉得尤其对于初学者来说,重点是理解驱动怎么写,ldd这个scull还是有点繁琐。

PCliangtao 发表于 2010-09-14 09:04

回复 2# dreamice


   的确...因为我是在缓冲区读写管理方面发生了错误...   由于初学... 也不会太多调试技术..只好strace嘞.最后花了很多时间才找到错误的地方...

    所以就觉得没必要把功夫花再这个方面... 对于初学... 关键是驱动的框架。把框架掌握了其他的都功能就自己再去确定了...

dreamice 发表于 2010-09-14 10:24

回复 3# PCliangtao


    你可以结合《Linux驱动开发详解》来学习,这本书上面的例子都比较浅显易懂

Godbach 发表于 2010-09-14 19:48

简单的字符驱动,实现open,read,write,close,就可以了解大致框架了。至于实现的复杂程度,那就是细节了
页: [1]
查看完整版本: LDD字符驱动程序scull修改