免费注册 查看新帖 |

Chinaunix

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

linux2.6 字符设备驱动程序 [复制链接]

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

头文件demo.h

#ifndef _DEMO_H_
#define _DEMO_H_
#include linux/ioctl.h> /* needed for the _IOW etc stuff used later */
/********************************************************
* Macros to help debugging
********************************************************/
#undef PDEBUG /* undef it, just in case */
#ifdef DEMO_DEBUG
#ifdef __KERNEL__
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "DEMO: " fmt, ## args)
#else//usr space
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
#endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
//设备号
#define DEMO_MAJOR 50
#define DEMO_MINOR 0
#define COMMAND1 1
#define COMMAND2 2
//自己定义的设备结构
struct DEMO_dev
{
    struct cdev cdev; /* Char device structure */
};
//函数申明 原来Linux驱动程序设计这么简单 只需要实现这么几个函数就可以了
int DEMO_open(struct inode *inode, struct file *filp)
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count,
                   loff_t *f_pos);
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count,
                    loff_t *f_pos);
loff_t DEMO_llseek(struct file *filp, loff_t off, int whence);
int DEMO_ioctl(struct inode *inode, struct file *filp,
                    unsigned int cmd, unsigned long arg);
int DEMO_release(struct inode *inode, struct file *filp)
#endif /* _DEMO_H_ */

源文件demo.c

#include linux/module.h>
#include linux/kernel.h>
#include linux/fs.h>
#include linux/errno.h>
#include linux/types.h>
#include linux/fcntl.h>
#include linux/cdev.h>
#include linux/version.h>
#include linux/vmalloc.h>
#include linux/ctype.h>
#include linux/pagemap.h>
#include "demo.h"
MODULE_AUTHOR("demo");
MODULE_LICENSE("GPL");
struct DEMO_dev *DEMO_devices;
//申明自己定义的设备结构的指针
static unsigned char demo_inc=0;
//计数 记录此设备被打开的次数
static u8 demoBuffer[256]; //8位无符号整型,相当于unsigned char
int DEMO_open(struct inode *inode, struct file *filp)
{
    struct DEMO_dev *dev;
    //只允许打开设备一次
    if(demo_inc>0)return -ERESTARTSYS;
    demo_inc++;
    dev = container_of(inode->i_cdev, struct DEMO_dev, cdev);
    filp->private_data = dev;
    return 0;
}
int DEMO_release(struct inode *inode, struct file *filp)
{
    demo_inc--;
    return 0;
}
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
    int result;
    loff_t pos= *f_pos; /* 文件的读写位置 */
    if(pos>=256)
    {
        result=0;
        goto out;
    }
    if(count>(256-pos))
    {
        count=256-pos;
    }
    pos += count;
   
    if (copy_to_user(buf,demoBuffer+*f_pos,count))
    {
       count=-EFAULT; /* 把数据写到应用程序空间 */
       goto out;
    }
   
    *f_pos = pos; /* 改变文件的读写位置 */
out:
    return count;
}
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
    ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
    loff_t pos= *f_pos;
    if(pos>=256)
    {
         goto out;
    }
    //如果要写入的输入大于剩下的内存空间 就只写入剩下的空间数量 以防溢出
    if(count>(256-pos))
    {
        count=256-pos;
    }
    pos += count;
     //copy_from_user 将数据从用户空间复制到内核空间
    if (copy_from_user(demoBuffer+*f_pos, buf, count)) {
        retval = -EFAULT;
        goto out;
    }
    *f_pos = pos;
    return count;
out:
    return retval;
}
/*ioctl的作用 ioctl是用来控制设备的 ,unsigned int cmd就是发给设备的命令,ioctl或许是Linux下最庞杂的函数*/
int DEMO_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
{
    if(cmd==COMMAND1)
    {
           printk("ioctl command1 successfully\n");
           return 0;
    }
    if(cmd==COMMAND2)
    {
           printk("ioctl command2 successfully\n");
           return 0;
    }
   
    printk("ioctl error\n");
          return -EFAULT;
}
//llseek 实现随即存取 ,loff_t应该是一个无符号整型 记录文件指针偏移量的 这段代码参考用户态的seek()就不难理解
loff_t DEMO_llseek(struct file *filp, loff_t off, int whence)
{
    loff_t pos;
    pos = filp->f_pos;
    switch (whence)
    {
    case 0:
        pos = off;
        break;
    case 1:
        pos += off;
        break;
    case 2:
    default:
        return -EINVAL;
    }
   
    if ((pos>256) || (pos0))
    {
        return -EINVAL;
    }
   
    return filp->f_pos=pos;
}
//file_operations这个结构体真是相当重要 需要搞清楚它的作用
struct file_operations DEMO_fops = {
    .owner = THIS_MODULE,
    .llseek = DEMO_llseek,
    .read = DEMO_read,
    .write = DEMO_write,
    .ioctl = DEMO_ioctl,
    .open = DEMO_open,
    .release = DEMO_release,
};
/*******************************************************
                MODULE ROUTINE
*******************************************************/
void DEMO_cleanup_module(void)
{
   
    dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR);
    if (DEMO_devices)
    {
        cdev_del(&DEMO_devices->cdev);
        //内核下的内存操作函数还真怪异 习惯就好
        kfree(DEMO_devices);
    }
    //调用unregister_chrdev_region()函数释放分配的一系列设备号
    unregister_chrdev_region(devno,1);
}
int DEMO_init_module(void)
{
    /*
    在内核中,dev_t类型(定义在中)用来保存设备编号——包括主设备号和次设备号。
    在内核2.6.0中,dev_t是一个32位的数,其中12位表示主设备号,20位表示次设备号。
    */
    int result;
    dev_t dev = 0;
    dev = MKDEV(DEMO_MAJOR, DEMO_MINOR);
    /*获取一个或多个设备编号来使用
     如果分配成功进行, register_chrdev_region 的返回值是 0.
     出错的情况下, 返回一个负的错误码, 你不能存取请求的区域.
    */
    result = register_chrdev_region(dev, 1, "DEMO");
    if (result  0)
    {
        printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR);
        return result;
    }
    //为自定义的设备结构申请空间
    DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL);
    if (!DEMO_devices)
    {
        result = -ENOMEM;
        goto fail;
    }
    //将新申请的空间清零
    memset(DEMO_devices, 0, sizeof(struct DEMO_dev));
   
   //初始化一个字符驱动 结构
    cdev_init(&DEMO_devices->cdev, &DEMO_fops);
    DEMO_devices->cdev.owner = THIS_MODULE;
    DEMO_devices->cdev.ops = &DEMO_fops;
   
    //在内核中添加字符驱动
    result = cdev_add (&DEMO_devices->cdev, dev, 1);
    if(result)
    {
        printk(KERN_NOTICE "Error %d adding DEMO\n", result);
        goto fail;
    }
    return 0;
fail:
   //失败了
    DEMO_cleanup_module();
    return result;
}
//模块入口和出口
module_init(DEMO_init_module);
module_exit(DEMO_cleanup_module);

Makefile

obj-m := demo.o
KERNELDIR := /home/ydzhang/linux-2.6.19
PWD := $(shell pwd)
all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

加载驱动,创建设备
加载驱动
# insmod demo.ko
然后使用lsmod 或 cat /proc/modules查看驱动是否加载
# mknod /dev/demo c 50 0 创建设备节点

测试代码 demo_test.c

#includesys/types.h>
#includeunistd.h>
#includefcntl.h>
#includelinux/rtc.h>
#includelinux/ioctl.h>
#includestdio.h>
#includestdlib.h>
#define COMMAND1 1
#define COMMAND2 2
int main()
{
    int fd;
    int i;
    char data[256];
    int retval;
    fd=open("/dev/demo",O_RDWR);
    if(fd==-1)
    {
        perror("error open\n");
        exit(-1);
    }
    printf("open /dev/smbus successfully\n");
    retval=ioctl(fd,COMMAND1,0);
    if(retval==-1)
    {
        perror("ioctl error\n");
        exit(-1);
    }
    printf("send command1 successfully\n");
    retval=write(fd,"abc",3);
    if(retval==-1)
    {
        perror("write error\n");
        exit(-1);
}
    retval=lseek(fd,0,0);
    if(retval==-1)
    {
        perror("lseek error\n");
        exit(-1);
    }
    retval=read(fd,data,3);
    data[3] = 0;
    printf("%s\n", data);
    if(retval==-1)
    {
        perror("read error\n");
        exit(-1);
    }
    printf("read successfully:%s\n",data);
    close(fd);
    return 0;
}


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

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP