網站首頁 編程語言 正文
字符設備驅動
- 1.注冊字符類設備號
- 1.靜態分配一個設備號
- 2.動態分配一個設備號
- 3.注銷設備號
- 2.注冊字符類設備
- 1.定義一個cdev結構體
- 2. 使用cdev_init函數初始化cdev結構體成員變量
- 3.使用cdev_add函數注冊到內核
- 3.創建設備節點
- 1.方法一
- 2.方法二
- 1.使用class_create函數創建一個class類
- 2.使用device_create函數在我們創建的類下面創建一個設備
- 4.chrdev.c
- 5 app.c
- 6 編譯運行
- 1.編譯chrdev_io.c
- 2.編譯app.c
- 7 在樹莓派運行
1.注冊字符類設備號
1.靜態分配一個設備號
int register_chrdev_region(dev_t, unsigned, const char *);
參數:
第一個:設備號的起始值。類型是dev_t類型
第二個:次設備號的個數
第三個:設備的名稱 (使用cat /proc/devices 輸出的就是設備號,設備的名稱)
返回值:成功返回0,失敗返回負數
dev_t類型:
dev_t是用來保存設備號的,是一個32位數。
高12位用來保存主設備號,低20位用來保存次設備號。
操作dev的函數
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
根據dev_t獲取主設備號
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
根據dev_t獲取次設備號
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
將我們的主設備號和次設備號組成一個dev_t類型,第一個參數是主設備號,第二個參數是次設備號。
2.動態分配一個設備號
int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
參數:
第一個:保存生成的設備號
第二個:我們請求的第一個設備號,通常是0
第三個:連續申請的設備號的個數。
第四個:設備的名稱
返回值:成功返回0 ,失敗返回負數。
使用動態分配優先使用234 --255的設備號。
3.注銷設備號
void unregister_chrdev_region(dev_t, unsigned);
參數:
第一個:分配設備號的起始地址
第二個: 申請的連續設備號的個數。
2.注冊字符類設備
1.定義一個cdev結構體
2. 使用cdev_init函數初始化cdev結構體成員變量
void cdev_init(struct cdev *, const struct file_operations *);
參數:
第一個:要初始化的cdev
第二個:文件操作集
cdev->ops = fops;//實際就是把文件操作集寫給ops
3.使用cdev_add函數注冊到內核
int cdev_add(struct cdev *, dev_t, unsigned);
參數:
第一個:cdev的結構體指針
第二個:設備號
第三個:次設備號的數量
3.創建設備節點
1.方法一
使用mknod命令創建一個設備節點
格式:
mknod 名稱 類型 主設備號 次設備號
2.方法二
1.使用class_create函數創建一個class類
2.使用device_create函數在我們創建的類下面創建一個設備
4.chrdev.c
在/home/kun/build_new/linux_kernel/drivers/char/(這為我內核目錄下的字符設備路徑)目錄下 mkdir 07_chrdev_io ,編寫chrdev.c app.c Makefile。
#include <linux/init.h> //初始化頭文件
#include <linux/module.h> //最基本的文件,支持動態添加和卸載模塊
#include <linux/moduleparam.h> //驅動傳參頭文件
#include <linux/fs.h> //文件操作相關的struct的定義
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>
#define DEVICE_NUMBER 1 //次設備號個數
#define DEVICE_SNAME "schrdev" //靜態注冊設備的名字
#define DEVICE_ANANME "achrdev" //動態注冊設備的名字
#define DEVICE_MINOR 0 //次設備號起始地址
#define DEVICE_CLASS_NAME "chrdev_class"
#define DEVICE_NODE_NAME "chrdev_test"
#define GPIO4_DR 0xfe200000
#define GPIO4_H 0xfe20001c
#define GPIO4_L 0xfe200028
unsigned int *vir_gpio4_dr=NULL;
unsigned int *vir_gpio4_h=NULL;
unsigned int *vir_gpio4_l=NULL;
static int major_num, minor_num;
struct cdev cdev;
static dev_t dev_num;
struct class *class;
struct device *device;
int chrdev_open( struct inode * inode, struct file *file)
{
printk( "hello chrdev_open \n");
return 0;
}
ssize_t chrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
char kbuf[64] = {0};
if (copy_from_user(kbuf, ubuf, size) != 0)
{
printk("copy_from_user error\n ");
return -1;
}
printk("kbuf is %s\n ", kbuf);
*vir_gpio4_dr |=(001<<(3*4));
if(kbuf[0]==1)
{
*vir_gpio4_h |=(1<<4);
}
else if(kbuf[0]==0)
{
*vir_gpio4_l |=(1<<4);
}
return 0;
}
struct file_operations chrdev_ops=
{
.owner = THIS_MODULE,
.open = chrdev_open,
.write = chrdev_write,
};
module_param( major_num, int, S_IRUGO);
module_param( minor_num, int, S_IRUGO);
MODULE_PARM_DESC( major_num, "e.g:a=1");
MODULE_PARM_DESC( minor_num, "e.g:a=1");
static int
chrdev_init( void)
{
int ret;
if( major_num) //靜態注冊
{
printk( "mjor_num=%d \n", major_num);
printk( "minor_num=%d \n", minor_num);
dev_num = MKDEV( major_num, minor_num);
ret = register_chrdev_region( dev_num, DEVICE_NUMBER, DEVICE_SNAME);
if( ret<0)
{
printk( "register_chrdev_region error\n");
}
printk( "register_chrdev_region success\n");
}
else //動態注冊
{
ret = alloc_chrdev_region( &dev_num, DEVICE_MINOR, DEVICE_NUMBER, DEVICE_ANANME);
if( ret<0)
{
printk( "alloc_chrdev_region error\n");
}
printk( "alloc_chrdev_region success\n");
major_num = MAJOR( dev_num);
minor_num = MINOR( dev_num);
printk( "mjor_num=%d \n", major_num);
printk( "minor_num=%d \n", minor_num);
}
//初始化cdev
cdev.owner = THIS_MODULE;
cdev_init( &cdev, &chrdev_ops);
//向系統注冊設備
cdev_add( &cdev, dev_num, DEVICE_NUMBER);
class = class_create( THIS_MODULE, DEVICE_CLASS_NAME);
device = device_create( class,NULL,dev_num,NULL,DEVICE_NODE_NAME);
//對物理地址進行虛擬映射
vir_gpio4_dr = ioremap( 0xfe200000,4);
if(vir_gpio4_dr == NULL )
{
printk( "gpio4dr ioremap error\n");
return EBUSY;
}
vir_gpio4_h = ioremap( GPIO4_H,4);
if( vir_gpio4_h == NULL)
{
printk( "gpio4h ioremap error\n");
return EBUSY;
}
vir_gpio4_l = ioremap( GPIO4_L,4);
if(vir_gpio4_l == NULL)
{
printk( "gpio4l ioremap error\n");
return EBUSY;
}
printk( "gpio ioremap success\n");
return 0;
}
static void
chrdev_exit( void)
{
unregister_chrdev_region( MKDEV( major_num, minor_num), DEVICE_NUMBER);
cdev_del( &cdev);
device_destroy( class,dev_num);
class_destroy( class);
printk( "bye bye \n");
}
module_init( chrdev_init);
module_exit( chrdev_exit);
MODULE_LICENSE( "GPL");
5 app.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
int fd;
char buf[64] = "0";
fd = open( "/dev/chrdev_test",O_RDWR);//打開設備節點
if(fd < 0)
{
perror( "open error \n");
return fd;
}
buf[0]= atoi( argv[1]);
write( fd,buf,sizeof(buf)); //向內核層寫數據
close( fd);
return 0;
}
6 編譯運行
1.編譯chrdev_io.c
Makefile文件內容
obj-m +=chrdev_io.o
KDIR:=/home/kun/build_new/linux_kernel
PWD?=$(shell pwd)
all:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) modules
clean:
rm *.mod.c *.order *.ko *.o *.mod *.symvers
KDIR:為自己在linux上存放的內核目錄路徑
執行make,生成.ko文件
2.編譯app.c
arm-linux-gnueabihf-gcc app.c -o app -static
7 在樹莓派運行
將步驟6生成的chrdev.ko app移動至樹莓派
執行 sudo insmod chrdev.ko即可加載驅動
執行 ./app 1 即可使GPIO4引腳輸出高電平
執行 ./app 0即可使GPIO4引腳輸出低電平
原文鏈接:https://blog.csdn.net/weixin_42963900/article/details/127536968
相關推薦
- 2022-03-24 Qt開發實現跨窗口信號槽通信_C 語言
- 2022-11-19 springboot整合使用云服務器上的Redis方法_Redis
- 2024-01-28 Mybatis Example 用法手冊,接口方法和實例方法
- 2022-08-20 在?pytorch?中實現計算圖和自動求導_python
- 2022-04-09 python多線程互斥鎖與死鎖問題詳解_python
- 2022-09-16 Oracle查看表空間使用率以及爆滿解決方案詳解_oracle
- 2022-08-03 GoFrame?glist?基礎使用和自定義遍歷_Golang
- 2022-04-09 一起來了解python的運算符_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支