LDD3《Linux设备驱动》中的最简单的字符设备驱动实现与测试

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情
源代码如下:
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>

#include <linux/cdev.h>
#include <asm/uaccess.h>
//#include "scull.h"

#define SCULL_MAJOR 0   /* dynamic major by default */
#define SCULL_NR_DEVS 4    /* scull0 through scull3 */
#define SCULL_P_NR_DEVS 4  /* scullpipe0 through scullpipe3 */
#define SCULL_QUANTUM 4000
#define SCULL_QSET    1000

struct scull_qset {
	void **data;
	struct scull_qset *next;
};

struct scull_dev {
	struct scull_qset *data;  /* Pointer to first quantum set */
	int quantum;              /* the current quantum size */
	int qset;                 /* the current array size */
	unsigned long size;       /* amount of data stored here */
	unsigned int access_key;  /* used by sculluid and scullpriv */
	struct semaphore sem;     /* mutual exclusion semaphore     */
	struct cdev cdev;	  /* Char device structure		*/
};

int	scull_major = SCULL_MAJOR;
int	scull_minor = 0;
int	scull_nr_devs = SCULL_NR_DEVS;
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_nr_devs,int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset,int,S_IRUGO);

MODULE_AUTHOR("BG2BKK");
MODULE_LICENSE("Dual BSD/GPL");


struct scull_dev *scull_devices;

int	scull_trim(struct scull_dev *dev)
{
	struct	scull_qset *next,*dptr;
	int	qset = dev->qset;
	int 	i;
	for(dptr = dev->data; dptr;dptr = next)
	{
		if(dptr->data){
			for (i=0;i<qset;i++)
				kfree(dptr->data[i]);
			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;
}

int	scull_open(struct inode *inode, struct file *filp)
{
	struct scull_dev *dev;
	dev = container_of(inode->i_cdev,struct scull_dev, cdev);
	filp->private_data	= dev;

	if((filp->f_flags & O_ACCMODE) == O_WRONLY){
		scull_trim(dev);
	}

	return 0;
}



int	scull_release(struct inode *inode,struct file *filp)
{
	printk(KERN_ALERT "scullrelease\n");
	return 0;
}

struct scull_qset *scull_follow(struct scull_dev *dev,int n)
{
	struct scull_qset  *qs = dev->data;

	if(!qs)
	{
		qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
		if(qs == NULL)
			return NULL;
		memset(qs,0,sizeof(struct scull_qset));
	}
	
	while(n--)
	{
		if( !qs->next ){
			qs->next = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
			if(qs->next == NULL)
				return NULL;
			memset(qs->next, 0 ,sizeof(struct scull_qset));
		}
		qs = qs->next;
		continue;
	}
	return qs;
}

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;
	int	quantum = dev->quantum, qset = dev->qset;
	int	itemsize = quantum*qset;
	int	item,s_pos,q_pos,rest;
	ssize_t	retval = 0;

	if(down_interruptible(&dev->sem))
		return -ERESTARTSYS;
//	printk("f_pos= %d\n",*f_pos);
//	printk("count= %d\n",count);
	if( *f_pos >= dev->size)
		goto out;
	if( *f_pos + count > dev->size)
		count = dev->size - *f_pos;
	item	= (long)*f_pos / itemsize;
	rest	= (long)*f_pos % itemsize;
	s_pos	= rest / quantum;
	q_pos	= rest % quantum;

	dptr	= scull_follow(dev,item);

	if(dptr == NULL || !dptr->data || !dptr->data[s_pos])
		goto out;
	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;
	}
//	printk("read scull: %d\n",count);
	*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;

	if(down_interruptible(&dev->sem))
		return -ERESTARTSYS;

	item	= (long)*f_pos / itemsize;
	rest	= (long)*f_pos % itemsize;
	s_pos	= rest / quantum;
	q_pos	= rest % quantum;

	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;
	}
	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;

	if(dev->size < *f_pos)
		dev->size = *f_pos;
out:
	up(&dev->sem);
	return retval;
}

int	scull_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
	return 0;
}

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:
			newpos = off;
			break;
		case 1:
			newpos = filp->f_pos + off;
			break;
		case 2:
			newpos = dev->size + off;
			break;
		default:
			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,
	.ioctl	= scull_ioctl,
	.open	= scull_open,
	.release= scull_release,
};

void 	scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major,scull_minor);

	if(scull_devices){
		for(i=0; i<scull_nr_devs;i++)
		{
			scull_trim(scull_devices + i);
			cdev_del(&scull_devices[i].cdev);
		}
		kfree(scull_devices);
	}
#ifdef	SCULL_DEBUG
//	scull_remove_proc();
#endif
	unregister_chrdev_region(devno,scull_nr_devs);

//	scull_p_cleanup();
//	scull_access_cleanup();
}

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err,devno=MKDEV(scull_major,scull_minor+index);
	cdev_init(&dev->cdev, &scull_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops	= &scull_fops;
	err = cdev_add(&dev->cdev, devno,1);
	if(err)
		printk(KERN_NOTICE "Error %d adding scull%d",err,index);
}

int	scull_init_module(void)
{
	int	result,i;
	dev_t 	dev=0;

	if(scull_major)
	{
		dev = MKDEV(scull_major,scull_minor);
		result = register_chrdev_region(dev,scull_nr_devs,"scull");
	}
	else{
		result = alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");	
		scull_major = MAJOR(dev);
	}
	if(result < 0){
		printk(KERN_WARNING "scull: can't get major %d\n",scull_major);
		return result;
	}
	printk(KERN_ALERT "hello scull	major %d\n minor %d\n",scull_major,scull_minor);
	

	scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev),GFP_KERNEL);
	if(!scull_devices){
		result = -ENOMEM;
		goto fail;
	}
	memset(scull_devices, 0,scull_nr_devs*sizeof(struct scull_dev));
	for(i=0; i< scull_nr_devs; i++){
		scull_devices[i].quantum = scull_quantum;
		scull_devices[i].qset = scull_qset;
		init_MUTEX(&scull_devices[i].sem);
		scull_setup_cdev(&scull_devices[i],i);
	}

	dev=MKDEV(scull_major,scull_minor+scull_nr_devs);
//	dev += scull_p_init(dev);
//	dev += scull_access_init(dev);
#ifdef	SCULL_DEBUG
	scull_create_proc();
#endif
	return 0;

fail:
	scull_cleanup_module();
	return result;
	
}

module_init(scull_init_module);
module_exit(scull_cleanup_module)

测试代码如下

/*************************************************************************
 *fileName:    test.c
 *description: test the myscull.c
 *author:      Hzc
 *create time: 2007-04-20
 *modify info: -
*************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>

/* device path */
char path[] = "/dev/sculldev";
int buf[40];
int rbuf[40];
int i;
int t=1;
int main()
{
int f = open(path, O_WRONLY);
if (f == -1)
{
printf("device open error!\n");
return 1;
}
printf("Input a string to write device \n");
//scanf("%s", buf);

for(i=0;i<40;i++)
{
	buf[i]=i;
	write(f, buf+i,1 );	 /* device wirte */
}

close(f);

f= open(path,O_RDONLY);
printf("Read the string from device...\n");
if (f == -1)
{
printf("device open error!\n");
return 1;
}
for(i=0;i<40;i++)
{
	read(f, rbuf+i, 1);	 /* device wirte */
}
for(i=0;i<40;i++)
	{
		printf("%d\t",rbuf[i]);
		if((i+1)%10 == 0) printf("\n");
	}	


close(f);
}


驱动加载脚本如下:

#!/bin/sh

insmod scull.ko
mknod /dev/sculldev c 252 0
驱动卸载脚本如下:
#!/bin/sh

rmmod scull.ko
rm /dev/sculldev



linux设备驱动第三篇:写一个简单的字符设备驱动 linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动。本篇借鉴LDD中的源码,实现一个硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存。 下面就开始学习如何写一个简单的字符设备驱动。首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作。 1、主设备号和次设备号 对于 阅读详情

相关推荐

Linux设备驱动程序》(第三版)的详细目录

本书的目标本书的组织结构本书的约定排版约定代码约定本书使用的内核版本获得帮助反馈1.1 什么是设备驱动程序1.1.1 驱动程序的角色1.1.2 字符设备、块设备和网络设备1.2 为什么要写驱动程序1.2.1 为新硬件写驱动程序1.2.2 访问内核服务1.2.3 内核开发者交流1.3 设备驱动程序的模块形式1.3.1 什么是内核模块1.3.2 模块的优点1.4 为什么要关心内核版本1.4.1 内核版本号1.4.2 内核版本的差异。

qq_40844444的博客 1133

Linux设备驱动LDD

Linux设备驱动LDDLinux设备的几个要点: 工作在内核态 硬件设备作为文件看待 使用文件接口对设备进行控制 Linux设备的分类(一种划分方式): 字符设备 块设备 网络设备 1. 了解设备 在Linux的/dev/目录(device缩写)下有我们的设备文件 以c开头的都是字符设备,b开头的都是块设备,后面依次有主、次设备号,主设备号用于标识设备种类(范围为1-255),次设备号用来标识不同的硬件设备,最后是相应的设备文件。 2. 设备驱动程序 功能完整的LDD结构(一般至少要包

panfengblog 1059

ldd3 驱动--测试scull

scull驱动模拟设备。编写读写设备的控制文件testscull.c向scull发送和接收数据。1 安装scull.ko  在ldd3驱动源码文件夹中,进入scull文件夹,make,生成scull.ko,运行其中的shell文件:scull_load,完成scull.ko的安装。  # cd /sys/module/scull/parameters/  #cat scull_ma

在水一方 2883

LDD学习笔记 -- Linux字符设备驱动

例如前面的伪字符设备,有一块内存数组(设备内存buffer)。当用户程序在该设备文件上发出read系统调用时,应该将数据从设备buffer传到用户buffer。设备创建时候在VFS注册设备号,虚拟文件系统,将设备文件的设备号驱动程序列表进行比较,选择正确的驱动程序,并将用户请求连接到对应驱动程序的文件操作方法。用户空间的指针不是完全可信的,用户地址空间有时可能无效,虚拟内存管理器可以交换出这些内存位置。字符设备指的是像内存区域这样的硬件组件,通常称为伪设备;可以动态申请主设备号,保证唯一性,传输设备号(

weixin_42040046的博客 1480

VoxCPM2完全实操指南:免费多语言语音合成声音克隆,从零跑通到生产部署

VoxCPM2 是一款开源的多语言语音合成(TTS)声音克隆工具:输入文本直接生成 48kHz 音频,覆盖 30 种语言和 9 种中文方言,还能凭一句描述"捏"出全新音色,或用几秒参考音频克隆指定声音。本文按"先跑通、再调优、后部署"的顺序,带你把安装、四种生成模式、原理、微调和常见问题一次讲清。 ## 1. 它适合解决什么问题,不适合什么 用语音合成工具时常见的三个卡点: - **语言覆

gitblog_00913的博客 777

如何将 Linux 设备驱动程序移植到现代内核

我很想立即开始编写我们的第一个 Hello World,但首先,我想指出的是,这方面有一本更现代的指南可以作为更简单的入门指南:《Linux 内核模块:编程指南》(LKMPG)。理想情况下,我们希望这些信息更容易理解,编写得更好,因为进入该领域的门槛仍然很高,而且关于该主题的通俗易懂(当然也是最新的)的书籍少之又少。坏消息是,LDD 中列出了一个有趣的设备,它实际上具有一些内核空间逻辑,并且对用户来说是可感知的。,该函数通知块设备请求已开始,并允许它们执行准备操作,例如启动一个计时器来计时请求的持续时间。

2501_93209230的博客 724

Linux设备驱动程序学习(三)——字符设备驱动程序

前面我们了解了Linux设备驱动程序的入门知识和准备工作,下面我们就将真正的进入Linux设备驱动程序的世界,下面就将LDD3下面的scull字符设备驱动程序的实现来学习字符设备驱动的特点以及如何在用户空间被调用,书籍和源码下载地址为链接: https://pan.baidu.com/s/1OB4z_vjlov2gHTwrCe7hjQ 提取码: b427 字符设备驱动程序 字符设备字符设备驱动程...

Alex_wu的博客 1340

LDD3 SCULL字符设备驱动搭建流程

本节对实现LDD3书中前六章的scull字符设备驱动的步骤进行了梳理。搭建步骤依据书中所讲内容,逐步完善。hello模块实现设备驱动的基础入门scull裸机模块的实现字符设备驱动开发方式pipe实现access.c实现我的实现在中,其中examples是官方例程,scull是我基于linux6.2的实现,scull/test是一些测试程序(文件忘记clean,如果文件太乱可以先make clean一下,使用时再make)。

m0_61455436的博客 257

Linux Device Drivers 3 (LDD3) 项目教程

Linux Device Drivers 3 (LDD3) 是一个开源项目,旨在提供现代内核兼容的设备驱动程序示例。该项目基于《Linux Device Drivers 3》一书中的示例代码,并对其进行了更新,以确保这些示例能够在最新的Linux内核版本中正常编译和运行。 LDD3 项目的主要目标是帮助开发者理解和编写Linux设备驱动程序。通过提供经过验证的示例代码,开发者可以快速上手并深入

gitblog_00506的博客 812

终极指南:如何从零构建嵌入式Linux驱动系统——基于LDD-LinuxDeviceDrivers项目实战

LDD-LinuxDeviceDrivers是一个专注于Linux内核设备驱动程序学习的开源项目,提供了从基础模块到高级驱动的完整实战案例。本文将带你快速掌握嵌入式驱动开发的核心技能,通过项目中的实例代码和调试工具,轻松入门Linux设备驱动开发。 ## 为什么选择LDD-LinuxDeviceDrivers? 对于嵌入式开发者而言,掌握Linux设备驱动开发是提升竞争力的关键。LDD-Li

gitblog_00596的博客 555

宋宝华 《Linux设备驱动开发详解》示例代码之基本字符设备驱动

最简单字符设备驱动代码 scull.c #include #include #include #include #include #include #include #define SCULL_MAJOR 252 #define SCULL_NAME "scull" #define MAX_DATA 0x1000 static int scull_major = SCULL

bg2bkk的专栏 2910

[个人总结] LDD33.字符驱动

以字节流的方式进行顺序读写的设备,读写操作无缓存、无块大小限制,数据的读写顺序和字节的先后顺序完全一致。块设备是按「固定大小的块(通常是 512 字节 / 4KB)」随机读写,有内核缓存,比如 U 盘、SD 卡、硬盘;网络设备是面向数据包,无固定读写顺序,比如网卡。

m0_63020222的博客 955

Linux设备驱动开发终极指南:从零基础到实战精通

Linux设备驱动开发是系统编程中的核心领域,而 **Linux Device Drivers 3 examples** 项目(ldd3)为开发者提供了适配最新内核的实战案例。本指南将带你从基础概念到实际操作,掌握设备驱动开发的关键技能,让你快速上手并解决实际开发中的问题。 ## 📚 项目简介:为什么选择ldd3ldd3项目是经典书籍《Linux Device Drivers 3》示例代

gitblog_00582的博客 414

lddlinux设备驱动程序)实验2:scull

按照ldd第三版书上的网址,下载下来的代码是最终版的,没有按章节剥离,不方便读者自己实验。 以下是我手把手敲下的书上第二个实验scull的代码以及安装和测试的步骤。 按上篇搭建linux驱动开发环境(配合LDD阅读)做完实验1后,scull的安装很简单了,在任意位置保存下面两个源文件Makefile,scull.c,执行make,生成目标模块scull.ko,然后insmod scu...

aiqieer2126的博客 351

如何在 JetBrains IDE 装好 Continue:新手友好的 AI 编程助手配置指南

如果你常用 IntelliJ IDEA 这类 JetBrains IDE,多半撞过这堵墙:想确认一个库的用法,得切去浏览器翻文档;写完一个函数,还得逐行自查有没有坑;网页上问来的 AI 回答,贴回项目里却总差一口气。Continue 是一个开源的 AI 编程助手,把聊天、智能补全和自动化任务执行都搬进了编辑器。这篇文章只讲一件事:怎么把它装进 JetBrains IDE,并且真正用起来。 [![

gitblog_00364的博客 482

LDD3边读边学(第三章)字符设备驱动 scull 实验全过程

通过本次实验,完成了一个简单的 Linux 字符设备驱动 scull 的实现,并掌握了以下关键知识:1. Linux 字符设备驱动的基本结构2. 内核模块的编译加载过程3. 设备文件 /dev 驱动之间的关系4. 用户空间程序内核驱动之间的通信机制5. copy_to_user() copy_from_user() 的使用实验验证了 Linux 字符设备驱动中 open、read、write、release 等接口能够正常工作。

ada73的博客 427

从零开始学习Linux设备驱动ldd3项目10个基础模块快速入门

Linux设备驱动开发是系统编程中的重要领域,而ldd3项目作为《Linux Device Drivers 3》的示例代码更新版,为新手提供了实践学习的绝佳资源。本文将带你快速了解该项目中的10个核心模块,掌握Linux设备驱动开发的基础知识。 ## 一、项目简介 ldd3项目是Linux设备驱动开发的经典学习资料,它包含了多个实用的驱动模块示例,所有代码均已更新以适应最新内核版本。通过学习这

gitblog_00315的博客 344

解决Linux设备驱动3示例在新版内核中编译问题完全指南

Linux Device Drivers 3LDD3)作为经典的设备驱动开发教材,其示例代码因内核API变化已无法在Linux 5.x+环境直接编译。本项目通过重构关键接口适配层、更新核心数据结构(如`proc_ops`)、修复内存访问函数(`access_ok`)等方式,使这些示例代码兼容最新内核版本(测试通过Ubuntu 20.04内核5.4.0-73-generic、Alpine 3.13

gitblog_01197的博客 442

SCULL简单字符设备驱动

SCULL简单字符设备驱动试验环境: Fedora8+2.6.23内核+vim7.1+lxr Server联系方式:4bsfreedom$gmail.com(替换 $ 为 @ )(不知如何上传附件,相关资料包括源码可发邮件向我索取)2008年01月28日 序言    我的《Linux D

8904
上一篇: ubuntu下的字典,使用有道词典
下一篇: (转自oschina)python学习中的一些小例子
bg2bkk
博客等级 码龄16年 84粉丝 51原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值