NanoPi NEO Air uses eight: write a simple driver and application

Create a new test directory in the Ubuntu user folder. For example, my linux source directory is: / home/ql/linux/H3/linux.
The directory where the driver and application source code are placed is / home/ql/linux/H3/MyDriver/01_chrdevbase
Then add 3 files in this directory:

Drive source file

chrdevbase.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
file name 		:  chrdevbase.c
 author 	  	:  Zuo Zhongkai
 edition 	   	:  V1.0
 describe 	   	:  chrdevbase driver file.
other 	   	:  nothing
 Forum 	   	:  www.openedv.com
 journal 	   	:  First version v1.0 created by Zuo Zhongkai on January 30, 2019
***************************************************************/

#define CHRDEVBASE_MAJOR 	 two hundred 				/*  Main equipment No*/
#define CHRDEVBASE_NAME 		 "chrdevbase"  	/*  Device name*/

static char readbuf[100];		/* Read buffer */
static char writebuf[100];		/* Write buffer */
static char kerneldata[] = {"kernel data!"};

/*
 * @description		: open device
 * @param - inode 	: inode passed to driver
 * @param - filp 	: The device file has a file structure called private_ Member variable of data
 * 					  Generally, private is used when open ing_ Data points to the device structure.
 * @return 			: 0 success; Other failures
 */
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
	//printk("chrdevbase open!\r\n");
	return 0;
}

/*
 * @description		: Read data from device 
 * @param - filp 	: Device file to open (file descriptor)
 * @param - buf 	: Data buffer returned to user space
 * @param - cnt 	: Length of data to read
 * @param - offt 	: Offset relative to the first address of the file
 * @return 			: The number of bytes read. If it is negative, it indicates that the read failed
 */
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue = 0;
	
	/* Send data to user space */
	memcpy(readbuf, kerneldata, sizeof(kerneldata));
	retvalue = copy_to_user(buf, readbuf, cnt);
	if(retvalue == 0){
		printk("kernel senddata ok!\r\n");
	}else{
		printk("kernel senddata failed!\r\n");
	}
	
	//printk("chrdevbase read!\r\n");
	return 0;
}

/*
 * @description		: Write data to device 
 * @param - filp 	: A device file that represents an open file descriptor
 * @param - buf 	: Data to write to device
 * @param - cnt 	: Length of data to write
 * @param - offt 	: Offset relative to the first address of the file
 * @return 			: The number of bytes written. If it is negative, it indicates that the write failed
 */
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue = 0;
	/* Receive the data passed from user space to the kernel and print it out */
	retvalue = copy_from_user(writebuf, buf, cnt);
	if(retvalue == 0){
		printk("kernel recevdata:%s\r\n", writebuf);
	}else{
		printk("kernel recevdata failed!\r\n");
	}
	
	//printk("chrdevbase write!\r\n");
	return 0;
}

/*
 * @description		: Turn off / release the device
 * @param - filp 	: Device file to close (file descriptor)
 * @return 			: 0 success; Other failures
 */
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
	//printk("chrdevbase release!\r\n");
	return 0;
}

/*
 * Device operation function structure
 */
static struct file_operations chrdevbase_fops = {
	.owner = THIS_MODULE,	
	.open = chrdevbase_open,
	.read = chrdevbase_read,
	.write = chrdevbase_write,
	.release = chrdevbase_release,
};

/*
 * @description	: Drive entry function 
 * @param 		: nothing
 * @return 		: 0 success; Other failures
 */
static int __init chrdevbase_init(void)
{
	int retvalue = 0;

	/* Register character device driver */
	retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
	if(retvalue < 0){
		printk("chrdevbase driver register failed\r\n");
	}
	printk("chrdevbase init!\r\n");
	return 0;
}

/*
 * @description	: Drive exit function
 * @param 		: nothing
 * @return 		: nothing
 */
static void __exit chrdevbase_exit(void)
{
	/* Unregister character device driver */
	unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
	printk("chrdevbase exit!\r\n");
}

/* 
 * Specify the above two functions as the driver's entry and exit functions 
 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

/* 
 * LICENSE And author information
 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

Application source file

chrdevbaseApp.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
file name 		:  chrdevbaseApp.c
 author 	  	:  Zuo Zhongkai
 edition 	   	:  V1.0
 describe 	   	:  chrdevbase test APP.
other 	   	:  Usage:. / chrdevbase / dev / chrdevbase < 1 > | < 2 >
  			 argv[2] 1:read file
  			 argv[2] 2:Write file		
Forum 	   	:  www.openedv.com
 journal 	   	:  First version v1.0 created by Zuo Zhongkai on January 30, 2019
***************************************************************/

static char usrdata[] = {"usr data!"};

/*
 * @description		: main main program
 * @param - argc 	: argv Number of array elements
 * @param - argv 	: Specific parameters
 * @return 			: 0 success; Other failures
 */
int main(int argc, char *argv[])
{
	int fd, retvalue;
	char *filename;
	char readbuf[100], writebuf[100];

	if(argc != 3){
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];

	/* Open driver file */
	fd  = open(filename, O_RDWR);
	if(fd < 0){
		printf("Can't open file %s\r\n", filename);
		return -1;
	}

	if(atoi(argv[2]) == 1){ /* Read data from driver file */
		retvalue = read(fd, readbuf, 50);
		if(retvalue < 0){
			printf("read file %s failed!\r\n", filename);
		}else{
			/*  If the reading is successful, print out the successfully read data */
			printf("read data:%s\r\n",readbuf);
		}
	}

	if(atoi(argv[2]) == 2){
 	/* Write data to device driver */
		memcpy(writebuf, usrdata, sizeof(usrdata));
		retvalue = write(fd, writebuf, 50);
		if(retvalue < 0){
			printf("write file %s failed!\r\n", filename);
		}
	}

	/* Turn off the device */
	retvalue = close(fd);
	if(retvalue < 0){
		printf("Can't close file %s\r\n", filename);
		return -1;
	}

	return 0;
}

Makefile

KERNELDIR := /home/ql/linux/H3/linux
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.o

build: kernel_modules

kernel_modules:
	make ARCH=arm CROSS_COMPILE=arm-linux- -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	make ARCH=arm CROSS_COMPILE=arm-linux- -C $(KERNELDIR) M=$(CURRENT_PATH) clean

compile

After the above three files are added:

Compile driver

Enter the directory where the source code of the driver file is located and compile the driver:

cd /home/ql/linux/H3/MyDriver/01_chrdevbase
make


The ko file has been compiled.

Compiling applications

Recompile the application and execute

arm-linux-gcc chrdevbaseApp.c -o chrdevbaseApp

After compilation, an executable program called chrdevbaseAPP will be generated. Enter the file chrdevbaseApp command to view the file information of chrdevbaseAPP:

Test run on development board

Transfer the compiled chrdevbase.ko and chrdevbaseApp to the development board through SCP.

scp chrdevbase.ko root@192.168.0.103:/lib/modules/4.14.111/
scp chrdevbaseApp  root@192.168.0.103:/lib/modules/4.14.111/



Use the following command to change the console message level:

echo 5 >/proc/sys/kernel/printk
cat /proc/sys/kernel/printk

Driver module loading

Enter the following command to load the chrdevbase.ko driver file:

insmod chrdevbase.ko


You can see the output of "chrdevbase init!", which is the output of the driver entry function, indicating that the driver module is loaded successfully.

Enter the lsmod command to view the modules existing in the current system:

Enter the cat /proc/devices command to check whether there is a chrdevbase device in the current system:

It can be seen that there is a chrdevbase device in the current system. The main device number is 200, which is consistent with the main device number we set.

Application testing

To load the driver successfully, you need to create a corresponding device node file in the / dev directory. The application completes the operation of the specific device by operating the device node file. Enter the following command to create the / dev/chrdevbase device node file:
mknod /dev/chrdevbase c 200 0
Where "mknod" is the command to create a node, "/ dev/chrdevbase" is the node file to be created, "c" indicates that this is a character device, "200" is the primary device number of the device, and "0" is the secondary device number of the device. After creation, the file / dev/chrdevbase will exist. You can use the ls /dev/chrdevbase -l command to view it.

Everything under Linux is a file, so you can read, write, open and close the file like under windows. The operation of files is implemented in the application program. Here, you can run it directly. First, read and enter the following command:
./chrdevbaseApp /dev/chrdevbase 1


Next, test the write operation to the chrdevbase device, and enter the following command:
./chrdevbaseApp /dev/chrdevbase 2

Unloading the driver module

rmmod chrdevbase.ko

Tags: Linux Embedded system

Posted on Sat, 23 Oct 2021 05:25:29 -0400 by Infinitive