环境:ubuntu20.04、source insight4.0、secure CRT

内核版本:Linux-3.14-fs4412

开发板:fs4412(ARM)

1、字符驱动代码

//头文件
#include <linux/module.h>
#include <linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include <linux/kernel.h>
#include<linux/uaccess.h>
#include<linux/string.h>
#include <linux/device.h>
#include <asm/io.h>


#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");














2、app_ioctl用户程序代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#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 <sys/ioctl.h>

       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 <sys/ioctl.h>

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



3、common.h头文件

#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

4、Makefile部分


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

Logo

更多推荐