免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 681 | 回复: 0
打印 上一主题 下一主题

linux设备驱动程序(3rd)第3章学习 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-05-26 16:08 |只看该作者 |倒序浏览

                一 字符设备驱动程序字符设备驱动编写的范例:
1 获取主设备号
文中兼容传统的静态分配和推荐使用的动态分配:
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,
                "cddt");
        scull_major = MAJOR(dev);
    }
2 注册字符设备
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);
}
3 编写file_operation中需要的各种函数
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,
};
4 字符驱动的注销
void scull_cleanup_module(void)
{
    dev_t devno = MKDEV(scull_major, scull_minor);

    /* Get rid of our char dev entries */
    if (scull_devices) {         
        scull_trim(scull_devices);
        cdev_del(&scull_devices->cdev);         
        kfree(scull_devices);
    }
    /* cleanup_module is never called if registering failed */
    unregister_chrdev_region(devno, 1);
}
书上的例子代码有些过于复杂,我删除了proc调试部分,并且是设备只有1个(原文是4个),不需要什么scull_load脚本,删除了ioctl函数。
#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;
int scull_quantum = SCULL_QUANTUM;
int scull_qset =    SCULL_QSET;
module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset, int, S_IRUGO);
MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");
struct scull_dev *scull_devices;    /* allocated in scull_init_module */
/*
* Empty out the scull device; must be called with the device
* semaphore held.
*/
int scull_trim(struct scull_dev *dev)
{
    struct scull_qset *next, *dptr;
    int qset = dev->qset;   /* "dev" is not-null */
    int i;
    for (dptr = dev->data; dptr; dptr = next) { /* all the list items */
        if (dptr->data) {
            for (i = 0; i  qset; i++)
                kfree(dptr->data);
            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) {
        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
        scull_trim(dev); /* ignore errors */
        up(&dev->sem);
    }
    return 0;          /* success */
}
int scull_release(struct inode *inode, struct file *filp)
{
    return 0;
}
/*
* Follow the list, reach the the number 'n' qset.
*/
struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
    struct scull_qset *qs = dev->data;
        /* Allocate first qset explicitly if need be */
    if (! qs) {
        qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
        if (qs == NULL)
            return NULL;  /* Never mind */
        memset(qs, 0, sizeof(struct scull_qset));
    }
    /* Then follow the list */
    while (n--) {
        if (!qs->next) {
            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
            if (qs->next == NULL)
                return NULL;  /* Never mind */
            memset(qs->next, 0, sizeof(struct scull_qset));
        }
        qs = qs->next;
        continue;
    }
    return qs;
}
/*
* Data management: read and write
*/
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
    struct scull_dev *dev = filp->private_data;
    struct scull_qset *dptr;    /* the first listitem */
    int quantum = dev->quantum, qset = dev->qset;
    int itemsize = quantum * qset; /* how many bytes in the listitem */
    int item, s_pos, q_pos, rest;
    ssize_t retval = 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;/*get the posion in a quantum*/
    /* follow the list up to the right position (defined elsewhere) */
    dptr = scull_follow(dev, item);
    if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
        goto out; /* don't fill holes */
    /* 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)) {
        retval = -EFAULT;
        goto out;
    }
    *f_pos += count;
    retval = count;
  out:
    up(&dev->sem);
    return retval;
}
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;
    struct scull_qset *dptr;
    int quantum = dev->quantum, qset = dev->qset;
    int itemsize = quantum * qset;
    int item, s_pos, q_pos, rest;
    ssize_t retval = -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 == NULL)
        goto out;
    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)) {
        retval = -EFAULT;
        goto out;
    }
    *f_pos += count;
    retval = count;
        /* update the size */
    if (dev->size  *f_pos)
        dev->size = *f_pos;
  out:
    up(&dev->sem);
    return retval;
}
/*
* The "extended" operations -- only seek
*/
loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
    struct scull_dev *dev = filp->private_data;
    loff_t newpos;
    switch(whence) {
      case 0: /* SEEK_SET */
        newpos = off;
        break;
      case 1: /* SEEK_CUR */
        newpos = filp->f_pos + off;
        break;
      case 2: /* SEEK_END */
        newpos = dev->size + off;
        break;
      default: /* can't happen */
        return -EINVAL;
    }
    if (newpos  0) return -EINVAL;
    filp->f_pos = newpos;
    return newpos;
}
struct file_operations scull_fops = {
    .owner =    THIS_MODULE,
    .llseek =   scull_llseek,
    .read =     scull_read,
    .write =    scull_write,
    .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);
    /* Get rid of our char dev entries */
    if (scull_devices) {        
        scull_trim(scull_devices);
        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,
                "cddt");
        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(1 * sizeof(struct scull_dev), GFP_KERNEL);
    if (!scull_devices) {
        result = -ENOMEM;
        goto fail;  /* Make this more graceful */
    }
    memset(scull_devices, 0, 1 * sizeof(struct scull_dev));
      
    scull_devices->quantum = scull_quantum;
    scull_devices->qset = scull_qset;
    init_MUTEX(&scull_devices->sem);
    scull_setup_cdev(scull_devices);
    printk(KERN_WARNING "scull: insert module over\n");
    return 0; /* succeed */
  fail:
    scull_cleanup_module();
    return result;
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);
二 cddt的测试
scull的例子有些过于复杂的数据结构增加了读者阅读的难度,难道是老外的逻辑能力很强吗?
测试驱动程序,我把scull改成cddt(charactor device driver test)
/lib/modules # ls  > /dev/cddt
/lib/modules # cat /dev/cddt
2.6.14.7-intc1-bs3
2.6.14.7-tiny1
2.6.17
cddt.ko
scull_load
/lib # cat > /dev/cddt  
dfff
cddt: write item=0x0,spos=0x0,qpos=0x0,count=0x5
dferew
cddt: write item=0x0,spos=0x0,qpos=0x5,count=0x7
rewwweqefdsfgfgftre
cddt: write item=0x0,spos=0x0,qpos=0xc,count=0x14
cddt: write item=0x0,spos=0x0,qpos=0x20,count=0x1
/lib # cat  /dev/cddt
cddt: read item=0x0,spos=0x0,qpos=0x0,count=0x21
dfff
dferew
rewwweqefdsfgfgftre
每次写都会把上次打开驱动后写的覆盖掉
下面的输出
/lib # ls -l > /dev/cddt
cddt: write item=0x0,spos=0x0,qpos=0x0,count=0xfa0
cddt: write item=0x0,spos=0x1,qpos=0x0,count=0x23
/lib # cat  /dev/cddt
cddt: read item=0x0,spos=0x0,qpos=0x0,count=0xfa0
cddt: read item=0x0,spos=0x1,qpos=0x0,count=0x23
lrwxrwxrwx    1 buildsla 1000            8 May 14 11:32 cpp -> /bin/cpp
-rwxr-xr-x    1 buildsla 1000       598021 May 16  2007 ld-2.5.so
lrwxrwxrwx    1 buildsla 1000            9 May 14 11:32 ld-linux.so.3 -> ld-2.5.so
-rw-r--r--    1 buildsla 1000        11056 May 16  2007 libBrokenLocale-2.5.so
lrwxrwxrwx    1 buildsla 1000           22 May 14 11:32 libBrokenLocale.so.1 -> li
......
count表示数量,item表示第几个量子集,spos表示的是在某个量子集中第几个量子,qpos是表示在这个量子中的位置。
如果我的理解没有错误的话,scull里面每次读写的最大值是量子大小(oxfa0=4000),剩余部分会被系统多次调用scull_read函数读取。
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/60303/showart_706980.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP