//头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "comm.h"
//定义全局结构体
struct global_struct{
char mem[128];
int len;
//定义Cdev结构体,字符设备对象结构体 Linux用来管理字符设备
struct cdev cdev;
}glo;
struct class* cls;
//定义设备号变量
unsigned int major=500;
unsigned int minor=0;
unsigned int count=1;
const char *name = "dzc";
static dev_t devno;
void led_sw(int num,char on)
{
/*
led2 GPX2_7
GPX2CON 0x11000c40
[31:28] = 0x1 as output mode
GPX2DAT 0x11000c44
[7] = 0/1
led3 GPX1_0
GPX1CON 0x11000c20
[3:0] = 0x1 as output mode
GPX1DAT 0x11000c24
[0] = 0/1
led3 GPF3_4
GPF3CON 0x114001e0
[19:16] = 0x1 as output mode
GPF3DAT 0x114001e4
[4] = 0/1
内核和app只能访问虚拟地址:
*/
// struct led_ctl ledctl;
#define GPX2CON 0x11000c40
#define GPX2DAT 0x11000c44
#define GPX1CON 0x11000c20
#define GPX1DAT 0x11000c24
#define GPF3CON 0x114001e0
#define GPF3DAT 0x114001e4
//将物理地址映射为虚拟地址
void *conf = ioremap(GPX2CON,4);
void *data = ioremap(GPX2DAT,4);
void *conf2 = ioremap(GPX1CON,4);
void *data2 = ioremap(GPX1DAT,4);
void *conf3 = ioremap(GPF3CON,4);
void *data3 = ioremap(GPF3DAT,4);
//解决volatile的问题
int val = readl(conf);
val &= ~(0xF<<28);
val |= 1<<28;
writel(val,conf);
val = readl(conf2);
val =(val & ~(0xf) )|0x1;
writel(val,conf2);
val = readl(conf3);
val =(val & ~(0xf<<16) )|(0x1<<16);
writel(val,conf3);
if(num==2)
{
if(on==1){
val = readl(data);
val |= 1<<7;
writel(val,data);
}if(on==0){
val = readl(data);
val &= ~(1<<7);
writel(val,data);}
}
if(num==3)
{
if(on){
val = readl(data2);
val |= 1;
writel(val,data2);
}else {
val = readl(data2);
val &= ~(1);
writel(val,data2);}
}
if(num==4)
{
if(on){
val = readl(data3);
val |= 1<<4;
writel(val,data3);
}else {
val = readl(data3);
val &= ~(1<<4);
writel(val,data3);}
}
iounmap(conf);
iounmap(data);
iounmap(conf2);
iounmap(data2);
iounmap(conf3);
iounmap(data3);
}
//自定义驱动方法open/release和应用中的open和close对应
int chrdev_open(struct inode * inode, struct file * file)
{
//全局变量的结构体指针,驱动是专用的,目的支持多个应用程序同时调用
file->private_data = container_of(inode->i_cdev,struct global_struct, cdev);
printk("driver_open26\n");
return 0;
}
int chrdev_release(struct inode * inode, struct file * file)
{
//全局变量的结构体指针,驱动是专用的,目的支持多个应用程序同时调用
struct global_struct* glo = file->private_data;
printk("global.len=%d\n",glo->len);
printk("driver_close34\n");
return 0;
}
ssize_t chrdev_write(struct file *file,char __user *buf,size_t cnt,loff_t* lops)
{
struct global_struct* glo = file->private_data;
//判断有效数据长度
if(cnt<0)return -EINVAL;
//用户空间参数拷贝到内核
if(cnt>128-glo->len)cnt = 128-glo->len;
//失败时返回值是不能写的字节数,成功返回0
if(copy_from_user(glo->mem+glo->len,buf,cnt))
{
printk("copy_from_user failed\n");
return -EFAULT;
} else{
glo->len+=cnt;
}
printk("glo->mem%s\n",glo->mem);
return cnt;
}
//
ssize_t chrdev_read(struct file *file,char __user *buf,size_t cnt,loff_t* lops)
{
struct global_struct *glo = file->private_data;
//判断有效数据长度
if(cnt<0)
return -EINVAL;//无效的参数
if(cnt>glo->len)
cnt = glo->len;
//copy_to_user失败时,返回值不能读到的字节数成功返回0
//将系统用的结果返回到用户空间
if(!copy_to_user(buf,glo->mem, cnt))
{
//擦除已经读取的数据
strcpy(glo->mem, glo->mem+cnt);
//更新,减去有效数据长度
glo->len -=cnt;
}else{
printk("copy_to_user failed\n");
return -EINVAL;
}
return cnt;
}
//文件描述 命令 地址
long chdev_ioctl(struct file *file, unsigned int cmd, unsigned long addr)
{
int ret;
struct tv_chnl tvchl;
struct tv_sw tvsw;
// struct tv_stat tvstat;
struct led_ctl ledctl;
switch(cmd){
case LED_CMD_CTL:
ret = copy_from_user(&ledctl,(void*)addr,sizeof(ledctl));
led_sw(ledctl.num,ledctl.on);
break;
case TV_CMD_CHNL:
ret = copy_from_user(&tvchl, (void*)addr,sizeof(tvchl));
printk("%s->%d chnl updown%d chno%d\n",__func__,__LINE__,tvchl.updown,tvchl.chnlno);
break;
case TV_CMD_SW:
ret = copy_from_user(&tvsw,(void*)addr,sizeof(tvsw));
printk("sw=%d\n",tvsw.sw);
break;
}
return 0;
}
//字符设备驱动与内核的接口,是用户空间对Linux进行系统调用最终的落实者
//这个结构体包含对文件的读写,控制等成员函数
struct file_operations fops={
.owner=THIS_MODULE,//指向拥有这个模块结构的指针,用来在他的操作还在被使用时阻止模块被卸载
//常用接口,用用层调用对应的驱动层来实现
.open = chrdev_open,
.release = chrdev_release,
// .write = chrdev_write,
// .read = chrdev_read,
.unlocked_ioctl = chdev_ioctl,
};
//1. 模块入口
int modx_init(void){
int ret=0;
//获取设备号
devno = MKDEV(major, minor);
//注册设备号
if(register_chrdev_region(devno,count,name))
{
printk("regist_chrdev_region faile\n");
return -1;
}
//初始化cdev
cdev_init(&glo.cdev,&fops);
//向内核添加cdev
ret = cdev_add(&glo.cdev,devno,count);
if(ret<0)
{
printk("cdev_add failed,err%d\n",ret);
return -24;
}
//创建字符设备驱动节点,创建一个类
cls = class_create(THIS_MODULE, "CHRDEV CLASS");
if(!cls)
{
printk("clas err\n");
return -26;
}
//在dev/创建一个设备节点 /dev/dev0
device_create(cls, NULL, devno,NULL,"chdev%d",0);
//初始化自定义结构体
memset(glo.mem,0,sizeof(glo.mem));
glo.len=0;
printk("devno%d major=%dminor=%d,\n",devno,MAJOR(devno),MINOR(devno));
printk("init\n");
return 0;
}
//2. 模块出口
void mod_exit(void){
//释放资源
unregister_chrdev_region(devno, count);
printk("cleanup_module\n");
}
module_init(modx_init);
module_exit(mod_exit);
//3. 许可证
MODULE_LICENSE("GPL");
///MODULE_DESCRIPTION("THE most simple kernel module in the history");
#include
#include
#include
#include
#include
#include "comm.h"
int main()
{
int ret;
struct msg_struct msgusr;
struct tv_sw tvsw;
struct tv_menu tvmenu;
struct tv_chnl tvchnl;
struct tv_stat tvstat;
struct led_ctl ledctl;
int fd = open("/dev/chdev0",O_RDWR);
if(fd < 0 ){
perror("open err");
return 0;
}
/*
#include
int ioctl(int fd, unsigned long cmd, long args);
cmd,命令
args,命令的参数, 一般是地址
tvsw.sw = 1;
ret = ioctl(fd,TV_CMD_SW,(long)&tvsw);
tvchnl.updown = -1;
tvchnl.chnlno = 56;
ret = ioctl(fd,TV_CMD_CHNL, (long)&tvchnl);
memset(&tvstat,0,sizeof(tvstat));
ret = ioctl(fd,TV_CMD_STAT,(long)&tvstat);
printf("get stat:chnl%d color%d vol%d\n",tvstat.chnl,tvstat.color,tvstat.vol);
*/
/*
#include
int ioctl(int fd, unsigned long cmd, long args);
cmd,命令
args,命令的参数, 一般是地址
*/
loop:
printf("pls select led 2 3 4 \n");
scanf("%d",&ledctl.num);
//选中LED
printf("pls select 1--on 0--off\n");
scanf("%d",&ledctl.on);
ret = ioctl(fd,LED_CMD_CTL,(long)&ledctl );
if(ret < 0 ){
perror("ioctl err");
return 0;
}
goto loop;
close(fd);
return 0;
}
#ifndef COMM_H__
#define COMM_H__
struct msg_struct {
char type;
int temp;
char des[128];
};
#define TV_CMD_SW (5001)
#define TV_CMD_VOL (5002)
#define TV_CMD_CHNL (5003)
#define TV_CMD_MENU (5004)
#define TV_CMD_STAT (5005)
#define LED_CMD_CTL (5010)
struct led_ctl{
char on; //0-off 1-on
int num;
};
struct tv_sw {
char sw; //0-off 1-on
};
struct tv_vol {
char vol; //0-down 1-up
};
struct tv_chnl{
char updown; //0-down 1-up -1:invalid
int chnlno; //
};
struct tv_menu {
char light;
char color;
char dbd;
};
struct tv_stat {
char vol;
char chnl;
char color;
};
#endif
KERNEL_PATH = /home/dzc/tftpboot/linux-3.14-fs4412 ## kernel source must be compiled
obj-m += chrdev.o
all:
make modules -C $(KERNEL_PATH) M=$(shell pwd) ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
clean:
rm *.o *.ko *.out *.mod.c *.order Module.symvers -rf
Linux字符驱动开发__创建字符设备驱动_嵌入式Home的博客-CSDN博客环境:ubuntu20.04、source insight4.0、secure CRT内核版本:Linux-3.14-fs4412开发板:fs4412(ARM)一、环境安装步骤1.将内核拷贝到虚拟机tftpboot目录下(目的使用tftp挂载到开发板上,见前章)2.配置环境变量,使用CRT终端操作注:第一次使用要用网线,并关闭网络托管sudo /etc/init.d/network-manager stopset ipaddr 192.168.3.202set ser.https://blog.csdn.net/daizhichaoaa/article/details/123533060