您的位置:首页 > 运维架构 > Linux

ldd(linux设备驱动程序)实验2:scull()

2016-07-12 19:49 330 查看
转自:http://www.cnblogs.com/yiru/archive/2012/11/29/2794658.html(博主:勒达与天鹅)

存档,仅供学习参考。

按照ldd第三版书上的网址,下载下来的代码是最终版的,没有按章节剥离,不方便读者自己实验。

以下是我手把手敲下的书上第二个实验scull的代码以及安装和测试的步骤。

按上篇搭建linux驱动开发环境(配合LDD阅读)做完实验1后,scull的安装很简单了,在任意位置保存下面两个源文件Makefile,scull.c,执行make,生成目标模块scull.ko,然后insmod
scull.ko。

lsmod, 看到第一个就是scull,且会生成这个文件夹/sys/module/scull,可以进去看看里面有什么。cat /proc/devices   可以看到"xxx scull"。因为我的scull驱动(源码贴在下面)是随机生成设备号,所以要进去看。

下面,我们就可以用mknod命令来申请设备文件了。

          mkdir  /dev/scull

           mknod /dev/scull/scull0 c xxx 0

    xxx必须是刚才在proc里看到的主设备号,此设备号是0。主、次设备号如果不符的话,这个命令不会执行失败,但是生成的设备在将来使用时会出这个错:scull0:No such device or address

      然后ls -li,看下新设备的主次设备号。

    到这步之后要注意力,如果你的驱动写得有问题,而编译时没有检测到的话,很可能在执行下面的步骤时使整个linux系统挂起,那只能切断虚拟机电源重新来过了。由于书上的scull代码极简,而官网下载的scull源码相对繁杂,我想用最简的来测试,书上的代码却不全,我只好半用书半用网上的源码,改了非常久。

    然后依据书上的“试试新设备”这一节来测试这个设备。可以用的命令有:cp, dd, 重新输入输出, free;还可以在驱动里写printk来跟踪测试。还可以用strace命令来监视系统调用。

解释下,mknod 的标准形式为:       mknod DEVNAME {b | c}  MAJOR  MINOR

       1,DEVNAME是要创建的设备文件名,如果想将设备文件放在一个特定的文件夹下,就需要先用mkdir在dev目录下新建一个目录;

       2, b和c 分别表示块设备和字符设备:

                  b表示系统从块设备中读取数据的时候,直接从内存的buffer中读取数据,而不经过磁盘;

                  c表示字符设备文件与设备传送数据的时候是以字符的形式传送,一次传送一个字符,比如打印机、终端都是以字符的形式传送数据;

       3,MAJOR和MINOR分别表示主设备号和次设备号:

             为了管理设备,系统为每个设备分配一个编号,一个设备号由主设备号和次设备号组成。主设备号标示某一种类的设备,次设备号用来区分同一类型的设备。 linux操作系统中为设备文件编号分配了32位无符号整数,其中前12位是主设备号,后20位为次设备号,所以在向系统申请设备文件时主设备号不好超过 4095,次设备号不好超过2^20 -1。

 

模块装好后,书本自带的代码中有个scull/scull_load用以装载,但这也是混装了全书的架构,而我们要的是剥离出来的第三章的实验,如果想试一试,可以在实验完毕后(因为这会造成难以估计的混乱)运行它。

 

 

 

Makefile

ifneq ($(KERNELRELEASE),)

obj-m := scull.o

else

PWD := $(shell pwd)

KVER ?= $(shell uname -r)

KDIR := /lib/modules/$(KVER)/build

all:

    $(MAKE) -C $(KDIR) M=$(PWD)

clean:

    rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.symvers *.order

endif

 

 

 

scull.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.

 *

 */

#ifndef __KERNEL__

#  define __KERNEL__

#endif

#ifndef MODULE

#  define MODULE

#endif

//#include <linux/config.h>

#include <linux/slab.h>   /* kmalloc() */

#include <asm/uaccess.h>

#include <linux/module.h>

#include <linux/kernel.h>   /* printk() */

#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 <asm/system.h>     /* cli(), *_flags */

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

int scull_major;

int scull_nr_devs;

int scull_quantum;

int scull_qset;

// struct file_operations *scull_fop_array[]={

//     &scull_fops,      /* type 0 */

//     &scull_priv_fops, /* type 1 */

//     &scull_pipe_fops,  type 2 

//     &scull_sngl_fops, /* type 3 */

//     &scull_user_fops, /* type 4 */

//     &scull_wusr_fops  /* type 5 */

// };

// #define SCULL_MAX_TYPE 5

// static void scull_setup_cdev(struct scull_dev *dev, int index){

//     int err, devno = MKDEV(scull_major, scull_minor + index);

//     cdev_init(&dev->cdev, &scull_fops):

//     dev->cdev.owner = THIS_MODULE;

//     dev->cdev.ops = &scull_fops;

//     err = cdev_add (&dev->cdev, devno, 1);

//     if(err)

//         printk(KERN_NOTICE "Error %d adding scull%d", err, index);

// }

struct cdev {

    struct kobject kobj;

    struct module *owner;

    const struct file_operations *ops;

    struct list_head list;

    dev_t dev;

    unsigned int count;

};

struct scull_dev{

    struct scull_qset *data;

    int quantum;

    int qset;

    unsigned long size;

    unsigned int access_key;

    struct semaphore sem;

    struct cdev cdev;

    struct scull_dev* next;

}scull_dev;

/* memory management */

struct scull_qset{

    void **data;

    struct scull_qset* next;

};

int scull_trim(struct scull_dev* dev){

    struct scull_qset *next, *dptr;

    int qset = dev->qset;

    int i;

    for(dptr = dev->data; dptr; dptr = next){

        if(dptr->data){

            for(i=0; i<qset; i++){

                kfree(dptr->data[i]);

            }

            kfree(dptr->data);

            dptr->data = NULL;

        }

        next = dptr->next;

        kfree(dptr);

    }

    dev->size = 0;

    dev->quantum = scull_quantum;

    dev->qset = scull_qset;

    dev->data = NULL;

    return 0;

}

/*

 * Open and close

 */

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 */

    /* now trim to 0 the length of the device if open was write-only */

    if( (filp->f_flags & O_ACCMODE) == O_WRONLY){

        scull_trim(dev); /* ignore errors */

    }

    return 0;   /* success */

}

int scull_release(struct inode *inode, struct file *filp)

{

    return 0;

}

/*

 * Follow the list 

 */

struct scull_dev *scull_follow(struct scull_dev *dev, int n)

{

    while (n--) {

        if (!dev->next) {

            dev->next = kmalloc(sizeof(scull_dev), GFP_KERNEL);

            memset(dev->next, 0, sizeof(scull_dev));

        }

        dev = dev->next;

        continue;

    }

    return dev;

}

/*

 * Data management: read and write

 */

ssize_t scull_read(struct file *filp, char *buf, size_t count,

                loff_t *f_pos)

{

    struct scull_dev *dev = filp->private_data; /* the first listitem */

    struct scull_qset *dptr;

    int quantum = dev->quantum;

    int qset = dev->qset;

    int itemsize = quantum * qset; /* how many bytes in the listitem */

    int item, s_pos, q_pos, rest;

    ssize_t ret = 0;

    if (down_interruptible(&dev->sem))

            return -ERESTARTSYS;

    if (*f_pos >= dev->size)

        goto out;

    if (*f_pos + count > dev->size)

        count = dev->size - *f_pos;

    /* find listitem, qset index, and offset in the quantum */

    item = (long)*f_pos / itemsize;

    rest = (long)*f_pos % itemsize;

    s_pos = rest / quantum; q_pos = rest % quantum;

    /* follow the list up to the right position (defined elsewhere) */

    dptr = scull_follow(dev, item);

    if (!dptr->data)

        goto out; /* don't fill holes */

    if (!dptr->data[s_pos])

        goto out;

    /* read only up to the end of this quantum */

    if (count > quantum - q_pos)

        count = quantum - q_pos;

    if (copy_to_user(buf, dptr->data[s_pos]+q_pos, count)) {

        ret = -EFAULT;

    goto out;

    }

    *f_pos += count;

    ret = count;

 out:

    up(&dev->sem);

    return ret;

}

ssize_t scull_write(struct file *filp, const char *buf, size_t count,

                loff_t *f_pos)

{

    struct scull_dev *dev = filp->private_data;

    struct scull_qset *dptr;

    int quantum = dev->quantum;

    int qset = dev->qset;

    int itemsize = quantum * qset;

    int item, s_pos, q_pos, rest;

    ssize_t ret = -ENOMEM; /* value used in "goto out" statements */

    if (down_interruptible(&dev->sem))

            return -ERESTARTSYS;

    /* find listitem, qset index and offset in the quantum */

    item = (long)*f_pos / itemsize;

    rest = (long)*f_pos % itemsize;

    s_pos = rest / quantum; q_pos = rest % quantum;

    /* follow the list up to the right position */

    dptr = scull_follow(dev, item);

    if (!dptr->data) {

        dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);

        if (!dptr->data)

            goto out;

        memset(dptr->data, 0, qset * sizeof(char *));

    }

    if (!dptr->data[s_pos]) {

        dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);

        if (!dptr->data[s_pos])

            goto out;

    }

    /* write only up to the end of this quantum */

    if (count > quantum - q_pos)

        count = quantum - q_pos;

    if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {

        ret = -EFAULT;

    goto out;

    }

    *f_pos += count;

    ret = count;

    /* update the size */

    if (dev->size < *f_pos)

        dev-> size = *f_pos;

  out:

    up(&dev->sem);

    return ret;

}

/*

 * 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

 */

static void scull_cleanup_module(void){

//     int i;

// #ifndef CONFIG_DEVFS_FS

//     /* cleanup_module is never called if registering failed */

//     unregister_chrdev(scull_major, "scull");

// #endif

// #ifdef SCULL_DEBUG /* use proc only if debugging */

//     scull_remove_proc();

// #endif

//     if (scull_devices) {

//         for (i=0; i<scull_nr_devs; i++) {

//             scull_trim(scull_devices+i);

//             /* the following line is only used for devfs */

//             devfs_unregister(scull_devices[i].handle);

//         }

//         kfree(scull_devices);

//     }

//     /* and call the cleanup functions for friend devices */

//     scull_p_cleanup();

//     scull_access_cleanup();

//     /* once again, only for devfs */

//     devfs_unregister(scull_devfs_dir);

}

static int scull_init_module(void){

//     int result, i;

//     SET_MODULE_OWNER(&scull_fops);

// #ifdef CONFIG_DEVFS_FS

//     /* If we have devfs, create /dev/scull to put files in there */

//     scull_devfs_dir = devfs_mk_dir(NULL, "scull", NULL);

//     if (!scull_devfs_dir) return -EBUSY; /* problem */

// #else /* no devfs, do it the "classic" way  */    

//     /*

//      * Register your major, and accept a dynamic number. This is the

//      * first thing to do, in order to avoid releasing other module's

//      * fops in scull_cleanup_module()

//      */

//     result = register_chrdev(scull_major, "scull", &scull_fops);

//     if (result < 0) {

//         printk(KERN_WARNING "scull: can't get major %d\n",scull_major);

//         return result;

//     }

//     if (scull_major == 0) scull_major = result; /* dynamic */

// #endif /* CONFIG_DEVFS_FS */

//     /* 

//      * allocate the devices -- we can't have them static, as the number

//      * can be specified at load time

//      */

//     scull_devices = kmalloc(scull_nr_devs * sizeof(scull_dev), GFP_KERNEL);

//     if (!scull_devices) {

//         result = -ENOMEM;

//         goto fail;

//     }

//     memset(scull_devices, 0, scull_nr_devs * sizeof(scull_dev));

//     for (i=0; i < scull_nr_devs; i++) {

//         scull_devices[i].quantum = scull_quantum;

//         scull_devices[i].qset = scull_qset;

//         sema_init(&scull_devices[i].sem, 1);

// #ifdef CONFIG_DEVFS_FS

//         sprintf(devname, "%i", i);

//         devfs_register(scull_devfs_dir, devname,

//                        DEVFS_FL_AUTO_DEVNUM,

//                        0, 0, S_IFCHR | S_IRUGO | S_IWUGO,

//                        &scull_fops,

//                        scull_devices+i);

// #endif  

//     }

//     /* At this point call the init function for any friend device */

//     if ( (result = scull_p_init()) )

//         goto fail;

//     if ( (result = scull_access_init()) )

//         goto fail;

//     /* ... */

// #ifndef SCULL_DEBUG

//     EXPORT_NO_SYMBOLS; /* otherwise, leave global symbols visible */

// #endif

// #ifdef SCULL_DEBUG /* only when debugging */

//     scull_create_proc();

// #endif

  struct file_operations scull_fops;

    register_chrdev(scull_major, "scull", &scull_fops);

    return 0; /* succeed */

//   fail:

//     scull_cleanup_module();

//     return result;

}

module_init(scull_init_module);

module_exit(scull_cleanup_module);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("DYYR");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux LDD3