08-ioctl控制LED软件实现(库函数操作)

从内核中最简单的驱动程序入手,描述Linux驱动开发,主要文章目录如下(持续更新中):
01 - 第一个内核模块程序
02 - 注册字符设备驱动
03 - open & close 函数的应用
04 - read & write 函数的应用
05 - ioctl 的应用
06 - ioctl LED灯硬件分析
07 - ioctl 控制LED软件实现(寄存器操作)
08 - ioctl 控制LED软件实现(库函数操作)
09 - 注册字符设备的另一种方法(常用)
10 - 一个cdev实现对多个设备的支持
11 - 四个cdev控制四个LED设备
12 - 虚拟串口驱动
13 - I2C驱动
14 - SPI协议及驱动讲解
15 - SPI Linux驱动代码实现
16 - 非阻塞型I/O
17 - 阻塞型I/O
18 - I/O多路复用之 select
19 - I/O多路复用之 poll
20 - I/O多路复用之 epoll
21 - 异步通知


 前面讲到了LED的硬件分析,得到了LED控制的流程,详见 ioctl LED硬件分析
 上一节通过对寄存器的操作实现了LED灯的控制 07-ioctl控制LED软件实现(寄存器操作)
 这节将通过调用库函数对LED灯进行控制。

1. 预备知识

 在实现代码之前,首先了解一下本节会用到的控制gpio的几个函数,这几个函数都比较简单,通过函数名就能知道函数的作用,函数的应用在代码中进行演示。函数的定义都在 include/asm/gpio.h 中

原    型: static inline bool gpio_is_valid(int number);
功    能: 判断gpio是否有效
@param1: GPIO的ID,例如GPIO2_8,则GPIO的ID为2*32+8=72		  (一共4组,每组32个引脚)
@return: bool类型
原    型: int gpio_request(unsigned gpio, const char *label);
功    能: 申请GPIO资源(单个申请)
@param1: GPIO的ID,例如GPIO2_8,则GPIO的ID为2*32+8=72         (一共4组,每组32个引脚)
@param2: 为该管脚命一个名字
@return: 成功返回0,失败返回负数
释    放: extern void gpio_free(unsigned gpio);
原    型: int gpio_request_array(const struct gpio *array, size_t num);
功    能: 申请GPIO资源(多个申请)
@param1: struct gpio结构体数组,定义见本文行
@param2: 结构体中GPIO的个数
@return: 成功返回0,失败返回负数
释    放: extern void gpio_free_array(const struct gpio *array, size_t num);
原    型: static inline int gpio_direction_output(unsigned gpio, int value);
功    能: 配置引脚为输出功能,并初始化						//gpio_direction_input
@param1: GPIO的ID,例如GPIO2_8,则GPIO的ID为2*32+8=72         (一共4组,每组32个引脚)
@param2: 初始化的值
@return: 成功返回0,失败返回负数
原    型: #define gpio_set_value  __gpio_set_value
          static inline void __gpio_set_value(unsigned gpio, int value)
功    能: 设置引脚的值		// gpio_get_value
@param1: GPIO的ID,例如GPIO2_8,则GPIO的ID为2*32+8=72         (一共4组,每组32个引脚)
@param2: 要设置的值	// 0 or 1
@return: 无返回值

2. 代码实现

2.1 demo.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>

#include "chrdev.h"

#define MY_LED_NUM 4

int major;
const char *name = "demoname";

struct class *cls;
struct device *dev;

static unsigned int led_gpio_id[MY_LED_NUM] = {
	(1*32+4),	/* GPIO1_4 */
	(1*32+5),	/* GPIO1_5 */
	(1*32+6),
	(1*32+7),
};

/*	
	// gpio.h
	struct gpio {
		unsigned		gpio;
		unsigned long	flags;
		const char		*label;
	};
*/
static struct gpio led_struct[MY_LED_NUM] ={
	{(1*32+4), 0, "led1"},
	{(1*32+5), 0, "led2"},
	{(1*32+6), 0, "led3"},
	{(1*32+7), 0, "led4"},
};

int demo_open(struct inode *inode, struct file *filp)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	return 0;
}

int demo_release(struct inode *inode, struct file *filp)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	return 0;
}

ssize_t demo_read(struct file *filp, char __user *userbuf, size_t size, loff_t *offset)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);
	//copy_to_user(void __user * to, const void * from, size_t n);

	return 0;
}

ssize_t demo_write(struct file *filp, const char __user *userbuf, size_t size, loff_t *offset)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);
	//copy_from_user(void * to, const void __user * from, unsigned long n);

	return 0;
}

long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{	
	led_node_t led_t;
	int ret, led_num;

	ret = copy_from_user(&led_t, (led_node_t *)args, sizeof(led_t));	//成功返回0,失败返回有多少个Bytes未完成copy。
	if (ret)
	{
		printk("copy_from_user failed.\n");
		goto err0;
	}

	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	led_num = led_t.which;
	switch(cmd)
	{
	case LEDON:
		if (led_num == 1)
		{
			printk("led%d on.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 1);
		}
		else if (led_num == 2)
		{
			printk("led%d on.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 1);
		}
		else if (led_num == 3)
		{
			printk("led%d on.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 1);
		}
		else if (led_num == 4)
		{
			printk("led%d on.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 1);
		}
		break;
	case LEDOFF:
		if (led_num == 1)
		{
			printk("led%d off.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 0);
		}
		else if (led_num == 2)
		{
			printk("led%d off.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 0);
		}
		else if (led_num == 3)
		{
			printk("led%d off.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 0);
		}
		else if (led_num == 4)
		{
			printk("led%d off.\n", led_num);
			gpio_set_value(led_gpio_id[led_num-1], 0);
		}
		break;
	default:
		printk("cmd id error.\n");
	}

	return 0;

err0:
	return ret;
}

const struct file_operations fops = {
	.open = demo_open,
	.release = demo_release,
	.read = demo_read,
	.write = demo_write,
	.unlocked_ioctl = demo_ioctl,
};

static int led_init(void)
{
	int i, retval;

	for (i=0; i<MY_LED_NUM; i++)
	{
		retval = gpio_is_valid(led_gpio_id[i]);
		if (retval == 0)
		{
			printk("gpio is not valid.\n");
			goto err0;
		}
	}
	
	retval = gpio_request_array(led_struct, MY_LED_NUM);
	if (retval < 0)
	{
		printk("retvla = %d.\n", retval);
		printk("gpio_request_array failed.\n");
		goto err1;
	}

	for (i=0; i<MY_LED_NUM; i++)
	{
		gpio_direction_output(led_gpio_id[i], 0);	// 将引脚设置为输出,并初始化为低电平
	}

err1:
	return retval;
err0:
	return retval;
}

static int __init demo_init(void)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	major = register_chrdev(0, name, &fops);
	if (major <= 0)
	{
		printk("register_chrdev failed.\n");
		goto err0;
	}

	cls = class_create(THIS_MODULE, "char_class");
	if (cls == NULL)
	{
		printk("class_create failed.\n");
		goto err1;
	}

	dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "chrdev%d", 0);
	if (dev ==NULL)
	{
		printk("device_create failed.\n");
		goto err2;
	}

	led_init();	// 设置为输出模式
	
	return 0;

err2:
	class_destroy(cls);
err1:
	unregister_chrdev(major, name);
err0:
	return major;
}

static void __exit demo_exit(void)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	gpio_free_array(led_struct, MY_LED_NUM);

	device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, name);
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");

2.2 test.c

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

#include "chrdev.h"

const char *pathname = "/dev/chrdev0";
#define LED_NUM 4

int main(void)
{
	int fd, i;
	led_node_t led;

	fd = open(pathname, O_RDWR, 0666);
	if (fd <= 0)
	{
		printf("open failed.\n");
		return -1;
	}

	for (i=1; i<=LED_NUM; i++)
	{
		led.which  = i;
		
		led.status = 0; // 0表示灭
		ioctl(fd, LEDOFF, &led);
		sleep(1);

		led.status = 1;	// 1表示亮
		ioctl(fd, LEDON, &led);
		sleep(1);
	}

	close(fd);
	
	return 0;
}

2.3 chrdev.h

#ifndef _CHRDEV_H_
#define _CHRDEV_H_

typedef struct led_node
{
	int which;
	int status;
}led_node_t;

#define LED_MAGIC 'q'
#define LEDON  _IOW(LED_MAGIC, 0, struct led_node)
#define LEDOFF _IOW(LED_MAGIC, 1, struct led_node)

#endif /* chrdev.h */

2.4 Makefile

KERNELDIR ?= /home/linux/ti-processor-sdk-linux-am335x-evm-05.02.00.10/board-support/linux-4.14.79/
PWD := $(shell pwd)

all:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) modules
	arm-linux-gnueabihf-gcc test.c -o app
install:
	sudo cp *.ko  app /tftpboot
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) clean
	rm app
clean:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) clean
	rm app

obj-m += demo.o

2.5 测试结果

root@am335x-evm:~# insmod demo.ko 
[12820.842929] demo_init -- 184.
root@am335x-evm:~# ./app
[12830.440113] demo_open -- 42.
[12830.444427] demo_ioctl -- 82.
[12830.447446] led1 off.
[12831.451161] demo_ioctl -- 82.
[12831.454271] led1 on.
[12832.456686] demo_ioctl -- 82.
[12832.459732] led2 off.
[12833.468087] demo_ioctl -- 82.
[12833.471131] led2 on.
[12834.480328] demo_ioctl -- 82.
[12834.484455] led3 off.
[12835.487004] demo_ioctl -- 82.
[12835.490053] led3 on.
[12836.499799] demo_ioctl -- 82.
[12836.503864] led4 off.
[12837.506374] demo_ioctl -- 82.
[12837.509433] led4 on.
[12838.511984] demo_release -- 49.
root@am335x-evm:~# rmmod demo.ko 
[17286.926789] demo_exit -- 221.
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值