国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Linux字符設(shè)備驅(qū)動(dòng)(設(shè)備文件,用戶空間與內(nèi)核空間進(jìn)行數(shù)據(jù)交互,ioctl接口)

這篇具有很好參考價(jià)值的文章主要介紹了Linux字符設(shè)備驅(qū)動(dòng)(設(shè)備文件,用戶空間與內(nèi)核空間進(jìn)行數(shù)據(jù)交互,ioctl接口)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

在Linux系統(tǒng)中“一切皆文件”,上一篇講述了cdev結(jié)構(gòu)體就描述了一個(gè)字符設(shè)備驅(qū)動(dòng),主要包括設(shè)備號(hào)和操作函數(shù)集合。但是要怎么操作這個(gè)驅(qū)動(dòng)呢?例如,使用open()該打開誰,read()該從哪讀取數(shù)據(jù)等等。所以就需要?jiǎng)?chuàng)建一個(gè)設(shè)備文件來代表設(shè)備驅(qū)動(dòng)。

應(yīng)用程序要操縱外部硬件設(shè)備,需要像和普通文件一樣,使用open(),read(),write()(初始化cdev時(shí)實(shí)現(xiàn)的操作函數(shù))等系統(tǒng)調(diào)用來操作設(shè)備文件間接實(shí)現(xiàn)控制外部硬件設(shè)備。注冊設(shè)備驅(qū)動(dòng)后想要?jiǎng)?chuàng)建相對應(yīng)的設(shè)備文件有兩種方式:手動(dòng)創(chuàng)建自動(dòng)創(chuàng)建。

手動(dòng)創(chuàng)建:

加載驅(qū)動(dòng)模塊之后,使用mknod命令在/dev目錄下創(chuàng)建設(shè)備文件。

mknod 設(shè)備文件路徑 文件類型 主設(shè)備號(hào) 次設(shè)備號(hào)

設(shè)備文件路徑:/dev/xxx

文件類型:c代表字符設(shè)備,b代表塊設(shè)備

設(shè)備文件代表設(shè)備驅(qū)動(dòng),用主次設(shè)備號(hào)來關(guān)聯(lián)。

字符設(shè)備文件,Linux,Linux驅(qū)動(dòng),linux,驅(qū)動(dòng)開發(fā),硬件工程

自動(dòng)創(chuàng)建:

每次新添加一個(gè)驅(qū)動(dòng)都手動(dòng)創(chuàng)建感覺非常麻煩,所以比較推薦自動(dòng)創(chuàng)建設(shè)備文件。

新添加一個(gè)頭文件。

#include <linux/device.h>

創(chuàng)建設(shè)備類?

內(nèi)核中定義了struct class結(jié)構(gòu)體,一個(gè)struct class結(jié)構(gòu)體類型變量對應(yīng)一個(gè)類, 內(nèi)核同時(shí)提供了class_create函數(shù),可以用它來創(chuàng)建一個(gè)類,這個(gè)類存放于/sys/class下面。

//原型是一個(gè)宏,主要使用里面__class_create函數(shù)。

#define class_create(owner, name) \

({? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\

?????????static struct lock_class_key __key; \

????????__class_create(owner, name, &__key); \

})

owner:類的所有者, 固定是 THIS_MODULE?
name:類名,可隨意起名

struct class * __class_create(模塊所有者, 設(shè)備類名);

//參數(shù)中還有一個(gè)key,不用管和功能沒有太大的關(guān)系

//返回設(shè)備類指針

銷毀設(shè)備類

有創(chuàng)建自然有銷毀。

void class_destroy(struct class *cls);

?創(chuàng)建設(shè)備文件也叫設(shè)備節(jié)點(diǎn)

創(chuàng)建好一個(gè)設(shè)備類,調(diào)用 device_create函數(shù)就可以在/dev目錄下創(chuàng)建相應(yīng)的設(shè)備節(jié)點(diǎn)。

struct device *device_create(struct class *class, struct device *parent,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dev_t devt, void *drvdata, const char *fmt, ...)

參數(shù)依次對應(yīng):設(shè)備類指針, 父設(shè)備指針,設(shè)備號(hào), 額外數(shù)據(jù), "設(shè)備文件名"

//父設(shè)備指針:如果有些設(shè)備之間有依賴關(guān)系,就可以傳入父設(shè)備指針,沒有就不需要

銷毀設(shè)備文件

void device_destroy(struct class *class, dev_t devt);

驅(qū)動(dòng)錯(cuò)誤處理

接下來說說怎么判斷這些函數(shù)是否創(chuàng)建成功?,有人可能覺得返回類型不都是指針嗎,直接判斷指針是不是NULL就好了。想一想如果都是返回NULL那你能判斷是哪一步出現(xiàn)了錯(cuò)誤,錯(cuò)誤的原因是什么嗎。事實(shí)上有很多函數(shù)的返回類型是指針,結(jié)果有三種分別是合法指針、NULL指針和非法指針。

那怎么判斷函數(shù)返回的指針是否為有效地址呢?使用IS_ERR宏去檢查函數(shù)的返回值,如果地址落在0xfffffffffffff000~0xffffffffffffffff范圍(64位系統(tǒng)),表示函數(shù)執(zhí)行失敗,IS_ERR宏返回真。在Linux中函數(shù)執(zhí)行錯(cuò)誤返回的非法地址對應(yīng)著一個(gè)錯(cuò)誤碼,使用PTR_ERR宏把相應(yīng)的非法地址轉(zhuǎn)換成錯(cuò)誤碼,每個(gè)錯(cuò)誤碼都用不一樣的含義,感興趣可以去errno.h中查看。

IS_ERR(指針)? ? ? ? ? ? ? ? ? ? ? //返回真,表示指針出錯(cuò)

IS_ERR_OR_NULL(指針)? ?//返回真,表示指針出錯(cuò)(可判斷空指針)

PTR_ERR(指針)? ? ? ? ? ? ? ? ? //將出錯(cuò)的指針轉(zhuǎn)換成錯(cuò)誤碼

ERR_PTR(錯(cuò)誤碼)? ? ? ? ? ? ? //將錯(cuò)誤碼轉(zhuǎn)成指針

?說了那么多我們來寫一個(gè)測試一下。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>

//起始次設(shè)備號(hào)
#define CDD_MINOR 0
//設(shè)備號(hào)個(gè)數(shù)
#define CDD_COUNT 1

//設(shè)備號(hào)
dev_t dev;
//聲明cdev
struct cdev cdd_cdev;
//設(shè)備類指針
struct class *cdd_class;
//設(shè)備指針
struct device *cdd_device;


int cdd_open(struct inode *inode, struct file *filp)
{
	printk("enter cdd_open!\n");
	return 0;
}

ssize_t cdd_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
	printk("enter cdd_read!\n");
	return 0;
}

ssize_t cdd_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
	printk("enter cdd_write!\n");
	return 0;
}

long cdd_ioctl(struct file *filp, unsigned int cmd, unsigned long data)
{
	printk("enter cdd_ioctl!\n");
	return 0;
}

int cdd_release(struct inode *inode, struct file *filp)
{
	printk("enter cdd_release!\n");
	return 0;
}

//聲明操作函數(shù)集合
struct file_operations cdd_fops = {
	.owner = THIS_MODULE,
	.open = cdd_open,
	.read = cdd_read,
	.write = cdd_write,
	.unlocked_ioctl = cdd_ioctl,//ioctl接口
	.release = cdd_release,//對應(yīng)用戶close接口
};

//加載函數(shù)
int cdd_init(void)
{
	int ret;
    //1.動(dòng)態(tài)申請?jiān)O(shè)備號(hào)
	ret = alloc_chrdev_region(&dev, CDD_MINOR, CDD_COUNT, "cdd_demo");
	if(ret<0){
		printk("alloc_chrdev_region failed!\n");
		goto failure_register_chrdev;
	}
    
    // 2.注冊cdev
	//初始化
	cdev_init(&cdd_cdev, &cdd_fops);
	//將cdev添加到內(nèi)核
	ret = cdev_add(&cdd_cdev, dev, CDD_COUNT);
	if(ret<0){
		printk("cdev_add failed!\n");
		goto failure_cdev_add;
	}

	// 3.注冊設(shè)備類
	/*成功會(huì)在/sys/class目錄下出現(xiàn)cdd_class子目錄*/
	cdd_class = class_create(THIS_MODULE, "cdd_class");
	if(IS_ERR(cdd_class)){
		printk("class_create failed!\n");
		ret = PTR_ERR(cdd_class);
		goto failure_class_create;
	}

	// 4.創(chuàng)建設(shè)備文件
	cdd_device = device_create(cdd_class, NULL, dev,NULL, "cdd");
	if(IS_ERR(cdd_device)){
		printk("device_create failed!\n");
		ret = PTR_ERR(cdd_device);
		goto failure_device_create;
	}

	return 0;

failure_device_create:       
	class_destroy(cdd_class);
failure_class_create:        
    cdev_del(&cdd_cdev);
failure_cdev_add:           
    unregister_chrdev_region(dev, CDD_COUNT);
failure_register_chrdev:   
	return ret;
}

//卸載函數(shù)
void cdd_exit(void)
{
	//銷毀設(shè)備文件
	device_destroy(cdd_class, dev);
	//注銷設(shè)備類
	class_destroy(cdd_class);
    //銷毀cdev
    cdev_del(&cdd_cdev);
	//注銷設(shè)備號(hào)
	unregister_chrdev_region(dev, CDD_COUNT);
}

//聲明為模塊的入口和出口
module_init(cdd_init);
module_exit(cdd_exit);


MODULE_LICENSE("GPL");//GPL模塊許可證
MODULE_AUTHOR("xin");//作者
MODULE_VERSION("2.0");//版本
MODULE_DESCRIPTION("charactor driver!");//描述信息

字符設(shè)備文件,Linux,Linux驅(qū)動(dòng),linux,驅(qū)動(dòng)開發(fā),硬件工程

從上圖可以看出這樣就不需要手動(dòng)創(chuàng)建設(shè)備文件了。?

這里需要說明一下我們現(xiàn)在編寫字符設(shè)備驅(qū)動(dòng)的流程是:1、注冊設(shè)備號(hào) 2、注冊 添加cdev 3、創(chuàng)建設(shè)備類 4、創(chuàng)建設(shè)備文件。如果我們在創(chuàng)建設(shè)備設(shè)備文件時(shí)出現(xiàn)了錯(cuò)誤,那我們不僅需要返回錯(cuò)誤碼,還需要把之前的設(shè)備號(hào)注銷、銷毀cdev和注銷設(shè)備類,就是哪一步出錯(cuò)了,就需要把之前的復(fù)原。這就體現(xiàn)了goto語句用來處理多步驟錯(cuò)誤處理的好處了。

注冊設(shè)備號(hào),注冊cdev合二為一

字符設(shè)備驅(qū)動(dòng)的流程有4步,內(nèi)核中提供了一個(gè)register_chrdev函數(shù)來把第一步和第二部合并。

int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);

參數(shù):

??? major - 主設(shè)備號(hào)

??? name - 設(shè)備號(hào)名字

??? fops - 操作函數(shù)集合

靜態(tài)申請成功返回0,動(dòng)態(tài)申請成功返回主設(shè)備號(hào),失敗返回負(fù)數(shù)???

注意:該函數(shù)的第一個(gè)參數(shù)不為0,就靜態(tài)申請?jiān)O(shè)備號(hào),第一個(gè)參數(shù)為0,就動(dòng)態(tài)申請?jiān)O(shè)備號(hào),該函數(shù)會(huì)自動(dòng)初始化好cdev,并添加到內(nèi)核中

該函數(shù)是調(diào)用內(nèi)核級(jí)__register_chrdev函數(shù)實(shí)現(xiàn)其功能,多添加了2個(gè)參數(shù)起始此設(shè)備號(hào)和次設(shè)備號(hào)的范圍。

static inline int register_chrdev(unsigned int major, const char *name,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const struct file_operations *fops)
{
? ? return __register_chrdev(major, 0, 256, name, fops);
}

該函數(shù)的調(diào)用將為給定的主設(shè)備號(hào)注冊0~255作為次設(shè)備號(hào),并為每個(gè)設(shè)備建立一個(gè)對應(yīng)的默認(rèn)cdev結(jié)構(gòu)。使用這一接口的驅(qū)動(dòng)程序必須能夠處理所有256個(gè)次設(shè)備號(hào)上的open調(diào)用(無論它們是否真正對應(yīng)于實(shí)際的設(shè)備),而且不能使用大于255的主設(shè)備號(hào)和次設(shè)備號(hào)。

將使用上面方法創(chuàng)建的設(shè)備從系統(tǒng)中移除使用以下函數(shù)。

int unregister_chrdev(unsigned int major, const char *name);

major和name必須與傳遞給register_chrdev函數(shù)的值保持一致,否則該調(diào)用會(huì)失敗。

注意這種方式是早期內(nèi)核驅(qū)動(dòng)程序注冊字符設(shè)備驅(qū)動(dòng)程序的方法,現(xiàn)在不推薦使用。

訪問字符設(shè)備文件=使用字符設(shè)備驅(qū)動(dòng)?

字符設(shè)備文件都有設(shè)備號(hào),當(dāng)我們操作字符設(shè)備文件時(shí),內(nèi)核會(huì)通過設(shè)備號(hào)去找到相同設(shè)備號(hào)cdev然后把cdev中的file_operations賦值給file結(jié)構(gòu)中的file_operations,最后調(diào)用file里file_operations中我們寫好的操作函數(shù)。所以我們訪問字符設(shè)備文件就相當(dāng)于使用了字符設(shè)備驅(qū)動(dòng)。

內(nèi)核和用戶空間進(jìn)行數(shù)據(jù)交互

需要的頭文件:#include <asm/uaccess.h>

Linux中內(nèi)核空間和用戶空間是隔離的,互相之間不能直接訪問,地址空間也相互獨(dú)立。內(nèi)核中提供用戶空間到內(nèi)核空間之間數(shù)據(jù)拷貝的方法。

copy_to_user(用戶地址,內(nèi)核地址,大小)? ? ? ?// 從內(nèi)核空間--->用戶空間

copy_from_user(內(nèi)核地址,用戶地址,大小)? ?//從用戶空間--->內(nèi)核空間

//在內(nèi)核中屬于用戶空間的地址需要用 __user 修飾

注:copy_to_user和copy_from_user調(diào)用時(shí)可能導(dǎo)致睡眠,某些禁止睡眠的場合不能使用。

ioctl接口

ioctl是Linux專門為用戶層控制設(shè)備設(shè)計(jì)的系統(tǒng)調(diào)用接口,這個(gè)接口具有極大的靈活性,我們的設(shè)備打算讓用戶通過哪些命令實(shí)現(xiàn)哪些功能。

?用戶空間使用ioctl

需要的頭文件:#include <sys/ioctl.h>

int ioctl(int fd, int cmd, ...) ;

參數(shù):

fd - 文件描述符

cmd - 操作命令,代表某個(gè)動(dòng)作(由內(nèi)核定義)

...? - 不定參數(shù),可以也可以沒有,取決于內(nèi)核實(shí)現(xiàn)

返回值:執(zhí)行成功時(shí)返回 0,失敗則返回 -1 并設(shè)置全局變量 errorno 值

驅(qū)動(dòng)程序使用ioctl

需要的頭文件:#include <linux/ioctl.h>

long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long data);

ioctl 命令(cmd)的統(tǒng)一格式

將一個(gè)32位int型劃分為4個(gè)部分

設(shè)備類型 ? ?序列號(hào) ? ? 方向 ? ? ?數(shù)據(jù)尺寸

? ? ?8bit? ? ? ? ? 8bit ? ? ? ?2bit? ? ? ? ? 14bit? ? ? ?

//設(shè)備類型,可以是0~0xff之間的數(shù)稱為幻數(shù),其主要作用是使 ioctl 命令有唯一的設(shè)備標(biāo)識(shí)??

//序列號(hào),表示當(dāng)前命令是整個(gè)ioctl命令中的第幾個(gè),從0開始計(jì)數(shù)? ? ?

//方向,表示數(shù)據(jù)的傳輸方向,可以為_IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,代表四種訪問模式:無數(shù)據(jù)、讀數(shù)據(jù)、寫數(shù)據(jù)、讀寫數(shù)據(jù)

//數(shù)據(jù)尺寸,表示涉及的用戶數(shù)據(jù)的大小

?構(gòu)造ioctl命令還是比較繁瑣的,內(nèi)核提供了宏來方便用戶構(gòu)造ioctl命令。

?_IO(設(shè)備類型,序列號(hào))? ? ? ? ? ? ? ? ? ? ? ? ?//沒有參數(shù)的命令? ? ?
_IOR(設(shè)備類型,序列號(hào),數(shù)據(jù)尺寸)? ? ? ? //該命令是從驅(qū)動(dòng)讀取數(shù)據(jù)
_IOW(設(shè)備類型,序列號(hào),數(shù)據(jù)尺寸)? ? ? ?//該命令是從驅(qū)動(dòng)寫入數(shù)據(jù)
_IOWR(設(shè)備類型,序列號(hào),數(shù)據(jù)尺寸)? ? ?//雙向數(shù)據(jù)傳輸?shù)拿?/p>

有生成命令的宏,也有拆分命令的宏。

?_IOC_DIR(cmd)? ? ? ? ? ?//從命令中提取方向
_IOC_TYPE(cmd)? ? ? ? ?//從命令中提取幻數(shù)
_IOC_NR(cmd)? ? ? ? ? ? ?//從命令中提取序數(shù)
_IOC_SIZE(cmd)? ? ? ? ? //從命令中提取數(shù)據(jù)大小

說了這么多,還是用一個(gè)簡單的示例來演示一下 ,把注冊設(shè)備號(hào),注冊cdev合二為一,內(nèi)核和用戶空間進(jìn)行數(shù)據(jù)交互,ioctl接口都用上。

用戶和內(nèi)核空間共用的頭文件,里面是ioctl命令的構(gòu)成和頭文件。

#ifndef __IOTEST_H
#define __IOTEST_H

#include <linux/ioctl.h>

//定義設(shè)備類型(幻數(shù))
#define IOC_MAGIC 'x'

#define HELLO_DEMO _IO(IOC_MAGIC,0)
#define HELLO_READ _IOR(IOC_MAGIC,1,int)
#define HELLO_WRITE _IOW(IOC_MAGIC,2,int)

#endif

驅(qū)動(dòng)模塊

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include "iotest.h"

#define CDD_MINOR 0

//設(shè)備號(hào)
dev_t dev;
//聲明cdev
struct cdev cdd_cdev;
//設(shè)備類指針
struct class *cdd_class;
//設(shè)備指針
struct device *cdd_device;

//內(nèi)核緩沖區(qū)
char arr[128] = {0};
int data = 1;

int cdd_open(struct inode *inode, struct file *filp)
{
	printk("enter cdd_open!\n");
	return 0;
}

//在內(nèi)核中屬于用戶空間的地址需要用 __user 修飾
ssize_t cdd_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
	int ret;

	printk("enter cdd_read!\n");
	if(size>127)
		size = 127;//數(shù)據(jù)不夠長,取最長的數(shù)據(jù)

	ret = copy_to_user(buf, arr, size);
	if(ret)
		return -EFAULT;

	return size;
}

//在內(nèi)核中屬于用戶空間的地址需要用 __user 修飾
ssize_t cdd_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
	int ret = 0;

	printk("enter cdd_write!\n");
	if(size>127)
		return -ENOMEM;//越界

	//拷貝數(shù)據(jù)
	ret = copy_from_user(arr, buf, size);
	if(ret)
		return -EFAULT;

	printk("arr = %s\n",arr);
	return size;
}

long cdd_ioctl(struct file *filp, unsigned int cmd, unsigned long val)
{
	int ret = 0;
	
	printk("enter cdd_ioctl!\n");
	//不同的命令對應(yīng)不同的操作
	switch(cmd){
		case HELLO_DEMO:
			printk("HELLO_DEMO!\n");
		break;
		case HELLO_READ:
		{
			ret = copy_to_user((int __user *)val, \
						&data, sizeof(int));
		}
			printk("HELLO_READ!\n");
		break;
        	case HELLO_WRITE:
        	{
        		ret = copy_from_user(&data, \
				(int __user *)val, sizeof(int));
			printk("HELLO_WRITE data = %d\n",data);
        	}
		break;
		default:
			return -EINVAL;
	}
	return 0;
}

int cdd_release(struct inode *inode, struct file *filp)
{
	printk("enter cdd_release!\n");
	return 0;
}

//聲明操作函數(shù)集合
struct file_operations cdd_fops = {
	.owner = THIS_MODULE,
	.open = cdd_open,
	.read = cdd_read,
	.write = cdd_write,
	.unlocked_ioctl = cdd_ioctl,//ioctl接口
	.release = cdd_release,//對應(yīng)用戶close接口
};

//加載函數(shù)
int cdd_init(void)
{
	int ret;
	// 1.注冊字符設(shè)備驅(qū)動(dòng)
	ret = register_chrdev(0, "cdd_demo", &cdd_fops);
	if(ret<0){
		printk("register_chrdev failed!\n");
		goto failure_register_chrdev;
	}
	//構(gòu)建設(shè)備號(hào)
	dev = MKDEV(ret,CDD_MINOR);

	printk("register_chrdev success!\n");

	// 2.注冊設(shè)備類
	/*成功會(huì)在/sys/class目錄下出現(xiàn)cdd_class子目錄*/
	cdd_class = class_create(THIS_MODULE, "cdd_class");
	if(IS_ERR(cdd_class)){
		printk("class_create failed!\n");
		ret = PTR_ERR(cdd_class);
		goto failure_class_create;
	}

	// 3.創(chuàng)建設(shè)備文件
	cdd_device = device_create(cdd_class, NULL, dev,NULL, "cdd");
	if(IS_ERR(cdd_device)){
		printk("device_create failed!\n");
		ret = PTR_ERR(cdd_device);
		goto failure_device_create;
	}

	return 0;

failure_device_create:
	class_destroy(cdd_class);
failure_class_create:
	unregister_chrdev(MAJOR(dev), "cdd_demo");
failure_register_chrdev:
	return ret;
}

//卸載函數(shù)
void cdd_exit(void)
{
	//銷毀設(shè)備文件
	device_destroy(cdd_class, dev);
	//注銷設(shè)備類
	class_destroy(cdd_class);
	//注銷字符設(shè)備驅(qū)動(dòng)
	unregister_chrdev(MAJOR(dev), "cdd_demo");
}

//聲明為模塊的入口和出口
module_init(cdd_init);
module_exit(cdd_exit);


MODULE_LICENSE("GPL");//GPL模塊許可證
MODULE_AUTHOR("xin");//作者
MODULE_VERSION("3.0");//版本
MODULE_DESCRIPTION("charactor driver!");//描述信息

測試模塊

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include "iotest.h"

int main()
{
	char ch = 0;
	char w_buf[10] = "welcome";
	char r_buf[10] = {0};
	int data = 5;

	int fd = open("/dev/cdd",O_RDWR);
	if(fd==-1){
		perror("open");
		exit(-1);
	}

	printf("open successed!fd = %d\n",fd);

	while(1){
		ch = getchar();
		getchar();

		if(ch=='q')
			break;

		switch(ch){
			case 'r':
				read(fd,r_buf,sizeof(r_buf));
				printf("r_buf = %s\n",r_buf);
				break;
			case 'w':
				write(fd,w_buf,sizeof(r_buf));
				break;
			case 'd':
				ioctl(fd,HELLO_DEMO);
				break;
			case 'i':
			{
				ioctl(fd,HELLO_READ,&data);
				printf("ioread data=%d\n",data);
			}
				break;
			case 'o':
				ioctl(fd,HELLO_WRITE,&data);
				break;
			default:
				printf("error input!\n");
				break;
		}

		sleep(1);
	}

	close(fd);
	return 0;
}

在驅(qū)動(dòng)模塊中使用兩種不同的方式進(jìn)行用戶空間和內(nèi)核空間的數(shù)據(jù)交互,一種是在read()和write()中交換字符串?dāng)?shù)據(jù),一種是在ioctl命令中交換int型數(shù)據(jù)。需要注意在內(nèi)核中屬于用戶空間的地址需要用 __user 修飾。

字符設(shè)備文件,Linux,Linux驅(qū)動(dòng),linux,驅(qū)動(dòng)開發(fā),硬件工程

應(yīng)用測試模塊cdd_test?

?字符設(shè)備文件,Linux,Linux驅(qū)動(dòng),linux,驅(qū)動(dòng)開發(fā),硬件工程

這大概可以算字符設(shè)備的基本框架吧,里面有些東西沒有細(xì)說,如果感興趣可自行百度。

好了,如果對以上內(nèi)容有什么疑問或建議歡迎在評論區(qū)里提出來^-^。文章來源地址http://www.zghlxwxcb.cn/news/detail-662722.html

到了這里,關(guān)于Linux字符設(shè)備驅(qū)動(dòng)(設(shè)備文件,用戶空間與內(nèi)核空間進(jìn)行數(shù)據(jù)交互,ioctl接口)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Linux 驅(qū)動(dòng)之字符設(shè)備

    Linux 驅(qū)動(dòng)之字符設(shè)備

    什么是設(shè)備號(hào) Linux 規(guī)定每一個(gè)字符設(shè)備或者塊設(shè)備都必須有一個(gè)專屬的設(shè)備號(hào)。一個(gè)設(shè)備號(hào)由主設(shè)備號(hào)和次設(shè)備號(hào)組成。主設(shè)備號(hào)用來表示某一類驅(qū)動(dòng),如鼠標(biāo),鍵盤都可以歸類到 USB 驅(qū)動(dòng)中。而次設(shè)備號(hào)是用來表示這個(gè)驅(qū)動(dòng)下的各個(gè)設(shè)備。比如第幾個(gè)鼠標(biāo),第幾個(gè)鍵盤等。

    2024年02月16日
    瀏覽(21)
  • Linux 驅(qū)動(dòng)之高級(jí)字符設(shè)備

    Linux 驅(qū)動(dòng)之高級(jí)字符設(shè)備

    什么是IO呢? IO 的英文全稱是 input 和output,翻譯過來就是輸入和輸出。 在馮.諾依曼結(jié)構(gòu)中,將計(jì)算機(jī)分成分為5個(gè)部分: 運(yùn)算器、控制器、存儲(chǔ)器、輸入設(shè)備、輸出設(shè)備 。其中輸入設(shè)備指的是向計(jì)算機(jī)輸入數(shù)據(jù)或者信息,如鼠標(biāo),鍵盤都是輸入設(shè)備。輸出設(shè)備指的是用于接收

    2023年04月14日
    瀏覽(30)
  • Linux -- 字符設(shè)備驅(qū)動(dòng)--LED的驅(qū)動(dòng)開發(fā)(初級(jí)框架)

    Linux -- 字符設(shè)備驅(qū)動(dòng)--LED的驅(qū)動(dòng)開發(fā)(初級(jí)框架)

    看原理圖確定引腳,確定引腳輸出什么電平才能點(diǎn)亮 / 熄滅 LED 看主芯片手冊,確定寄存器操作方法:哪些寄存器?哪些位?地址是? 編寫驅(qū)動(dòng):先寫框架,再寫硬件操作的代碼 注意 :在芯片手冊中確定的寄存器地址被稱為 物理地址 ,在 Linux 內(nèi)核中無法直接使用。 需要使

    2024年04月28日
    瀏覽(27)
  • LDD學(xué)習(xí)筆記 -- Linux字符設(shè)備驅(qū)動(dòng)

    LDD學(xué)習(xí)筆記 -- Linux字符設(shè)備驅(qū)動(dòng)

    字符驅(qū)動(dòng)程序用于與Linux內(nèi)核中的設(shè)備進(jìn)行交互; 字符設(shè)備指的是像內(nèi)存區(qū)域這樣的硬件組件,通常稱為偽設(shè)備; 用戶空間應(yīng)用程序通常使用 open read write 等系統(tǒng)調(diào)用與這些設(shè)備通信; 把用戶空間的系統(tǒng)調(diào)用連接到設(shè)備驅(qū)動(dòng)的系統(tǒng)調(diào)用實(shí)現(xiàn)方法上。 內(nèi)核的虛擬文件系統(tǒng) vir

    2024年02月02日
    瀏覽(26)
  • Linux下字符設(shè)備驅(qū)動(dòng)開發(fā)以及流程介紹

    Linux下字符設(shè)備驅(qū)動(dòng)開發(fā)以及流程介紹

    首先我們介紹一下什么是字符設(shè)備,然后講解一下字符設(shè)備開發(fā)的具體的流程,分別詳細(xì)介紹每一個(gè)流程中涉及到的結(jié)構(gòu)體以及知識(shí)點(diǎn),最后我們編寫代碼實(shí)現(xiàn)字符設(shè)備的開發(fā)以及測試。 Linux內(nèi)核設(shè)計(jì)哲學(xué)是把所有的東西都抽象成文件進(jìn)行訪問,這樣對設(shè)備的訪問都是通過文

    2024年02月01日
    瀏覽(36)
  • Linux設(shè)備驅(qū)動(dòng)開發(fā)學(xué)習(xí)筆記(等待隊(duì)列,鎖,字符驅(qū)動(dòng)程序,設(shè)備樹,i2C...)

    container_of函數(shù)可以通過結(jié)構(gòu)體的成員變量檢索出整個(gè)結(jié)構(gòu)體 函數(shù)原型: 內(nèi)核開發(fā)者只實(shí)現(xiàn)了循環(huán)雙鏈表,因?yàn)檫@個(gè)結(jié)構(gòu)能夠?qū)崿F(xiàn)FIFO和LIFO,并且內(nèi)核開發(fā)者要保持最少代碼。 為了支持鏈表,代碼中要添加的頭文件是linux/list.h。內(nèi)核中鏈表實(shí)現(xiàn)核心部分的數(shù)據(jù)結(jié)構(gòu) 是struct li

    2024年01月22日
    瀏覽(19)
  • 【linux驅(qū)動(dòng)開發(fā)】在linux內(nèi)核中注冊一個(gè)雜項(xiàng)設(shè)備與字符設(shè)備以及內(nèi)核傳參的詳細(xì)教程

    【linux驅(qū)動(dòng)開發(fā)】在linux內(nèi)核中注冊一個(gè)雜項(xiàng)設(shè)備與字符設(shè)備以及內(nèi)核傳參的詳細(xì)教程

    開發(fā)環(huán)境: windows + ubuntu18.04 + 迅為rk3568開發(fā)板 相較于字符設(shè)備,雜項(xiàng)設(shè)備有以下兩個(gè)優(yōu)點(diǎn): 節(jié)省主設(shè)備號(hào):雜項(xiàng)設(shè)備的主設(shè)備號(hào)固定為 10,在系統(tǒng)中注冊多個(gè) misc 設(shè)備驅(qū)動(dòng)時(shí),只需使用子設(shè)備號(hào)進(jìn)行區(qū)分即可。 使用簡單:相比如普通的字符設(shè)備驅(qū)動(dòng), misc驅(qū)動(dòng)只需要將基本信

    2024年01月21日
    瀏覽(20)
  • 嵌入式培訓(xùn)機(jī)構(gòu)四個(gè)月實(shí)訓(xùn)課程筆記(完整版)-Linux ARM驅(qū)動(dòng)編程第五天-ARM Linux編程之字符設(shè)備驅(qū)動(dòng)(物聯(lián)技術(shù)666)

    嵌入式培訓(xùn)機(jī)構(gòu)四個(gè)月實(shí)訓(xùn)課程筆記(完整版)-Linux ARM驅(qū)動(dòng)編程第五天-ARM Linux編程之字符設(shè)備驅(qū)動(dòng)(物聯(lián)技術(shù)666)

    鏈接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688 提取碼:1688 教學(xué)內(nèi)容: 1 、內(nèi)核模塊的簡單框架: __init __exit 執(zhí)行完后就釋放空間 簡單框架:包含三個(gè)部分 1)模塊初始化和模塊退出函數(shù) 2)注冊模塊函數(shù) 3)模塊許可 //*************************************************** #include linux

    2024年02月21日
    瀏覽(21)
  • 驅(qū)動(dòng)開發(fā)--字符驅(qū)動(dòng)設(shè)備2

    字符設(shè)備驅(qū)動(dòng) 1.定義 以字節(jié)流的形式進(jìn)行訪問,且只能順序訪問的設(shè)備,針對字符設(shè)備編寫的驅(qū)動(dòng)叫做字符設(shè)備驅(qū)動(dòng) 2.字符設(shè)備框架 用戶空間通過IO函數(shù)如open、read、write、close等函數(shù)接口,調(diào)用內(nèi)核空間中的字符設(shè)備驅(qū)動(dòng)函數(shù)中的用戶自定義的open、read、write、close等函數(shù),通

    2024年02月15日
    瀏覽(49)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包