Linux设备驱动程序学习笔记07:字符设备驱动程序V

本文详细介绍了如何实现和测试Linux字符设备驱动程序,包括驱动程序的源码解析、测试程序编写、设备节点创建及操作方法。通过实例演示了如何使用字符设备进行读写操作、内存清零及内存位置跳转等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们的字符设备的驱动程序源码如下:

/*  
 memdev.c : ldd

	Author : moon.cheng.2014@gmail.com  
	Date   : 2014-08-05
	Version: 1.0
	
	This program is a demo program for linux device drivers created by moon. 
	It is a driver for a segment of memory.You can use the interface it supply 
	to operate the memory. Such as read,write or clear it.
	
	If you find some bugs or you have some advices. Send me a email pls!
	
	This program is free software; you can redistribute it and/or modify
<span style="white-space:pre">	</span>it under the terms of the GNU General Public License as published by
<span style="white-space:pre">	</span>the Free Software Foundation; either version 2 of the License, or
<span style="white-space:pre">	</span>(at your option) any later version.
 	
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/uaccess.h>/*copy_to_user & copy_from_user*/

#define M_DEBUG 1  /*debug output infomation switch*/
#define MEM_SIZE 512  /*memory size*/
#define MAJOR_DEVNO 0 /*use 0 to use malloc_chrdev_region*/

static int major_devno = MAJOR_DEVNO;	 
static struct memdev *devp;
/*device struct*/
struct memdev {
	struct cdev cdev;
	unsigned char mem[MEM_SIZE];
};


loff_t memdev_llseek(struct file *fp, loff_t off, int ori)
{
	int ret;

#if M_DEBUG
	printk(KERN_INFO"%s:%d off = %d ori = %d\n", __func__, __LINE__,off, ori);
#endif
	switch (ori) {
		case SEEK_SET:
			if (off < 0 || off > MEM_SIZE) {
				return -EINVAL;
			}
			fp -> f_pos = off;
			ret = fp -> f_pos;
			break;
		case SEEK_CUR:
			if ((fp->f_pos + off < 0) || (fp->f_pos + off > MEM_SIZE)) {
				return -EINVAL;
			}
			fp -> f_pos += off;	
			ret = fp -> f_pos;
			break;
		case SEEK_END:
			if (off < 0 || off > MEM_SIZE) {
				return -EINVAL;
			}
			fp -> f_pos = MEM_SIZE - off;
			ret = fp -> f_pos;
			break;
		default : 
			return -EINVAL;
	}
	return ret;
}

ssize_t memdev_read(struct file *fp, char __user *buff, size_t count, loff_t *f_pos)
{
	struct memdev *devp = fp->private_data;
	unsigned int size = count;
	unsigned long p = *f_pos;
	int ret = 0;
#if M_DEBUG
	printk(KERN_INFO"%s:%d *f_pos = %d size = %d\n", __func__, __LINE__, p, size);
#endif
	if ( p > MEM_SIZE) {
		return -EINVAL;
	}
	if (p + size > MEM_SIZE) {
		size = MEM_SIZE - p;
	}

	if (copy_to_user(buff, devp->mem + p, size)) {
		ret = -EFAULT;
	} else {
		ret = size;
		*f_pos += size;
#if M_DEBUG
		printk(KERN_INFO"%s:%d read %d bytes from mem!\n", __func__, __LINE__, size);
#endif
	}

	return ret;
}

ssize_t memdev_write(struct file *fp, const char __user *buff, size_t count, loff_t *f_pos)
{
	struct memdev *devp = fp->private_data;
	unsigned int size = count;
	unsigned long p = *f_pos;
	int ret = 0;
#if M_DEBUG
	printk(KERN_INFO"%s:%d *f_pos = %d size = %d\n", __func__, __LINE__, p, size);
#endif
	if ( p > MEM_SIZE) {
		return -EINVAL;
	}
	if (p + size > MEM_SIZE) {
		size = MEM_SIZE - p;
	}

	if (copy_from_user(devp->mem + p, buff, size)){
		ret = -EFAULT;
	} else {
		ret = size;
		*f_pos += size;
#if M_DEBUG
		printk(KERN_INFO"%s:%d write %d bytes to mem!\n", __func__, __LINE__, size);
#endif
	}
	
	return ret;
}

int memdev_ioctl(struct inode *nodep, struct file *fp, unsigned int cmd, unsigned long arg)
{
	int ret;
	struct memdev *devp;

#if M_DEBUG
	printk(KERN_INFO"%s:%d cmd = %d \n", __func__, __LINE__, cmd);
#endif
	switch (cmd) {
		case 0 : 
			devp = fp -> private_data;
			 memset(devp->mem, 0, MEM_SIZE);
			break;
		default : ret = -EINVAL;
	}
	return 0;
}

int memdev_open(struct inode *nodep, struct file *fp)
{
	struct memdev *devp;

	devp = container_of(nodep->i_cdev, struct memdev, cdev);
	fp->private_data = devp;
#if M_DEBUG
	printk(KERN_INFO"%s:%d you have open the device\n", __func__, __LINE__);
#endif
	return 0;
}


int memdev_release(struct inode *nodep, struct file *fp)
{
#if M_DEBUG
	printk(KERN_INFO"%s:%d you have release the device\n", __func__, __LINE__);
#endif
	return 0;
}


/*file_operations */
static struct file_operations memdev_fops = {
	.owner = THIS_MODULE,
	.llseek = memdev_llseek,
	.read = memdev_read,
	.write = memdev_write,
	.ioctl = memdev_ioctl,
	.open = memdev_open,
	.release = memdev_release,
};

static int __init memdev_init(void)
{
	int ret;
	dev_t devno;

/*apply for device no*/
	if (major_devno) {
	   devno=MKDEV(major_devno, 0);
	   ret = register_chrdev_region(devno, 0, "memdev");
	} else {
		ret = alloc_chrdev_region(&devno, 0, 1, "memdev");
		major_devno = MAJOR(devno);
	}
	if (!!ret) {
		printk(KERN_ERR"arrage device no erro!\n");
		return ret;
	}

/*apply for device struct*/
	devp = kmalloc(sizeof(struct memdev), GFP_KERNEL);
	if (!devp) {
		printk(KERN_ERR"malloc memory to devp erro!\n");
		unregister_chrdev_region(devno, 1);
		return -ENOMEM;
	}
	memset(devp, 0, sizeof(struct memdev));

/*Init our device struct*/	
	cdev_init(&devp->cdev, &memdev_fops);
	devp->cdev.owner = THIS_MODULE;
	ret = cdev_add(&devp->cdev, devno, 1);
	if (!!ret) {
		printk(KERN_ERR"register the device to system erro!\n");
		kfree(devp);
		unregister_chrdev_region(devno, 1);
		return ret;
	}

	return 0;

}

static void __exit memdev_exit(void)
{
	cdev_del(&devp->cdev);
	kfree(devp);
	unregister_chrdev_region(MKDEV(major_devno, 0), 1);
}

module_init(memdev_init);
module_exit(memdev_exit);

MODULE_AUTHOR("moon.cheng.2014@gmail.com");
MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("This is a charactor device demo driver");

同时我们为其写了一个小的测试程序,源代码如下:

/*  
 memts.c : ldd

	Author : moon.cheng.2014@gmail.com  
	Date   : 2014-08-05
	Version: 1.0
	
	This program is a testing program for charactor drivers.You can use
	it to open/close/read/write/llseek/ioctl a charactor devices. The 
	origin purpose for this program is to test the functions in memdev.c
	
	
	If you find some bugs or you have some advices. Send me a email pls!
	
	This program is free software; you can redistribute it and/or modify
<span style="white-space:pre">	</span>it under the terms of the GNU General Public License as published by
<span style="white-space:pre">	</span>the Free Software Foundation; either version 2 of the License, or
<span style="white-space:pre">	</span>(at your option) any later version.
 	
 */
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

void m_read(int fd)
{
	int r_number;
	char buff[33];

	memset(buff, 0, 32);

	printf("input the number of bytes you want to read pls[1-32]:");
	scanf("%d", &r_number);
	getchar();
	
	if (r_number <= 0 || r_number > 32) {
		r_number = 4;
	}
	r_number = read(fd, buff, r_number);

	buff[r_number+1] = '\0';
	printf("%s\n", buff);
}

void m_write(int fd)
{
	char buff[32];
	printf("input the string you want to write pls:");
	memset(buff, 0, 32);
	fgets(buff, 32, stdin);
	write(fd, buff, strlen(buff)-1);
}

void m_seek(int fd)
{
	char seek_base;
	int offset;
	printf("syntax:_N N is the offset number\n");
	printf("       sN---seek from the begin\n");
	printf("       cN---seek from the current\n");
	printf("       eN---seek from the end\n");
	printf("input where to seek pls:");

	seek_base = getchar();
	scanf("%d", &offset);
	getchar();

	switch (seek_base){
		case 's':
			lseek(fd, offset, SEEK_SET);
			break;
		case 'c':
			lseek(fd, offset, SEEK_CUR);
			break;
		case 'e':
			lseek(fd, offset, SEEK_END);
			break;
		default:
			printf("Check you input!\n");
	}
}

int main(void)
{
	int fd = -1;
	char cmd;
	do {
		printf("[o-open|w-write|r-read|c-clear|s-seek|q-quit]:");
		cmd = getchar();
		getchar();
		switch (cmd) {
			case 'o':
				if (-1 == fd) 
					fd = open("./memdev", O_RDWR);
				break;
			case 'w':
				if (-1 == fd) {
					printf("you should run command o first!\n");
					break;
				}
				m_write(fd);	
				break;
			case 'r': 
				if (-1 == fd) {
					printf("you should run command o first!\n");
					break;
				}
				m_read(fd);
				break;
			case 'q':
				if (-1 != fd)
					close(fd);
				break;
			case 'c':
				if (-1 == fd) {
					printf("you should run command o first!\n");
					break;
				}
				ioctl(fd, 0);
				break;
			case 's':
				if (-1 == fd) {
					printf("you should run command o first!\n");
					break;
				}
				m_seek(fd);
				break;
			default:
				;
		}
	}while('q' != cmd);
	return 0;
}

编译好ko文件并将其insmod到系统之后,我们需要创建设备节点文件。通过查看/proc/devices来看系统给我们分配的设备节点:


可以看到我们的主设备号是250,通过mknod命令在当前目录下创建设备节点:


编译好memts程序后,我们就可以运行它来测试我们的字符设备了。打开字符设备的M_DEBUG宏,我们可以再打开一个终端通过dmesg来查看我们设置的打印消息。

下面的两张图是我们的测试输出以及通过dmesg看到的打印消息对照图:



还可以通过cat以及echo等命令,同我们的测试程序一起来操作设备文件。大家可以去尝试一下。到此,字符设备驱动程序的基本内容已经学完了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值