简单字符设备驱动程序

理论知识参考Linux Device Driver, 3rd Edition。实验中使用主动分配主设备号,按照LDD3中说的最好是自动分配设备号,这里只是为了理解。

实验步骤如下:

(1)使用cat/proc/device查看字符设备主设备号,这里假设50主设备号没有使用而在本设备中使用。

创建字符设备文件节点:mknod/dev/mycdev c 50 0

修改设备文件权限:chmod 777/dev/mycdev

其中的mycdev为步骤(2)中要安装的模块名。

(2)编写驱动程序:注意代码中 #define MYCDEV_MAJOR (50)

/* =====================================================================

* Filename: mycdev.c

*

* Description:

*

* Version: 1.0 (02/26/2013)

* Created: xhzuoxin(xiahouzuoxin@163.com)

* Compiler: gcc

*======================================================================

*/

#include<linux/init.h>

#include<linux/module.h>

#include<linux/types.h>

#include<linux/fs.h>

#include<linux/mm.h>

#include<linux/sched.h>

#include<linux/cdev.h>

#include<asm/system.h>

#include<asm/io.h>

#include<asm/uaccess.h>

#include<linux/kernel.h>

#include<linux/errno.h>

#include<linux/slab.h>

MODULE_LICENSE("GPL");

#defineMYCDEV_MAJOR (50)

#defineMYCDEV_SIZE 0x1000

staticdev_t mycdev_major = MYCDEV_MAJOR;

structmycdev {

struct cdev cdev;

unsigned charmem[255];

};

//struct mycdev dev;

structmycdev *devp;

static int mycdev_open(structinode *inode,struct file *fp)

{

fp->private_data= devp;

return 0;

}

static int mycdev_release(structinode *inode,struct file *fp)

{

return 0;

}

staticssize_t mycdev_read(struct file *fp,char __user *buf, size_t size, loff_t *pos)

{

unsigned long p =*pos;

unsigned int count =size;

struct mycdev *tmp_dev = fp->private_data;

if (p >= MYCDEV_SIZE) {

return -1;

}

if (count > MYCDEV_SIZE - p) {

count =MYCDEV_SIZE - p;

}

if (copy_to_user(buf, (void*)(tmp_dev->mem + p), count) != 0) {

printk("read error!\n");

return -1;

} else {

*pos +=count;

printk(KERN_INFO"read %d bytes from %ld\n", count, p);

}

return count;

}

staticssize_t mycdev_write(struct file *fp,const char __user*buf, size_t size, loff_t *pos)

{

unsigned long p =*pos;

unsigned int count =size;

struct mycdev *tmp_dev = fp->private_data;

if (p > MYCDEV_SIZE) {

return -1;

}

if (p > MYCDEV_SIZE - count) {

count =MYCDEV_SIZE - p;

}

if (copy_from_user((void*)(tmp_dev->mem + p), buf, count) != 0) {

return -1;

} else {

*pos +=count;

printk(KERN_INFO"write %d bytes from %ld\n", count, p);

}

return count;

}

staticloff_t mycdev_llseek(struct file *fp, loff_toff,int whence)

{

// structmycdev *dev = fp->private_data;

loff_tnew_pos = 0;

switch(whence) {

case SEEK_SET:

new_pos= off;

break;

case SEEK_CUR:

new_pos= fp->f_pos + off;

break;

case SEEK_END:

new_pos= MYCDEV_SIZE + off;

}

if (new_pos < 0) {

return -EINVAL;

} else {

fp->f_pos= new_pos;

return new_pos;

}

}

/* paddingg struct file operation */

static const structfile_operations mycdev_fops = {

.owner =THIS_MODULE,

.read =mycdev_read,

.write =mycdev_write,

.open =mycdev_open,

.release =mycdev_release,

.llseek =mycdev_llseek,

};

static void setup_mycdev(structmycdev *dev,int index)

{

int ret;

int devno = MKDEV(mycdev_major, index);

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

dev->cdev.owner= THIS_MODULE;

dev->cdev.ops= &mycdev_fops;

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

if (ret) {

printk("adding mycdev error!\n");

}

}

static int __init mycdev_init(void)

{

int ret;

dev_tdevno = 0;

if (mycdev_major) { /* 静态分配*/

devno =MKDEV(mycdev_major, 0);

ret =register_chrdev_region(devno, 1, "mycdev");

} else { /* 动态分配 */

ret =alloc_chrdev_region(&devno, 0, 1, "mycdev");

mycdev_major= MAJOR(devno);

}

devp =kmalloc(sizeof(structmycdev), GFP_KERNEL);

if (!devp) {

ret =-ENOMEM;

unregister_chrdev_region(devno,1);

return ret;

}

memset(devp,0, sizeof(structmycdev));

setup_mycdev(devp,0);

return 0;

}

static void __exit mycdev_exit(void)

{

printk("mycdev module is leaving...\n");

cdev_del(&devp->cdev);

kfree(devp);

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

}

module_init(mycdev_init);

module_exit(mycdev_exit);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/c16ab294b611fbab13f44542a5d705f2.html