Linux_kernel字符设备驱动12

news2024/10/9 4:53:24

一、字符设备的编程框架

        在Linux_kernel驱动开发11中,我们介绍的系统调用。只是为了做一个实验,在真正开发时,我们并不会直接在内核中添加一个新的系统调用,这样做会导致内核体积变大。

        1、字符设备结构体

                我们实现一个硬件字符设备的驱动程序,实际上是实例化一个struct cdev类型的对象。

        【1】struct cdev;        // 存储字符设备的相关信息

注意:

        在该结构体中,我们只需关注这两个成员,其他的成员由内核自己维护

        【2】dev_t dev;        // 指示当前的设备号

         【3】const struct file_operations *ops;        // 操作函数的集合

                1)设备号
        【1】区分主次设备号

设备号(32bit) = 主设备号(12bit [msb])+ 次设备号(20bit [lsb])

                1】示例

        ls -l /dev/tty0

主次设备号的范围理论上都是[0, 255]

主设备号:区分不同类型的设备

次设备号:区分同一类型设备的不同个体

        MINORBITS:次设备号的位数

        MINORMASK:次设备号掩码

        MAJOR(dev):得到主设备号

        MINOR(dev):得到次设备号

        MKDEV(ma,mi):将主设备号与次设备号合为一个32bit整型数(dev_t

        【2】静态注册设备号

                就是自己先挑一个没有被内核占用的设备号去注册

                0】查看被内核占用的设备号

        cat /proc/devices

                1】register_chrdev_region(注册设备号)

注释:

        from:要注册的起始设备号

        count:要连续注册的设备号个数

        name:给设备起的名称

                2】unregister_chrdev_region(注销设备号)

注释:

        from:要注销的起始设备号

        count:要连续注销的设备号个数

        【3】静态注册实验

                1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

                2】创建新的工程

        mkdir chrdev

                3】编写程序

        vim chrdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>

#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	10
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"leds"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int dev = 0;	// store the major dev number and the minor dev number

int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;	// major dev number
	int minor = CHRDEV_MINOR;		// minor dev number

	// dev = major << 20 | minor;
	// there is a define func to do this task
	dev = MKDEV(major, minor);

	register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of device

	return 0;
}

void __exit chrdev_exit(void)
{
	unregister_chrdev_region(dev, CHRDEV_NUM);

	return ;
}

module_init(chrdev_init);
module_exit(chrdev_exit);

                4】编写Makefile

        vim Makefile

obj-m += chrdev.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

                5】编译工程

        make

                6】下位机验证

注意:我们现在只有设备号,而没有设备文件

        【4】动态注册设备号

                内核自己找一个没有注册的设备号,注册完归程序员使用

                1】alloc_chrdev_region(注册设备号)

注释:

        dev:回填设备号

        baseminor:次设备号的基值(起始值)

        count:要连续注册的设备号个数

        name:给设备起的名称

                2】unregister_chrdev_region(注销设备号)

注释:

        from:要注销的起始设备号

        count:要连续注销的设备号个数

         【5】动态注册实验

                1】编写程序

        vim chrdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>

#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	10
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"leds"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int dev = 0;	// store the major dev number and the minor dev number
#if 0
// fixed register
int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;	// major dev number
	int minor = CHRDEV_MINOR;		// minor dev number

	// dev = major << 20 | minor;
	// there is a define func to do this task
	dev = MKDEV(major, minor);

	register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of device

	return 0;
}
#else
// variable register
int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;	// major dev number
	int minor = CHRDEV_MINOR;		// minor dev number

	// there is a define func to register the number of devices automatically
	alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);
	major = MAJOR(dev);	// gain the major dev number
	minor = MINOR(dev);	// gain the minor dev number
	
	printk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor);

	return 0;
}
#endif

void __exit chrdev_exit(void)
{
	unregister_chrdev_region(dev, CHRDEV_NUM);

	return ;
}

module_init(chrdev_init);
module_exit(chrdev_exit);

                2】编写Makefile

        vim Makefile

obj-m += chrdev.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

                3】编译工程

        make

                4】下位机验证

注意:我们现在只有设备号,而没有设备文件

        2)操作函数的集合
        【1】操作函数集合

       const struct file_operations *ops;        // 操作函数的集合

        实现一个字符设备驱动程序的主要编程工作都集中在操作函数集合,我们将来具体到某一个字符设备驱动程序的时候,只需要实现下列函数集合的子集就可以了。

        【2】内核中提供的操作cdev的API

                1】cdev_init(初始化cdev结构体)

                2】cdev_add(将cdev注册到内核)

注释:

        p:要注册的cdev地址

        dev:要注册的设备号

        count:要连续注册的cdev个数

                3】cdev_del(从内核中注销cdev)

注释:

        p:要注销的cdev地址

        【3】实验

                 1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

                2】创建新的工程

        mkdir chrdev_func

                3】编写程序

        vim chrdev_func.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	10
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int dev = 0;	// store the major dev number and the minor dev number

// 1_step :define a struct cdev be named led_cdev
struct cdev led_cdev;

// 3_step :implement the function of led_fops
int led_open(struct inode *inode, struct file *fp)
{
	printk(KERN_EMERG "enter:%s\n", __FUNCTION__);

	return 0;
}

int led_close(struct inode *inode, struct file *fp)
{
	printk(KERN_EMERG "enter:%s\n", __FUNCTION__);

	return 0;
}

// 2_step :define a struct file_operation be named led_fops
// what functions shall we to implement ?
// there is turn_on and turn_off of the leds
// So, we should to implement the function of open() and release(), eithor or we should keep up the same type as the definations of the struct file_operations
struct file_operations led_fops = {
	.owner = THIS_MODULE,
	// int (*open) (struct inode *, struct file *);
	.open = led_open,
	// int (*release) (struct inode *, struct file *);
	.release = led_close
};

#if 0
// fixed register
int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;	// major dev number
	int minor = CHRDEV_MINOR;		// minor dev number

	// dev = major << 20 | minor;
	// there is a define func to do this task
	dev = MKDEV(major, minor);

	register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of device

	return 0;
}
#else
// variable register
int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;	// major dev number
	int minor = CHRDEV_MINOR;		// minor dev number

	// there is a define func to register the number of devices automatically
	alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);
	major = MAJOR(dev);	// gain the major dev number
	minor = MINOR(dev);	// gain the minor dev number
	
	printk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor);

	// 4_step :initalize the struct cdev object led_cdev
	cdev_init(&led_cdev, &led_fops);

	// 5_step :register led_cdev into Kernel
	cdev_add(&led_cdev, dev, CHRDEV_NUM);

	return 0;
}
#endif

void __exit chrdev_exit(void)
{
	// 6_step :destory cdev
	cdev_del(&led_cdev);
	unregister_chrdev_region(dev, CHRDEV_NUM);

	return ;
}

module_init(chrdev_init);
module_exit(chrdev_exit);

                4】编写Makefile

        vim Makefile

obj-m += chrdev_func.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

                5】编译工程

        make

                6】下位机安装模块

                7】写一个应用层程序测试

        mkdir test

        cd test

        vim led_test.c

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

#define CDEV_PATH	"/dev/myleds"

int main(void)
{
	int fd = 0;
	
	if((fd = open(CDEV_PATH, O_RDWR)) < 0) {
		perror("open()");
		return -1;
	}

	printf("open success!\n");

	sleep(5);

	printf("closing...\n");

	close(fd);

	return 0;
}

        arm-cortex_a9-linux-gnueabi-gcc led_test.c -o led_test

        cp led_test /nfs_share/_install/

        8】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 10

        ./led_test

        9】疑惑

        内核中的打印函数与应用程序中的打印函数,执行顺序孰先孰后?

        不确定:内核打印输出的是自己维护的缓冲区,应用程序打印输出的是标准输出缓冲区

二、GPIO库

        1、读懂开发板原理图

LED0        GPIOB26

LED1        GPIOC11

LED2        GPIOC7

LED3        GPIOC12

输出低电平,灯亮

输出高电平,灯灭

        2、CPU Data Sheet

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

#define GPIOBOUT        *(volatile unsigned int *)0xC001B000

        3、内核中提供的操作GPIO的API

        【0】gpio宏定义

        【1】gpio_request(申请GPIO管脚)

int gpio_request(unsigned gpio, const char *label)

        【2】使用GPI管脚

                1】gpio_direction_input(设置输入)

                2】gpio_direction_output(设置输出)

                3】gpio_set_value(设置value)

                4】gpio_get_value(获取value)

        【3】gpio_free(释放GPIO管脚)

void gpio_free(unsigned gpio)

        4、实验

        【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir led_drv

        【3】编写程序

        vim led_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <mach/platform.h>

#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	26
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"
#define HIGH			1
#define LOW				0

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int dev = 0;	// store the major dev number and the minor dev number

// 1_step :define a struct cdev be named led_cdev
struct cdev led_cdev;

// 3_step :implement the function of led_fops
int led_open(struct inode *inode, struct file *fp)
{
	printk(KERN_EMERG "enter:%s\n", __FUNCTION__);
	// c_step :set the value=0(turn on) of the gpio
	gpio_set_value(PAD_GPIOB26, LOW);

	return 0;
}

int led_close(struct inode *inode, struct file *fp)
{
	printk(KERN_EMERG "enter:%s\n", __FUNCTION__);
	// c_step :set the value=1(turn off) of the gpio
	gpio_set_value(PAD_GPIOB26, HIGH);

	return 0;
}

// 2_step :define a struct file_operation be named led_fops
// what functions shall we to implement ?
// there is turn_on and turn_off of the leds
// So, we should to implement the function of open() and release(), eithor or we should keep up the same type as the definations of the struct file_operations
struct file_operations led_fops = {
	.owner = THIS_MODULE,
	// int (*open) (struct inode *, struct file *);
	.open = led_open,
	// int (*release) (struct inode *, struct file *);
	.release = led_close
};

#if 0
// fixed register
int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;	// major dev number
	int minor = CHRDEV_MINOR;		// minor dev number

	// dev = major << 20 | minor;
	// there is a define func to do this task
	dev = MKDEV(major, minor);

	register_chrdev_region(dev, CHRDEV_NUM, CHRDEV_NAME);	//register the number of device

	return 0;
}
#else
// variable register
int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;	// major dev number
	int minor = CHRDEV_MINOR;		// minor dev number

	// there is a define func to register the number of devices automatically
	alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);
	major = MAJOR(dev);	// gain the major dev number
	minor = MINOR(dev);	// gain the minor dev number
	
	printk(KERN_EMERG "dev number is :%d\n major number is :%d\n minor number is :%d\n", dev, major, minor);

	// 4_step :initalize the struct cdev object led_cdev
	cdev_init(&led_cdev, &led_fops);

	// 5_step :register led_cdev into Kernel
	cdev_add(&led_cdev, dev, CHRDEV_NUM);

	// a_step :apply gpio
	gpio_request(PAD_GPIOB26, "LED0");

	// b_step :set the default value=1(turn_off) of GPIOB26
	gpio_direction_output(PAD_GPIOB26, HIGH);

	return 0;
}
#endif

void __exit chrdev_exit(void)
{
	// e_step :release gpio
	gpio_free(PAD_GPIOB26);

	// 6_step :destory cdev
	cdev_del(&led_cdev);
	unregister_chrdev_region(dev, CHRDEV_NUM);	// unregister the number of dev

	return ;
}

module_init(chrdev_init);
module_exit(chrdev_exit);

        【4】编写Makefile

        vim Makefile

obj-m += led_drv.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】下位机安装模块

        【7】编写应用层程序

        mkdir test

        cd test

        vim led_test.c

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

#define CDEV_PATH	"/dev/myleds"

int main(void)
{
	int fd = 0;
	
	if((fd = open(CDEV_PATH, ORDWR)) < 0) {
		perror("open()");
		return -1;
	}

	printf("open success!\n");

	sleep(5);

	printf("closing...\n");

	close(fd);

	return 0;
}

        arm-cortex_a9-linux-gnueabi-gcc led_test.c -o led_test

        cp led_test /nfs_share/_install/

        【8】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 26

        ./led_test

三、用户态与内核态的数据交互

        用户空间不能直接访问内核空间

        内核空间不能直接访问用户空间

        1)内核中提供的数据交互的API

        【1】传递多数据
                1】copy_to_user(内核到用户)

int copy_to_user(void __user *to, const void *from, int n)

注释:

        to:内核空间缓冲区地址,

        from:用户空间地址

        n:数据字节数

        retval:不能被复制的字节数,返回0表示全部复制成功。

                2】copy_from_user(用户到内核)

int copy_from_user(void *to, const void __user *from, int n)

注释:

        to:内核空间缓冲区地址,

        from:用户空间地址

        n:数据字节数

        retval:不能被复制的字节数,返回0表示全部复制成功。

        【2】传递单数据

        可以从指定空间获取单个数据,单个数据并不是指一个字节数据,对ARM而言,一次性可获取一个char、short或者 int型的数据,即1、2或者4字节。

                1】put_user(x, ptr)(内核到用户)

注释:

        x :内核空间的数据,

        p :用户空间的指针。

        传递成功,返回 0,否则返回-EFAULT。

                2】get_user(x, ptr)(用户到内核)

注释:

        x :内核空间的数据,

        p :用户空间的指针。

        传递成功,返回 0,否则返回-EFAULT。

注意:

以上API与C标准库中memcpy(3)相似,但多了一个对访问的空间的权限检查

        2、实验

         【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir param_drv

        【3】编写程序

        vim param_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>

#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	26
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"
#define HIGH			1
#define LOW				0
#define LED0	(PAD_GPIO_B + 26)
#define LED1	(PAD_GPIO_C + 12)
#define LED2	(PAD_GPIO_C + 7)
#define LED3	(PAD_GPIO_C + 11)

unsigned int leds[] = {LED0, LED1, LED2, LED3};
const char *leds_label[] = {"LED0", "LED1", "LED2", "LED3"};

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

int dev = 0;

struct cdev led_cdev;

int k_cmd = 0;	//kernel's buffer
int k_status = 0;	//the state of LEDs

int led_open(struct inode *inode, struct file *fp)
{
	return 0;
}

int led_close(struct inode *indoe, struct file *fp)
{
	return 0;
}

ssize_t led_read(struct file *fp, char __user *buf, size_t len, loff_t *offset)
{
	int ret = 0;

	ret = copy_to_user(buf, &k_status, len);

	return len;
}

ssize_t led_write(struct file *fp, const char __user *buf, size_t len, loff_t *offset)
{
	int ret = 0;
	int i = 0;

	ret = copy_from_user(&k_cmd, buf, len);

	for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) {
		gpio_set_value(leds[i], k_cmd);
	}
	k_status = k_cmd;

	return len;
}

struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_close,
	.read = led_read,
	.write = led_write
};

int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;
	int minor = CHRDEV_MINOR;
	int i = 0;

	alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);
	major = MAJOR(dev);
	minor = MINOR(dev);

	printk(KERN_EMERG "major = %d\nminor = %d\n", major, minor);

	cdev_init(&led_cdev, &led_fops);

	cdev_add(&led_cdev, dev, CHRDEV_NUM);

	for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) {
		gpio_request(leds[i], leds_label[i]);
		gpio_direction_output(leds[i], HIGH);
	}

	return 0;
}

void __exit chrdev_exit(void)
{
	int i = 0;

	for (i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) {
		gpio_free(LED1);
	}

	cdev_del(&led_cdev);
	unregister_chrdev_region(dev, CHRDEV_NUM);

	return ;
}

module_init(chrdev_init);
module_exit(chrdev_exit);

        【4】编写Makefile

        vim Makefile

obj-m += param_drv.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

        【5】编译工程

        make

        【6】下位机安装模块

        【7】编写应用层程序

        mkdir test

        cd test

        vim led_test.c

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

#define ON		0
#define OFF		1
#define CDEV_PATH	"/dev/myleds"

int main(int argc, char *argv[])
{
	int fd = 0;
	int cmd = 0;
	int status = 0;

	if (argc < 2) {
		printf("Usage : %s <on/off>\n", argv[0]);
		return -1;
	}

	if (!strcmp(argv[1], "on")) {
		cmd = ON;
	} else if (!strcmp(argv[1], "off")){
		cmd = OFF;
	} else {
		printf("illegal param\n");
		return -2;
	}
	
	if((fd = open(CDEV_PATH, O_RDWR)) < 0) {
		perror("open()");
		return -3;
	}

	printf("open success!\n");

	write(fd, &cmd, sizeof(cmd));
	read(fd, &status, sizeof(status));

	if (status == ON) {
		printf("Led is On!\n");
	} else {
		printf("Led is Off!\n");
	}

	printf("closing...\n");

	close(fd);

	return 0;
}

        vim Makefile

SRC=led_test.c
OBJ=led_test

ARM_COMPILE=arm-cortex_a9-linux-gnueabi-
GCC=gcc

ROOTFS_PATH=/nfs_share/_install

all:
	$(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ)
	cp $(OBJ) $(ROOTFS_PATH)

clean:
	rm -rf $(OBJ)
        【8】编译工程

        make

        【9】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 26

        ./led_test

四、ioctl

        1)介绍

        2)实操

        【1】进入工程目录

        cd /home/zjd/s5p6818/KERNEL/drivers

        【2】创建新的工程

        mkdir ioctl

        【3】编写程序

        vim ioctl.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zjd");

#define CHRDEV_MAGOR	200
#define CHRDEV_MINOR	26
#define CHRDEV_NUM		1
#define CHRDEV_NAME		"myleds"
#define HIGH			1
#define LOW				0
#define LED0	(PAD_GPIO_B + 26)
#define LED1	(PAD_GPIO_C + 12)
#define LED2	(PAD_GPIO_C + 7)
#define LED3	(PAD_GPIO_C + 11)
#define TURN_ON			LOW
#define TURN_OFF		HIGH



dev_t dev = 0;

struct cdev led_cdev;

typedef struct led_desc{
	unsigned int gpio;
	char *name;
}led_desc_t;

led_desc_t leds[] = {
	{LED0, "LED0"},
	{LED1, "LED1"},
	{LED2, "LED2"},
	{LED3, "LED3"}
};

long led_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
	int k_index = 0;
	int ret = 0;

	ret = copy_from_user(&k_index, (const void *)arg, sizeof(int));
	if (k_index > 4 || k_index < 1)
		return -EINVAL;
	
	switch (cmd) {
		case TURN_ON:
			gpio_set_value(leds[k_index - 1].gpio, LOW);
			break;
		case TURN_OFF:
			gpio_set_value(leds[k_index - 1].gpio, HIGH);
			break;
		default:
			return -EINVAL;
	}

	return arg;
}

struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = led_ioctl
};

int __init chrdev_init(void)
{
	int major = CHRDEV_MAGOR;
	int minor = CHRDEV_MINOR;
	int i = 0;

	alloc_chrdev_region(&dev, CHRDEV_MINOR, CHRDEV_NUM, CHRDEV_NAME);
	major = MAJOR(dev);
	minor = MINOR(dev);

	printk(KERN_EMERG "major = %d\nminor = %d\n", major, minor);

	cdev_init(&led_cdev, &led_fops);

	cdev_add(&led_cdev, dev, CHRDEV_NUM);

	for (i = 0; i < ARRAY_SIZE(leds); i++) {
		gpio_request(leds[i].gpio, leds[i].name);
		gpio_direction_output(leds[i].gpio, HIGH);
	}

	return 0;
}

void __exit chrdev_exit(void)
{
	int i = 0;

	for (i = 0; i < ARRAY_SIZE(leds); i++) {
		gpio_free(leds[i].gpio);
	}

	cdev_del(&led_cdev);
	unregister_chrdev_region(dev, CHRDEV_NUM);

	return ;
}

module_init(chrdev_init);
module_exit(chrdev_exit);

        【4】编写Makefile

        vim Makefile

obj-m += ioctl.o
KERNEL_PATH=/home/zjd/s5p6818/KERNEL/kernel
ROOTFS_PATH=/nfs_share/_install

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
	cp *.ko $(ROOTFS_PATH)

clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean
        【5】编译工程

        make

        【6】下位机安装模块

        【7】编写应用层程序

        mkdir test

        cd test

        vim led_test.c

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

#define ON		0
#define OFF		1
#define CDEV_PATH	"/dev/myleds"

int main(int argc, char *argv[])
{
	int fd = 0;
	int cmd = 0;
	int index = 0;

	if (argc < 3) {
		printf("Usage : %s <on/off> <1/2/3/4>\n", argv[0]);
		return -1;
	}

	if (!strcmp(argv[1], "on")) {
		cmd = ON;
	} else if (!strcmp(argv[1], "off")){
		cmd = OFF;
	} else {
		printf("illegal param\n");
		return -2;
	}
	
	index = atoi(argv[2]);
	if (index < 1 || index > 4) {
		printf("illegal param\n");
		return -2;
	}

	if((fd = open(CDEV_PATH, O_RDWR)) < 0) {
		perror("open()");
		return -3;
	}
	printf("open success!\n");

	ioctl(fd, cmd, &index);

	printf("closing...\n");

	close(fd);

	return 0;
}

     vim Makefile

SRC=led_test.c
OBJ=led_test

ARM_COMPILE=arm-cortex_a9-linux-gnueabi-
GCC=gcc

ROOTFS_PATH=/nfs_share/_install

all:
	$(ARM_COMPILE)$(GCC) $(SRC) -o $(OBJ)
	cp $(OBJ) $(ROOTFS_PATH)

clean:
	rm -rf $(OBJ)
        【8】编译工程

        make

        【9】下位机测试

        手动创建设备文件

        mknod /dev/myleds c 244 26

        ./led_test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2194783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WPF|依赖属性SetCurrentValue方法不会使绑定失效, SetValue方法会使绑定失效?是真的吗?

引言 最近因为一个触发器设置的结果总是不起效果的原因&#xff0c;进一步去了解[依赖属性的优先级](Dependency property value precedence - WPF .NET | Microsoft Learn)。在学习这个的过程中发现对SetCurrentValue一直以来的谬误。 在WPF中依赖属性Dependency property的…

力扣 中等 216组合总和III

文章目录 题目介绍解法 题目介绍 解法 是77.组合链接的扩展 class Solution {List<List<Integer>> result new ArrayList<>();List<Integer> path new ArrayList<>();public List<List<Integer>> combinationSum3(int n, int k) …

流速仪设备操作说明

1 流速仪设备安装 按图示对流速仪设备进行安装&#xff0c;主要是流速仪和电频。 连接电脑&#xff0c;直接插上信号接收器&#xff0c;后面使用蓝牙连接。 2 安装软件 3 双击ParaniWin进行设备连接 3.1 按图片所示&#xff0c;设置端口等信息 3.2 Device Setting —>输…

2024软件设计师高频考点体系—软件工程体系考点大全

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 更多高频考点&#x1f9e7;&#x1f7e5;软件设计师高频考点电子手册✨点击进入&#x1f381;&#x1f7e6; 软件设计师高频考点…

力扣59.螺旋矩阵||

题目链接&#xff1a;59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff…

【C/C++】错题记录(五)

题目一 题目二 在 16 位机器上&#xff0c;通常以 2 字节为边界对齐。 首先看 char a&#xff0c;它占用 1 个字节。接着是 int b&#xff0c;占用 2 个字节。由于要满足边界对齐&#xff0c;在 char a后面会填充 1 个字节&#xff0c;使得 int b从 2 字节边界开始存储。最后是…

TMGM开户后还可以开代理账户吗

在TMGM平台进行交易的人也想开立代理账户。那么&#xff0c;TMGM的代理账户如何开设&#xff1f;答案是&#xff1a;首先需要先开设个人交易账户&#xff0c;然后再申请成为代理商。 1. 先开个人交易账户 在TMGM开设代理账户的首要步骤是先拥有一个个人交易账户。您可以通过访…

Carsim报错总结及解决方法

1. simulink报错&#xff1a;vs_state 、StopMode无法识别 - matlab命令行窗口输入&#xff1a;vs_state -1&#xff0c;StopMode -1 2. Video变暗&#xff0c;无法点击 - 说明书中提示&#xff1a;如果输出文件不存在&#xff08;例如&#xff0c;在单击复制按钮创…

身份证二要素-身份证二要素接口-身份证尔雅欧批量核验

身份证批量核查简介&#xff1a;【身份证批量核查】极速查询、多线程、高并发&#xff0c;可批量上传excel文件&#xff0c;2万条数据约30分钟核验完成。无需技术参与、自主便捷查询 1、登录后&#xff0c;点击右上角【个人中心】按钮&#xff0c;进入个人中心页面 2、进入个…

基于单片机的信号选择与温度变化

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采用DS18B20检测温度&#xff0c;通过三种LED灯代表不同状态。 采用DAC0832显示信号脉冲&#xff0c;通过8位数码管显示温度。 信号脉冲可以根据两个按键分别调整为正弦…

5G NR 切换流程简介

文章目录 切换类型分类切换步骤测量事件分类5G NR系统内切换信令流程 切换类型分类 5G NR 的切换类型分为 系统间切换和系统内切换&#xff0c;而 系统间切换又分为 5G NR 与 4G LTE 和5G NR 与3G WCDMA的切换。站内切换则分为站内切换和站间间切换。 切换步骤 主要分为三个阶…

边缘端大模型是怎么部署的?重点关注哪些?

写在前面 在设备端运行的大语言模型&#xff08;LLMs&#xff09;&#xff0c;即指在边缘设备上运行LLMs&#xff0c;因其出色的隐私保护、低延迟和节省带宽的特点而引起了广泛关注。然而&#xff0c;与功能更为强大的云中心相比&#xff0c;设备端LLMs的能力本质上受到边缘设…

高效数据处理:MapReduce与Hive的实战应用

文章目录 hive分析汇总互联网日志分析1.项目需求2.数据说明3.算法思路 用户电影推荐1.项目需求2.数据说明3.算法思路4.解题步骤 简单数据统计WordCount数据说明 疫情数据分析1.项目需求2.数据说明step1:创建ods层数据表step2&#xff1a;创建dwd层数据表step3&#xff1a;创建d…

Vue84 vue3项目结构分析

打开main.js文件&#xff0c;发现和vue2不同 //引入的不再是Vue构造函数了&#xff0c;引入的是一个名为createApp的工厂函数 import { createApp } from vue import App from ./App.vue//创建应用实例对象——app(类似于之前Vue2中的vm&#xff0c;但app比vm更“轻”) const …

D29【python 接口自动化学习】- python基础之输入输出与文件操作

day29 格式化输出 学习日期&#xff1a;20241006 学习目标&#xff1a;输入输出与文件操作&#xfe63;-41 格式化输出&#xff1a;如何将执行结果通过屏幕输出&#xff1f; 学习笔记&#xff1a; 三种常用的格式化输出方式 百分号方式 format函数方式 总结 1. 格式化输出…

win10服务器启动且未登录时自动启动程序

场景&#xff1a;公司服务器安装了几个程序&#xff0c;当服务器断电重启之后希望程序能自动打开&#xff0c;而不需要手动登录服务器打开。 因为软件是自己开发的所以安全方面这里没有考虑。 1.打开服务器管理器&#xff0c;点击工具&#xff0c;选择任务计划程序 2.在任务计…

文件处理不再难:带你轻松攻克C语言文件操作

嘿嘿,家人们,今天咱们来详细剖析C语言中的文件操作,好啦,废话不多讲,开干! 目录 1:为什么使用文件 2:文件的概念 2.1:程序文件 2.2:数据文件 2.3:文件名 3:二进制文件与文本文件 4:文件的打开与关闭 4.1:流与标准流 4.1.1:流 4.1.2:标准流 4.2:文件指针 4.3:文件的…

小米路由器ax1500+DDNS+公网IP+花生壳实现远程访问

有远程办公的需求&#xff0c;以及一些其他东西。 为什么写&#xff1f; ax1500路由器好像没搜到相关信息。以及其中有一点坑。 前置 公网ip Xiaomi路由器 AX1500 MiWiFi 稳定版 1.0.54 实现流程 花生壳申请壳域名https://console.hsk.oray.com/ 这里需要为域名实名认证 …

深度学习-----------------------注意力分数

目录 注意力分数注意力打分函数代码 掩蔽softmax操作拓展到高纬度Additive Attention(加性注意力)加性注意力代码演示一下AdditiveAttention类该部分总代码注意力权重 Scaled Dot-Product Attention&#xff08;缩放点积注意力&#xff09;缩放点积注意力代码演示一下DotProduc…

35-搜索插入位置

题目:35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09; 思想:常规的二分&#xff0c;很简单的理解是当大于所有数时要在right的边界1插入 class Solution { public:int searchInsert(vector<int>& nums, int target) {int left 0;int right nums.size()-…