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

linux 一个简单的字符设备驱动例子

2012-05-08 11:01 573 查看
先包含这些头文件

#include <linux/module.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/mm.h>

#include <linux/sched.h>

#include <linux/init.h>

#include <linux/cdev.h>

#include <asm/io.h>

#include <asm/system.h>

#include <asm/uaccess.h>

#define BUFFERSIZE 200

#define DEVICE_MAJOR 250 /*设置一个主设备号*/

static int device_major = DEVICE_MAJOR;

定义一个与字符设备对应的结构体

struct my_cdev

{

struct cdev cdev; /*cdev结构体,与字符设备对应*/

/*下面可以定义一些与字符设备相关的数据*/

usigned char mem[BUFFERSIZE];

};

struct my_cdev *my_cdevp; /*设备结构体指针*/

int my_cdev_open( struct inode *node, struct file *filp )

{

/*将设备结构体指针赋给文件私有数据指针*/

filp->private_data = my_cdevp /*这样可以通过文件私有数据指针得到设备结构体*/

return 0;

}

int my_cdev_release( struct inode *node, struct file *filp )

{

return 0;

}

static size_t my_cdev_read( struct file *filp, char __user *buf, size_t size, loff_t *ppos )

{

unsigned long p = *ppos; /*文件当前位置*/

unsigned int count = size; /*要读取的长度*/

int ret = 0;

struct my_cdev *dev = filp->private_data; /*通过文件私有数据指针得到设备结构体,和前面的open对应*/

if ( p >= BUFFERSIZE )

return count ? -ENXIO:0;

if ( count > BUFFERSIZE - p )

count = BUFFERSIZE - p;

/*内核空间->用户空间*/

if ( copy_to_user(buf, (void *)(dev->mem + p), count) )

{

ret = -EFAULT;

}

else

{

*ppos += count;

ret = count;

}

return ret;

}

static size_t my_cdev_write( struct file *filp, const char __user *buf, size_t size, loff_t *ppos )

{

unsigned long p = *ppos;

unsigned int count = size;

int ret = 0;

struce my_cdev *dev = filp->private_data;

if( p >= BUFFERSIZE )

return count ? -ENIX : 0;

if (count > BUFFERSIZE - p )

count = BUFFERSIZE - p;

if ( copy_from_user( dev->mem + p, buf, count))

{

ret = -EFAULT;

}

else

{

*ppos += count;

ret = count;

}

return ret

}

static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig)

{

loff_t ret = 0;

switch (orig)

{

case 0:

if ( offset < 0 )

{

ret = -EINVAL;

break;

}

if (offset > BUFFERSIZE)

{

ret = -EINVAL;

break;

}

filp->f_pos = (unsigned int)offset;

ret = fips->f_pos;

break;

default:

ret = -EINVAL;

break;

}

return ret;

}

/*文件操作结构体*/

static const struct file_operstions my_cdev_fops =

{

.owner = THIS_MODULE,

.open = my_cdev_open,

.release = my_cdev_release,

.read = my_cdev_read,

.write = my_cdev_write,

.llseek = my_cdev_llseek,

};

/*初始化并注册cdev,就是注册我们自己的字符设备*/

static void my_cdev_setup( struct my_cdev *dev, int index )

{

int err;

dev_t devno = MKDEV(DEVICE_MAJOR, index);

cdev_init( &dev->cdev, &my_cdev_fops );

dev->cdev.owner = THIS_MODULE;

dev->cdev.ops = &my_cdev_fops; /*我认为在cdev_init里应该做过赋值,应该可以不用写*/

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

if (err)

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

}

static int __init my_cdev_init(void)

{

int result;

dev_t devno = MKDEV(DEVICE_MAJOR, index);

/*申请设备号*/

if ( device_major )

{

result = register_chrdev_region(devno, 1, "my_cdev");

}

else

{

result = alloc_chrdev_region( &devno, 0, 1, "my_cdev");

device_major = MAJOR(devno);

}

if ( result < 0 )

{

return result;

}

my_cdevp = kmalloc(sizeof(struct my_cdev), GFP_KERNEL);

if ( !my_cdevp )

{

result = -ENOMEM;

goto fail_malloc;

}

memset(my_cdevp, 0, sizeof(struct my_cdev));

my_cdev_setup(my_cdevp, 0);

return 0;

fail_malloc:

unregister_chrdev_region(devno, 1);

return result;

}

static void __exit my_cdev_exit(void)

{

cdev_del(&my_cdevp->cdev);

kfree(my_cdevp);

unregister_chrdev_region(MKDEV(device_major, 0), 1);

}

MODULE_AUTHOR("Song Baohua");

MODULE_LICENSE("Dual BSD/GPL");

module_init(my_cdev_init);

module_exit(my_cdev_exit);

*********************************************************************************

然后可以写一个简单的内核模块的Makefile,编译make后生成mycdev.ko文件,insmod mycdev.ko,

装上我们自己的驱动,注意有可能在装的时候提示说device busy什么的,这就是我们前面指定的主设备号现在

有设备在用,我们就在重新指定一个在编译。

那怎么看有那些设备呢?可以用cat /proc/devices,就可以查看已经有哪些主设备号已被占用。

然后就可以自己先创建一个虚拟的字符设备mknod /dev/mycdev c 250 0

******************************************************

下面就可以自己写一个应用程序来看我们自己的字符设备驱动是否OK。

#include <stdio.h>

#include <fctl.h>

#include <string.h>

#include <sys/stat.h>

#define BUFFERSIZE 200

int main( void )

{

int fp = 0 ;

char str[BUFFERSIZE];

fp = open( "/dev/mycdev", O_RDWR, S_IRUSR|S_IWUSR );

if ( !fp )

{

printf("Open device failed\n");

return -1;

}

write( fp, "Hello, my devices", strlen("Hello, my devices") );

lseek( fp, 0, 0 );/*修改字符设备里字符数组的位置,将字符数据位置设到开始的位置,不然下面的read操作将读不到数据*/

read( fp, str, BUFFERSIZE );

printf("Read content: %s\n", str );

close(fp);

}

gcc -o sample sample.c

最后运行./sample

应该会输出:Read content: Hello, my devices

总结一下,我个人觉得字符设备驱动相关的API和数据结构。

1. struct cdev 一个设备对应一个这个的数据结构,结构体是重要的两个字段ops 和dev(设备号)

2. struct file_opertions 文件操作结构体

3. cdev_init(struct cdev *, struct file_opertions *) 主要就是把字符设备和对这个设备的文件操作结构体对应起来

4. cdev_add(struct cdev *, dev_t, unsigned) 注册设备

5. register_chrdev_region(dev_t, unsigned, const char *name)/alloc_chrdev_region() 申请设备号,为注册设备(cdev_add())准备

6. 就是内核空间的数据和用户空间的数据交换
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: