从内核中最简单的驱动程序入手,描述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.