Level11day6作业

1.操作寄存器和出错处理实验

代码如下:

驱动程序代码

//------------------led_drv.c------------------
//驱动程序代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>

#include <asm/io.h>
#include <linux/slab.h>
#include <asm/string.h>



#define GPX2CON_ADDR 0x11000c40
#define GPX2_SIZE 8

const char led_dev_name[] = "led_dev";

struct led_desc {
	unsigned int dev_major;		//主设备号
	unsigned int dev_minor;		//次设备号
	struct class *cls;			
	struct device* dev;			//创建设备文件
	void *reg_virt_base;		//表示是寄存器地址到基准值
};
struct led_desc *led_dev;		//表示一个全局的设备对象

int chr_drv_open (struct inode *inode, struct file *filp)
{
	printk("------------%s----------\n",__FUNCTION__);
	return 0;
}
ssize_t chr_drv_read (struct file * filp, char __user *buf, size_t count, loff_t *fpos)
{
	int buf_read = 666;
	int ret;
	
	ret = copy_to_user(buf,&buf_read,count);
	if(ret == 0)
	{
		printk("------------%s----------\n",__FUNCTION__);
	}
	else 
	{
		printk("KERN:Error to read\n");
		return -EFAULT;
	}
	return 0;
}

ssize_t chr_drv_write (struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	int buf_write = 0;
	int ret; 
	unsigned int value;
	
	ret = copy_from_user(&buf_write,buf,count);

	if(ret == 0)
	{
		printk("__KERN__:%d\n",buf_write);
		if(buf_write == 0){	
			value = readl((led_dev->reg_virt_base + 4));
			value &= ~(0x1<<7);
			writel(value,led_dev->reg_virt_base + 4);
		}
		else{
			value = readl((led_dev->reg_virt_base + 4));
			value |= (0x1<<7);
			writel(value,led_dev->reg_virt_base + 4);
		}
	}
	else 
	{
		printk("KERN:Error to read\n");
		return -EFAULT;
	}
	return 0;
}

int chr_drv_close (struct inode *inode, struct file *filp)
{
	printk("------------%s----------\n",__FUNCTION__);
	return 0;
}

const struct file_operations major_fops = {
		.open = chr_drv_open,
		.read = chr_drv_read,
		.write = chr_drv_write,
		.release = chr_drv_close,
	};

static int __init my_chrdrv_init(void)
{
	long ret;
	unsigned int value;
	//0. 实例化全局的设备对象--分配空间
	//  GFP_KERNEL 如果当前内存不够用到时候,该函数会一直阻塞(休眠)
	led_dev = (struct led_desc *)kmalloc(sizeof(struct led_desc),GFP_KERNEL);
		
	if(led_dev == NULL){
		printk(KERN_ERR "malloc error\n");
		return -ENOMEM;
	}
	
	memset((void*)led_dev,1,sizeof(struct led_desc));		//初始化led_dev
	
	//一般都是申请资源
	//申请设备号
	led_dev->dev_major = register_chrdev(0, led_dev_name, &major_fops);
	led_dev->dev_minor = 0;

	if (led_dev->dev_major < 0){
		printk(KERN_ERR "register failed\n");
		goto err0;
	}

	// 2,创建设备文件
	led_dev->cls = class_create(THIS_MODULE,"led_cls");

	//IS_ERR 用于内核判断指针合法性,包括指针是否为NULL,以及指针访问非法地址
	//合法为0 非法不为0
	if(IS_ERR(led_dev->cls))		
	{
		printk(KERN_ERR "class create failed\n");
		ret = PTR_ERR(led_dev->cls); //将指针出错的具体原因转换成一个出错码
		goto err1;
	}

	// /dev/led0
	//下面的"mychr%d",0 等效于 "mychr0"
	led_dev->dev = device_create(led_dev->cls,NULL,MKDEV(led_dev->dev_major, led_dev->dev_minor),NULL,"myled%d",0);
	
	if(IS_ERR(led_dev->dev))
	{
		printk(KERN_ERR "devic create failed\n");
		ret = PTR_ERR(led_dev->dev); //将指针出错的具体原因转换成一个出错码
		goto err2;
	}

	// 3,硬件初始化
	// 对地址进行映射
	led_dev->reg_virt_base = ioremap(GPX2CON_ADDR, GPX2_SIZE);	
	//下面做判断也可以用IS_ERR()
	if(led_dev->reg_virt_base == NULL)
	{
		printk(KERN_ERR "ioremap error\n");
		ret = -ENOMEM;
		goto err3;
	}

	// gpio的输出功能的配置
	value = readl(led_dev->reg_virt_base);
	value &= ~(0xf << 28);
	value |= (0x1 << 28);
	writel(value,led_dev->reg_virt_base);
	
	return 0;

err3:
	device_destroy(led_dev->cls,MKDEV(led_dev->dev_major, led_dev->dev_minor));
	
err2:
	class_destroy(led_dev->cls);

err1:
	unregister_chrdev(led_dev->dev_major, led_dev_name);

err0:
	kfree((void*)led_dev);
	return -ENOMEM;
}

static void __exit my_chrdrv_exit(void)
{
	//一般都是释放资源
	//释放设备号资源	
	iounmap(led_dev->reg_virt_base);
	device_destroy(led_dev->cls,MKDEV(led_dev->dev_major, led_dev->dev_minor));
	class_destroy(led_dev->cls);
	unregister_chrdev(led_dev->dev_major, led_dev_name);
	kfree((void*)led_dev);
}

MODULE_LICENSE("GPL");

module_init(my_chrdrv_init);
module_exit(my_chrdrv_exit);

APP代码

//--------------led_test.c-------------------
//APP 代码 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	int fd;
	int buf = 233;
	
	fd = open("/dev/myled0",O_RDWR);
	
	if(fd < 0)
	{
		perror("open");
		exit(1);
	}
	
	while(1){
		buf = 0;
		write(fd, &buf, 4);
		sleep(1);
		
		buf = 1;
		write(fd, &buf, 4);
		sleep(1);
	}
		
	read(fd, &buf, 4);
		
	close(fd);

	return 0;
}

Makefile代码

#Makefile代码
ROOTFS_DIR = /home/linux/Level11

APP_NAME = led_test

CROSS_COMPILE = arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc

ifeq ($(KERNELRELEASE), )

KERNEL_DIR = /home/linux/Level10/day6/linux-5.4.79
CUR_DIR = $(shell pwd)

all :
		make -C  $(KERNEL_DIR) M=$(CUR_DIR) modules -j6 ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
		$(CC) $(APP_NAME).c -o $(APP_NAME)

clean :
		make -C  $(KERNEL_DIR) M=$(CUR_DIR) clean
		clear

install:
		cp -raf  *.ko  $(APP_NAME)   $(ROOTFS_DIR)/drv_module
		
else
obj-m += led_drv.o

endif

效果展示

[root@farsight ]# insmod led_drv.ko
[root@farsight ]# ./led_test
[ 1781.874456] ------------chr_drv_open----------
[ 1781.877516] __KERN__:0
[ 1782.880055] __KERN__:1
[ 1783.881074] __KERN__:0
[ 1784.882093] __KERN__:1
[ 1785.883115] __KERN__:0
[ 1786.884136] __KERN__:1
[ 1787.384514] ------------chr_drv_close----------
[root@farsight ]# rmmod led_drv.ko

灯一闪一闪,效果达成。