【Linux驱动入门实验班.............】

02_搭建环境_NAT网卡

在这里插入图片描述
在这里插入图片描述

03_搭建环境_USB网卡

在这里插入图片描述
在这里插入图片描述

09_字符设备驱动程序框架

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

driver.c

#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>


static int major;

static int hello_open (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return size;
}    

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return size;
}

static int hello_release (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* 1. create file_operations */
static const struct file_operations hello_drv = {
    .owner      = THIS_MODULE,
	.read		= hello_read,
	.write		= hello_write,
	.open		= hello_open,
    .release    = hello_release,
};

/* 2. register_chrdev */

/* 3. entry function */
static int hello_init(void)
{
   major = register_chrdev(0, "100ask_hello", &hello_drv);
   return 0;
}

/* 4. exit function */
static void hello_exit(void)
{
    unregister_chrdev(major, "100ask_hello");
}


module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

app

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

/* 写: ./hello_test /dev/xxx 100ask
 * 读: ./hello_test /dev/xxx
 */
int main(int argc, char **argv)
{
    int fd;
    int len;
    char buf[100];

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

    // open
    fd = open(argv[1], O_RDWR);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        return -1;
    }

    if (argc == 3)
    {
        // write
        len = write(fd, argv[2], strlen(argv[2])+1);
        printf("write ret = %d\n", len);
    }
    else
    {
        // read
        len = read(fd, buf, 100);
        buf[99] = '\0';
        printf("read str : %s\n", buf);
    }
    
    // close
    close(fd);
    return 0;
}

在这里插入图片描述

14_APP_驱动_硬件传输数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

用户与内核传数据和创建设备文件驱动程序

driver.c

#include "asm/cacheflush.h"
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>

#include <linux/uaccess.h>


static struct class *hello_class;		//定义一个类型,用于创建设备
static int major;						//主设备号
static unsigned char hello_buf[100];	//内核数据缓存

static int hello_open (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;

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

    copy_to_user(buf, hello_buf, len);		//将内核数据发送给用户

    return len;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    copy_from_user(hello_buf, buf, len);

    return len;
}

static int hello_release(struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* 1. create file_operations */
static const struct file_operations hello_drv = {
    .owner      = THIS_MODULE,
	.read		= hello_read,
	.write		= hello_write,
	.open		= hello_open,
    .release    = hello_release,
};


/* 2. register_chrdev */

/* 3. entry function */
static int hello_init(void)
{	//申请设备号,只能进行注测主设备号0-255个设备,次设备号全部被占用
    major = register_chrdev(0, "100ask_hello", &hello_drv);  //0代表自动寻找设备号,

	hello_class = class_create(THIS_MODULE, "hello_class");		//创建一个类型
	if (IS_ERR(hello_class)) {
		printk("failed to allocate class\n");
		return PTR_ERR(hello_class);
	}
	//创建一个名字为hello的设备
    device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello");  /* /dev/hello */

   return 0;
}


/* 4. exit function */
static void hello_exit(void)
{
    device_destroy(hello_class, MKDEV(major, 0));

    class_destroy(hello_class);

    unregister_chrdev(major, "100ask_hello");
}


module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

18_APP使用驱动的4种方式

在这里插入图片描述

在这里插入图片描述

19_APP和驱动交互的内部流程

非阻塞

不断地查询,访问

阻塞

在这里插入图片描述

poll(定时查询)

在这里插入图片描述
在这里插入图片描述

异步通知

当进入中断时,执行app中的fun()函数,再继续执行while(1)里面的程序

在这里插入图片描述

20_字符设备的另一种注册方法cdev

在这里插入图片描述
在这里插入图片描述

drive.c

#include "asm-generic/errno-base.h"
#include "asm/cacheflush.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>


static struct class *hello_class;	
static struct cdev hello_cdev;
static dev_t dev;

static unsigned char hello_buf[100];

static int hello_open (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;

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

    copy_to_user(buf, hello_buf, len);

    return len;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    copy_from_user(hello_buf, buf, len);

    return len;
}

static int hello_release (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* 1. create file_operations */
static const struct file_operations hello_drv = {
    .owner      = THIS_MODULE,
	.read		= hello_read,
	.write		= hello_write,
	.open		= hello_open,
    .release    = hello_release,
};


/* 2. register_chrdev */

/* 3. entry function */
static int hello_init(void)
{
    int ret;

    // register_chrdev
	//参数一:分配设备号成功后用来存放分配到的设备号,分配结束后要把主设备号提取出来(major = MAJOR(devno);)因为主设备号会变所以要将变得重新赋值
	//参数二:起始的次设备号,一般为0
	//参数三:count:申请的设备数量,从起始设备号累加。如果在创建一个设备,主设备号跟前一个一样,次设备号为1的设备,依然可以访问,就是同一个设备。
	//参数四:/proc/devices文件中与该设备对应的名字,方便用户层查询主次设备号
	ret = alloc_chrdev_region(&dev, 0, 2, "hello");		//自动注册设备
	if (ret < 0) 
	{
		printk(KERN_ERR "alloc_chrdev_region() failed for hello\n");
		return -EINVAL;
	}

    cdev_init(&hello_cdev, &hello_drv);	//将hello_drv与hello_cdev链接起来	

    ret = cdev_add(&hello_cdev, dev, 2);//将设备号添加到设备结构体
	if (ret)
    {
		printk(KERN_ERR "cdev_add() failed for hello\n");
		return -EINVAL;
    }
		
	hello_class = class_create(THIS_MODULE, "hello_class");	
	if (IS_ERR(hello_class)) {
		printk("failed to allocate class\n");
		return PTR_ERR(hello_class);
	}
	//创建一名字为hello的设备 
    device_create(hello_class, NULL, dev, NULL, "hello");  /* /dev/hello */

   return 0;
}

/* 4. exit function */
static void hello_exit(void)
{
    device_destroy(hello_class, dev);

    class_destroy(hello_class);

    //unregister_chrdev(major, "100ask_hello");
    cdev_del(&hello_cdev);
    unregister_chrdev_region(dev, 2);
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

21_GPIO子系统简述

21.1.1 引脚编号

在硬件上如何确定GPIO引脚?它属于哪组GPIO?它是这组GPIO里的哪个引脚?需要2个参数。

但是在Linux软件上,可以使用引脚编号来表示。

在开发板上执行如下命令查看已经在使用的GPIO状态:

# cat /sys/kernel/debug/gpio
gpiochip0: GPIOs 0-15, parent: platform/soc:pin-controller@50002000, GPIOA:
 gpio-10  (                    |heartbeat           ) out lo
 gpio-14  (                    |shutdown            ) out hi

gpiochip1: GPIOs 16-31, parent: platform/soc:pin-controller@50002000, GPIOB:
 gpio-26  (                    |reset               ) out hi ACTIVE LOW

gpiochip2: GPIOs 32-47, parent: platform/soc:pin-controller@50002000, GPIOC:

gpiochip3: GPIOs 48-63, parent: platform/soc:pin-controller@50002000, GPIOD:

可以看到:在Linux系统中可以使用编号来访问某个GPIO。

怎么确定GPIO引脚的编号?方法如下:

① 先在开发板的/sys/class/gpio目录下,找到各个gpiochipXXX目录:

在这里插入图片描述

② 然后进入某个gpiochipXXX目录,查看文件label的内容,就可以知道起始号码XXX对于哪组GPIO

21.1.2 基于sysfs操作引脚

以100ask_imx6ull为例,它有一个按键,原理图如下:

在这里插入图片描述

那么GPIO4_14的号码是96+14=110,可以如下操作读取按键值:

[root@100ask:~]# echo 110 > /sys/class/gpio/export              // gpio_request
[root@100ask:~]# echo in > /sys/class/gpio/gpio110/direction    // gpio_direction_input
[root@100ask:~]# cat /sys/class/gpio/gpio110/value              // gpio_get_value
[root@100ask:~]# echo 110 > /sys/class/gpio/unexport            // gpio_free

注意:如果驱动程序已经使用了该引脚,那么将会export失败,会提示下面的错误:

在这里插入图片描述

对于输出引脚,假设引脚号为N,可以用下面的方法设置它的值为1:

[root@100ask:~]# echo N > /sys/class/gpio/export
[root@100ask:~]# echo out > /sys/class/gpio/gpioN/direction
[root@100ask:~]# echo 1 > /sys/class/gpio/gpioN/value
[root@100ask:~]# echo N > /sys/class/gpio/unexport

21.1.3 GPIO子系统的函数

GPIO子系统函数有新、老两套:

descriptor-basedlegacy
获得GPIO
gpiod_getgpio_request
gpiod_get_index
gpiod_get_arraygpio_request_array
devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array
设置方向
gpiod_direction_inputgpio_direction_input
gpiod_direction_outputgpio_direction_output
读值、写值
gpiod_get_valuegpio_get_value
gpiod_set_valuegpio_set_value
释放GPIO
gpio_freegpio_free
gpiod_putgpio_free_array
gpiod_put_array
devm_gpiod_put
devm_gpiod_put_array

22_中断函数简述

22.2.1 使用中断的流程

在这里插入图片描述

在这里插入图片描述

在驱动程序里使用中断的流程如下:

  • 确定中断号

  • 注册中断处理函数,函数原型如下:

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
    	    const char *name, void *dev);
    
  • 在中断处理函数里

    • 分辨中断
    • 处理中断
    • 清除中断

22.2.2 函数细节

request_irq函数的第1个参数是中断号,可以根据GPIO函数获得中断号:

int gpio_to_irq(unsigned int gpio);
int gpiod_to_irq(const struct gpio_desc *desc);

request_irq函数的第2个参数是函数指针:

enum irqreturn {
	IRQ_NONE		= (0 << 0),
	IRQ_HANDLED		= (1 << 0),
	IRQ_WAKE_THREAD		= (1 << 1),
};
typedef enum irqreturn irqreturn_t;
typedef irqreturn_t (*irq_handler_t)(int irq, void *dev);

request_irq函数的第3个参数有如下取值:

#define IRQF_TRIGGER_NONE	0x00000000
#define IRQF_TRIGGER_RISING	0x00000001
#define IRQF_TRIGGER_FALLING	0x00000002
#define IRQF_TRIGGER_HIGH	0x00000004
#define IRQF_TRIGGER_LOW	0x00000008

#define IRQF_SHARED		0x00000080

request_irq函数的第4个参数是中断的名字,可以在执行cat /proc/interrupts的结果里查看。

request_irq函数的第5个参数是给中断处理函数使用的。

23_驱动模板1(按键)代码解读

在这里插入图片描述

23.3.1 file_operations

分配/设置/注册file_operations结构体:

  • 设置

在这里插入图片描述

  • 注册

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

环形缓冲区

![在这里插入图片描述](https://img-blog.csdnimg.cn/dae11dc1b537427bb2c9b88b0d26cdd9.png
在这里插入图片描述

在这里插入图片描述

阻塞

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

poll

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

异步通知

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

driver.c

#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

struct gpio_desc{							//定义一个中断结构体
	int gpio;
	int irq;
    char *name;
    int key;
	struct timer_list key_timer;
} ;

static struct gpio_desc gpios[2] = {   		//中断结构体赋初值
    {131, 0, "gpio_100ask_1", 1,},
    {132, 0, "gpio_100ask_2", 2,},  
};

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

struct fasync_struct *button_fasync;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
	return (r == w);
}

static int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

// static void key_timer_expire(struct timer_list *t)
static void key_timer_expire(unsigned long data)
{
	/* data ==> gpio */
	// struct gpio_desc *gpio_desc = from_timer(gpio_desc, t, key_timer);
	struct gpio_desc *gpio_desc = (struct gpio_desc *)data;
	int val;
	int key;

	val = gpio_get_value(gpio_desc->gpio);		//读取引脚  0或1

	//printk("key_timer_expire key %d %d\n", gpio_desc->gpio, val);
	key = (gpio_desc->key) | (val<<8);  //key值用来放在环形数组中,用key第八位看按键是否按下
	put_key(key);//放入环形数组
	wake_up_interruptible(&gpio_wait);
	kill_fasync(&button_fasync, SIGIO, POLL_IN);
}

/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	int err;
	int key;

	if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
		return -EAGAIN;
	
	wait_event_interruptible(gpio_wait,!is_key_buf_empty());
	key = get_key();
	err = copy_to_user(buf, &key, 4);
	
	return 4;
}

static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned char ker_buf[2];
    int err;

    if (size != 2)
        return -EINVAL;

    err = copy_from_user(ker_buf, buf, size);
    
    if (ker_buf[0] >= sizeof(gpios)/sizeof(gpios[0]))
        return -EINVAL;

    gpio_set_value(gpios[ker_buf[0]].gpio, ker_buf[1]);
    return 2;    
}

static unsigned int gpio_drv_poll(struct file *fp, poll_table * wait)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	poll_wait(fp, &gpio_wait, wait);
	return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_drv_fasync(int fd, struct file *file, int on)
{
	if (fasync_helper(fd, file, on, &button_fasync) >= 0)
		return 0;
	else
		return -EIO;
}

/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
	.write   = gpio_drv_write,
	.poll    = gpio_drv_poll,
	.fasync  = gpio_drv_fasync,
};

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_desc *gpio_desc = dev_id;
	printk("gpio_key_isr key %d irq happened\n", gpio_desc->gpio);
	//jiffies是全局变量  
	mod_timer(&gpio_desc->key_timer, jiffies + HZ/5);		//消抖,每次抖动时长小于(HZ/5)*Tms,都会推迟进入中断服务程序
	return IRQ_HANDLED;
}

/* 在入口函数 */
static int __init gpio_drv_init(void)
{
    int err;
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	for (i = 0; i < count; i++)
	{		
		gpios[i].irq  = gpio_to_irq(gpios[i].gpio);		//调用函数获得中断号
		//定时器初始化函数 参数一:定时器时间 参数二:中断服务程序 参数三:传入中断服务程序的参数
		setup_timer(&gpios[i].key_timer, key_timer_expire, (unsigned long)&gpios[i]);
	 	//timer_setup(&gpios[i].key_timer, key_timer_expire, 0);
		gpios[i].key_timer.expires = ~0;
		add_timer(&gpios[i].key_timer);
		err = request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpios[i]);  //注册中断
	}

	/* 注册file_operations 	*/
	major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "100ask_gpio_key");
		return PTR_ERR(gpio_class);
	}

	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_gpio"); /* /dev/100ask_gpio */
	
	return err;
}

/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 */
static void __exit gpio_drv_exit(void)
{
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "100ask_gpio_key");

	for (i = 0; i < count; i++)
	{
		free_irq(gpios[i].irq, &gpios[i]);
		del_timer(&gpios[i].key_timer);
	}
}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");

app.c

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

static int fd;

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{
	int val;
	struct pollfd fds[1];
	int timeout_ms = 5000;
	int ret;
	int	flags;

	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	for (i = 0; i < 10; i++) 
	{
		if (read(fd, &val, 4) == 4)
			printf("get button: 0x%x\n", val);
		else
			printf("get button: -1\n");
	}

	flags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);

	while(1)
	{
		if (read(fd, &val, 4) == 4)
			printf("get button: 0x%x\n", val);
		else
			printf("while get button: -1\n");
	}
	close(fd);
	return 0;
}

34_模板1实战_LED驱动编写

driver.c

#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

struct gpio_desc{
	int gpio;
	int irq;
    char *name;
    int key;
	struct timer_list key_timer;
} ;

static struct gpio_desc gpios[2] = {
    {131, 0, "led0", },
    //{132, 0, "led1", },
};

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	char tmp_buf[2];
	int err;
    int count = sizeof(gpios)/sizeof(gpios[0]);		//表示有多少可操作引脚

	if (size != 2)
		return -EINVAL;

	err = copy_from_user(tmp_buf, buf, 1);

	if (tmp_buf[0] >= count)	//大于可操作数量报错
		return -EINVAL;

	tmp_buf[1] = gpio_get_value(gpios[tmp_buf[0]].gpio);

	err = copy_to_user(buf, tmp_buf, 2);
	
	return 2;
}

static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned char ker_buf[2];			//内核数组
    int err;

    if (size != 2)					//传入的参数不是2直接退出
        return -EINVAL;

    err = copy_from_user(ker_buf, buf, size);	//将用户数据拷贝到内核
    
    if (ker_buf[0] >= sizeof(gpios)/sizeof(gpios[0]))		//大于课操作引脚报错
        return -EINVAL;

    gpio_set_value(gpios[ker_buf[0]].gpio, ker_buf[1]);
    return 2;    
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
	.write   = gpio_drv_write,
};


/* 在入口函数 */
static int __init gpio_drv_init(void)
{
    int err;
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	for (i = 0; i < count; i++)
	{		
		/* set pin as output */
		err = gpio_request(gpios[i].gpio, gpios[i].name);//其实就是让内核检查一下该GPIO引脚是否被其它设备占用,如果没有占用则返回0并用label做一下标记,表示被本设备占用,否则返回负数
		if (err < 0) {
			printk("can not request gpio %s %d\n", gpios[i].name, gpios[i].gpio);
			return -ENODEV;
		}
		
		 (gpios[i].gpio, 1);		//设置gpio输出为1
	}

	/* 注册file_operations 	*/
	major = register_chrdev(0, "100ask_led", &gpio_key_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "100ask_led_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "100ask_led_class");
		return PTR_ERR(gpio_class);
	}

	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_led"); /* /dev/100ask_gpio */
	
	return err;
}

/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 */
static void __exit gpio_drv_exit(void)
{
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "100ask_led");

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


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");



app.c


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

static int fd;


//int led_on(int which);
//int led_off(int which);
//int led_status(int which);

/*
 * ./led_test <0|1|2|..>  on 
 * ./led_test <0|1|2|..>  off
 * ./led_test <0|1|2|..>
 */
int main(int argc, char **argv)
{
	int ret;
	char buf[2];

	int i;
	
	/* 1. 判断参数 */
	if (argc < 2) 
	{
		printf("Usage: %s <0|1|2|...> [on | off]\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
	fd = open("/dev/100ask_led", O_RDWR);
	if (fd == -1)
	{
		printf("can not open file /dev/100ask_led\n");
		return -1;
	}

	if (argc == 3)
	{
		/* write */
		buf[0] = strtol(argv[1], NULL, 0);  //将字符串内容转化成整数

		if (strcmp(argv[2], "on") == 0)  //strcmp字符串比较,如果相等返回0
			buf[1] = 0;
		else
			buf[1] = 1;
		
		ret = write(fd, buf, 2);
	}
	else
	{
		buf[0] = strtol(argv[1], NULL, 0);
		ret = read(fd, buf, 2);
		if (ret == 2)
		{
			printf("led %d status is %s\n", buf[0], buf[1] == 0 ? "on" : "off");
		}
	}
	
	close(fd);
	
	return 0;
}

40_模板1实战_SR501红外模块驱动编程

cat /sys/kernel/debug/gpio
cat /proc/interrupts

driver.c

#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

struct gpio_desc{
	int gpio;
	int irq;
    char *name;
    int key;
	struct timer_list key_timer;
} ;

static struct gpio_desc gpios[2] = {
    {115, 0, "sr501", },
};

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

struct fasync_struct *button_fasync;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
	return (r == w);
}

static int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);  //初始化gpio休眠队列


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	int err;
	int key;

	if (is_key_buf_empty() && (file->f_flags & O_NONBLOCK))
		return -EAGAIN;
	
	wait_event_interruptible(gpio_wait, !is_key_buf_empty());
	key = get_key();
	err = copy_to_user(buf, &key, 4);
	
	return 4;
}


static unsigned int gpio_drv_poll(struct file *fp, poll_table * wait)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	poll_wait(fp, &gpio_wait, wait);
	return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_drv_fasync(int fd, struct file *file, int on)
{
	if (fasync_helper(fd, file, on, &button_fasync) >= 0)
		return 0;
	else
		return -EIO;
}


/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
	.poll    = gpio_drv_poll,
	.fasync  = gpio_drv_fasync,
};


static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_desc *gpio_desc = dev_id;
	int val;
	int key;

	printk("gpio_key_isr key %d irq happened\n", gpio_desc->gpio);

	val = gpio_get_value(gpio_desc->gpio);

	//printk("key_timer_expire key %d %d\n", gpio_desc->gpio, val);
	key = (gpio_desc->key) | (val<<8);
	put_key(key);
	wake_up_interruptible(&gpio_wait);
	kill_fasync(&button_fasync, SIGIO, POLL_IN);

	return IRQ_HANDLED;
}


/* 在入口函数 */
static int __init gpio_drv_init(void)
{
    int err;
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	for (i = 0; i < count; i++)
	{		
		gpios[i].irq  = gpio_to_irq(gpios[i].gpio);
		err = request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[i].name, &gpios[i]);  //使能按键中断,中断号为gpios[i].irq,中断服务函数gpio_key_isr,触发方式为上升沿下降沿触发IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
	}

	/* 注册file_operations 	*/
	major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "100ask_gpio_key");
		return PTR_ERR(gpio_class);
	}

	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr501"); /* /dev/sr501 */
	
	return err;
}

/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 */
static void __exit gpio_drv_exit(void)
{
    int i;
    int count = sizeof(gpios)/sizeof(gpios[0]);
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "100ask_gpio_key");

	for (i = 0; i < count; i++)
	{
		free_irq(gpios[i].irq, &gpios[i]);
	}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");

app.c


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

static int fd;

/*
 * ./button_test /dev/sr501
 *
 */
int main(int argc, char **argv)
{
	int val;
	struct pollfd fds[1];
	int timeout_ms = 5000;
	int ret;
	int	flags;
	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while (1) 
	{
		if (read(fd, &val, 4) == 4)
			printf("get button: 0x%x\n", val);
		else
			printf("get button: -1\n");
	}

	close(fd);
	return 0;
}

driver_2.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>


struct gpio_desc *SR501_gpio;
static int irq;

/* 主设备号     */
static int major = 0;
static struct class *gpio_class;

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
	return (r == w);
}

static int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	int val;
	int len=(size<4)?size:4;
    
	wait_event_interruptible(gpio_wait, !is_key_buf_empty());//等待唤醒队列,并查看是否有数据
	val=get_key();//获取环形缓冲区的数据
	
	err=copy_to_user(buf, &val, len); //将数据传给用户
	return 4;
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_sr501_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
};

/*中断服务函数*/
irqreturn_t sr501_irq(int irq, void *dev_id)
{
	int val;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	val=gpiod_get_value(SR501_gpio);//获取引脚电平的数据
   	put_key(val);//将数据存储到环形缓冲区当中	
    wake_up_interruptible(&gpio_wait);//话唤醒队列  
    return IRQ_HANDLED;
}



/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{
	int err = 0;
    int count=0;

	struct device_node *np = pdev->dev.of_node;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	if (np)
	{
		/* pdev来自设备树 : 示例
        sr501 {
            compatible = "sch,sr501";
            gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;
        };
		*/
		count = of_gpio_count(np);
		if (!count)
			return -EINVAL;
		
		SR501_gpio= gpiod_get(&pdev->dev, NULL, 0);//获取引脚
		gpiod_direction_input(SR501_gpio);//设置输入方向
		irq=gpiod_to_irq(SR501_gpio);//获取中断号
		//注册中断函数
        err=request_irq(irq, sr501_irq, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr501_irq", &SR501_gpio);
	}
	
	
	/* 注册file_operations 	*/
	major = register_chrdev(0, "sch", &gpio_sr501_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "sr501_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "sch");
		return PTR_ERR(gpio_class);
	}
	
    /* 自动创建设备节点          /dev/sr501 */
	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr501"); 

	return 0;
}

static int  gpio_drv_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "sch");

	free_irq(irq, &SR501_gpio);
	gpiod_put(SR501_gpio);

    return 0;
}


static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "sch,sr501", },
        { /* sentinel */ }
};


static struct platform_driver gpio_platform_driver = {
	.driver = {
		.name = "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,
	},
	.probe  = gpio_drv_probe,
	.remove = gpio_drv_remove,
};


static int __init gpio_drv_init(void)
{
	/* 注册platform_driver */
	return platform_driver_register(&gpio_platform_driver);
}

static void __exit gpio_drv_exit(void)
{
	/* 反注册platform_driver */
	platform_driver_unregister(&gpio_platform_driver);
}

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");



app_2.c

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

static int fd;

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{
	int val;
	int ret;
	int	flags;
	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR );
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while (1)
	{
		if (read(fd, &val, 4) == 4)
			printf("get button: 0x%x\n", val);
		else
			printf("while get button: -1\n");//不会执行此语句,由于read为阻塞方式
	}
	
	close(fd);
	
	return 0;
}

driver_3.c /内核线程/

#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/kthread.h>



struct gpio_desc *SR501_gpio;
static struct task_struct *sr501_kthread;


/* 主设备号     */
static int major = 0;
static struct class *gpio_class;

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
	return (r == w);
}

static int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	int val;
	int len=(size<4)?size:4;
    
	wait_event_interruptible(gpio_wait, !is_key_buf_empty());//等待唤醒队列,并查看是否有数据
	val=get_key();//获取环形缓冲区的数据
	
	err=copy_to_user(buf, &val, len); //将数据传给用户
	return 4;
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_sr501_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
};

static int sr501_detect(void *arg)
{
	int val=-1;
	int pre=-1;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    while(!kthread_should_stop()) 
   {
	   val=gpiod_get_value(SR501_gpio);//获取引脚电平的数据
		if(val!=pre)
		{
		  pre=val;
		  put_key(val);//将数据存储到环形缓冲区当中	
		  wake_up_interruptible(&gpio_wait);//话唤醒队列  
		}
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(HZ/5);
	}
	return 0;
}


/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{
	//int err = 0;
    int count=0;

	struct device_node *np = pdev->dev.of_node;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	if (np)
	{
		/* pdev来自设备树 : 示例
        sr501 {
            compatible = "sch,sr501";
            gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;
        };
		*/
		count = of_gpio_count(np);
		if (!count)
			return -EINVAL;
		
		SR501_gpio= gpiod_get(&pdev->dev, NULL, 0);//获取引脚
		gpiod_direction_input(SR501_gpio);//设置输入方向
	
	
	/* 注册file_operations 	*/
	major = register_chrdev(0, "sch", &gpio_sr501_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "sr501_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "sch");
		return PTR_ERR(gpio_class);
	}
	
    /* 自动创建设备节点          /dev/sr501 */
	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr501"); 

    //创建内核线程检测GPIO引脚
    sr501_kthread=kthread_run(sr501_detect,NULL,"sr501d");
    
	return 0;
}

static int  gpio_drv_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "sch");
	gpiod_put(SR501_gpio);
    kthread_stop(sr501_kthread);

    return 0;
}


static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "sch,sr501", },
        { /* sentinel */ }
};


static struct platform_driver gpio_platform_driver = {
	.driver = {
		.name = "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,
	},
	.probe  = gpio_drv_probe,
	.remove = gpio_drv_remove,
};


static int __init gpio_drv_init(void)
{
	/* 注册platform_driver */
	return platform_driver_register(&gpio_platform_driver);
}

static void __exit gpio_drv_exit(void)
{
	/* 反注册platform_driver */
	platform_driver_unregister(&gpio_platform_driver);
}

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");


41_SR04超声波模块驱动编程

cd /sys/firmware/devicetree/base
cd /sys/bus/platform/devices

/***********  dts ***************/
sr04{
   compatible ="sch,sr04";
   trig-gpios=<&gpio4 19    GPIO_ACTIVE_HIGH>;
   echo-gpios=<&gpio4  20  GPIO_ACTIVE_HIGH>;
};

driver.c(中断方式)

#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>


struct gpio_desc *SR04_trig;
struct gpio_desc *SR04_echo;

static int irq;

/* 主设备号     */
static int major = 0;
static struct class *gpio_class;

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
	return (r == w);
}

static int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

static int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	int us = 0;
    int timeout;//判断超时时间

	/* 发送10us高电平    , 测量距离 2cm-450cm */
	gpiod_set_value(SR04_trig, 1);
	udelay(15);
	gpiod_set_value(SR04_trig, 0);

	timeout=wait_event_interruptible_timeout(gpio_wait, !is_key_buf_empty(),HZ);

    if(timeout)	
    {
	 us = get_key();
	 //将数据传给用户
	 err=copy_to_user(buf, &us, 4);
	 return 4;
    }
	else
	  return -EAGAIN;
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_SR04_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
};

/*中断服务函数*/
irqreturn_t SR04_irq(int irq, void *dev_id)
{
	int val;
	static int us=0;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	val=gpiod_get_value(SR04_echo);//获取引脚电平的数据

	/*上升沿*/
	if(val==1)
	{ 
       us=ktime_get_ns();//获取当前时间
	}
	else
	{
      us=ktime_get_ns()-us;
	  put_key(us);//将数据存储到环形缓冲区当中	
	  wake_up_interruptible(&gpio_wait);//话唤醒队列
	}
   	
   
    return IRQ_HANDLED;
}



/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{
	int err = 0;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	//获取引脚
	SR04_trig= gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
	SR04_echo= gpiod_get(&pdev->dev, "echo", GPIOD_IN);

	//获取中断号
	irq=gpiod_to_irq(SR04_echo);

	//注册中断函数
    err=request_irq(irq, SR04_irq, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "SR04_irq", NULL);
	
	
	/* 注册file_operations 	*/
	major = register_chrdev(0, "sch", &gpio_SR04_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "SR04_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "sch");
		return PTR_ERR(gpio_class);
	}
	
    /* 自动创建设备节点          /dev/SR04 */
	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "SR04"); 
    
	return 0;
}

static int  gpio_drv_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "sch");
	
	free_irq(irq, &SR04_echo);
	gpiod_put(SR04_trig);
    gpiod_put(SR04_echo);
    
    return 0;
}


static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "sch,sr04", },
        { /* sentinel */ }
};


static struct platform_driver gpio_platform_driver = {
	.driver = {
		.name = "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,
	},
	.probe  = gpio_drv_probe,
	.remove = gpio_drv_remove,
};


static int __init gpio_drv_init(void)
{
	/* 注册platform_driver */
	return platform_driver_register(&gpio_platform_driver);
}

static void __exit gpio_drv_exit(void)
{
	/* 反注册platform_driver */
	platform_driver_unregister(&gpio_platform_driver);
}

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");

app.c(中断方式)

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

static int fd;

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{
	int us;
	int ret;
	int	flags;
	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR );
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while (1)
	{
		sleep(1);
		if (read(fd, &us, 4) == 4)
		{
			printf("get distance: %d mm\n ", us*340/2/1000000);
		}
		else
		{
			printf("while get distance: -1\n");
		}
	}
	
	close(fd);
	
	return 0;
}

driver.c (查询方式)

#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>


struct gpio_desc *SR04_trig;
struct gpio_desc *SR04_echo;

static int irq;

/* 主设备号     */
static int major = 0;
static struct class *gpio_class;


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	int us = 0;
	unsigned long flags;
	unsigned long timeout_us = 1000000;
	
	local_irq_save(flags);	  // 关中断

	/* 发送10us高电平    , 测量距离 2cm-450cm */
	gpiod_set_value(SR04_trig, 1);
	udelay(15);
	gpiod_set_value(SR04_trig, 0);
	
	
	//使用udelay来延时判断引脚电平
	while (!gpiod_get_value(SR04_echo) && timeout_us)
	{
		udelay(1); // 等待高电平
		timeout_us--;
	}
	if (!timeout_us)
	{
		local_irq_restore(flags); // 恢复中断
		return -EAGAIN;
	}

	timeout_us = 1000000;
	while (gpiod_get_value(SR04_echo) && timeout_us)
	{ 
		udelay(1); 
		us++; // 累加时间
		timeout_us--;
	}  

	if (!timeout_us)
	{
		local_irq_restore(flags); // 恢复中断
		return -EAGAIN;
	}
		
	local_irq_restore(flags); // 恢复中断
	err=copy_to_user(buf, &us, 4);
	return 4;
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_SR04_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
};

/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{
	int err = 0;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	//获取引脚
	SR04_trig= gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
	SR04_echo= gpiod_get(&pdev->dev, "echo", GPIOD_IN);

	/* 注册file_operations 	*/
	major = register_chrdev(0, "sch", &gpio_SR04_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "SR04_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "sch");
		return PTR_ERR(gpio_class);
	}
	
    /* 自动创建设备节点          /dev/SR04 */
	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "SR04"); 
    
	return 0;
}

static int  gpio_drv_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "sch");
	
	gpiod_put(SR04_trig);
    gpiod_put(SR04_echo);
	

    return 0;
}


static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "sch,sr04", },
        { /* sentinel */ }
};


static struct platform_driver gpio_platform_driver = {
	.driver = {
		.name = "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,
	},
	.probe  = gpio_drv_probe,
	.remove = gpio_drv_remove,
};


static int __init gpio_drv_init(void)
{
	/* 注册platform_driver */
	return platform_driver_register(&gpio_platform_driver);
}

static void __exit gpio_drv_exit(void)
{
	/* 反注册platform_driver */
	platform_driver_unregister(&gpio_platform_driver);
}

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");

app.c (查询方式)

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

static int fd;

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{
	int us;
	int ret;
	int	flags;
	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR );
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while (1)
	{
		sleep(1);
		if (read(fd, &us, 4) == 4)
		{
			printf("get distance: %d mm\n", us*340/2/1000);  /* mm */
		}
		else
		{
			printf("while get distance: -1\n");
		}
	}
	
	close(fd);
	
	return 0;
}

42_DHT11模块驱动编程

dht11{
   compatible ="sch,dht11";
   gpios=<&gpio4 19    GPIO_ACTIVE_HIGH>;
};

driver.c(查寻方式)

#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>


struct gpio_desc *dht11_pin;
/* 主设备号     */
static int major = 0;
static struct class *gpio_class;

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

static void dht11_reset(void)
{
   gpiod_direction_output(dht11_pin,GPIOD_OUT_HIGH);
}

static void dht11_start(void)
{
   gpiod_direction_output(dht11_pin,GPIOD_OUT_LOW);
   mdelay(30);
   gpiod_set_value(dht11_pin,0);
   mdelay(20);
   gpiod_set_value(dht11_pin,1);
   udelay(40);
   gpiod_direction_input(dht11_pin);
}

static int dht11_wait_for_ready(void)
{
	/*等待低电平*/
    int timeout=100;
	while(gpiod_get_value(dht11_pin)&&--timeout)
	{
        udelay(1);
	}
	if(timeout==0)
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	    return -1;
	}

	/*等待高电平*/
	timeout=100;	
	while(!gpiod_get_value(dht11_pin)&&--timeout)
	{
        udelay(1);
	}
	if(timeout==0)
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	    return -1;
	}

	/*等待低电平*/
    timeout=100;	
	while(gpiod_get_value(dht11_pin)&&--timeout)
	{
        udelay(1);
	}
	if(timeout==0)
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	    return -1;
	}	

	return 0;
}

static int dht11_read_byte(unsigned char *buf)
{
    int i;
	unsigned char data=0;
	int timeout=200;
   	for (i = 0; i < 8; i++)
	{
	  /*等待高电平*/
	  timeout=100;	  
	  while(!gpiod_get_value(dht11_pin)&&--timeout)
	  {
		  udelay(1);
	  }
	  if(timeout==0)
	  {
	  	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		  return -1;
	  }
	  udelay(40);
	  /*判断高低电平*/
      if(gpiod_get_value(dht11_pin))
     {
	    data|=0x01<<(7-i);
		/*等待低电平*/
	    timeout=100;
		while(gpiod_get_value(dht11_pin)&&--timeout)
		{
			udelay(1);
		}
		if(timeout==0)
		{
			printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		    return -1;
		 }
	 }
	  else
	  	data|=0x00<<(7-i);
	  	
	}
	*buf=data;

	return 0;
}

/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	int i;
	unsigned long flags;
	unsigned char data[5];
	
	local_irq_save(flags);	  // 关中断
  	/* 1. 发送高脉冲启动DHT11 */
	dht11_start();

	/* 2. 等待DHT11就绪 */
	if (dht11_wait_for_ready())
	{
		local_irq_restore(flags); // 恢复中断
		return -EAGAIN;
	}
	
	/* 3. 读5字节数据 */
	for (i = 0; i < 5; i++)
	{
		if (dht11_read_byte(&data[i]))
		{
			printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
			local_irq_restore(flags); // 恢复中断
			return -EAGAIN;
		}
	}
	dht11_reset();
	local_irq_restore(flags); // 恢复中断

	/* 4. 根据校验码验证数据 */
	if (data[4] != (data[0] + data[1] + data[2] + data[3]))
		return -1;
		
	err=copy_to_user(buf, data, 4);
	return 4;
}

/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_dht11_drv = {
	.owner	 = THIS_MODULE,
	.read    = gpio_drv_read,
};

/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{
	int err = 0;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	//获取引脚
	dht11_pin= gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);
	
	/* 注册file_operations 	*/
	major = register_chrdev(0, "sch", &gpio_dht11_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "dht11_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "sch");
		return PTR_ERR(gpio_class);
	}
	
    /* 自动创建设备节点          /dev/dht11 */
	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "dht11"); 
    
	return 0;
}

static int  gpio_drv_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "sch");
	gpiod_put(dht11_pin);
    return 0;
}

static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "sch,dht11", },
        { /* sentinel */ }
};


static struct platform_driver gpio_platform_driver = {
	.driver = {
		.name = "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,
	},
	.probe  = gpio_drv_probe,
	.remove = gpio_drv_remove,
};


static int __init gpio_drv_init(void)
{
	/* 注册platform_driver */
	return platform_driver_register(&gpio_platform_driver);
}

static void __exit gpio_drv_exit(void)
{
	/* 反注册platform_driver */
	platform_driver_unregister(&gpio_platform_driver);
}

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");

app.c(查询方式)

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

static int fd;

/*
 * ./button_test /dev/100ask_button0
 *
 */
int main(int argc, char **argv)
{
	unsigned char data[4];
	int ret;
	int	flags;
	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR );
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while (1)
	{
		sleep(1);
		if (read(fd, data, 4) == 4)
		{
			printf("get humidity   : %d \n", data[0]);  
			printf("get temprature : %d \n", data[2]);
		}
		else
		{
			printf("get : -1\n");
		}
	}
	
	close(fd);
	
	return 0;
}

43_HS0038模块驱动

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

hs0038{
   compatible ="sch,hs0038";
   gpios=<&gpio4 19    GPIO_ACTIVE_HIGH>;
};

driver.c(中断方式)

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/version.h>

static int major;
static struct class *hs0038_class;
static struct gpio_desc *hs0038_data_pin;
//static struct gpio_desc *hs0038_test_pin;
static int irq;
static unsigned int hs0038_data = 0;  
static wait_queue_head_t hs0038_wq;
static u64 hs0038_edge_time[100];
static int hs0038_edge_cnt = 0;


static unsigned int hs0038_data_buf[8];
static int r, w;

static void put_data(unsigned int val)
{
	if (((w+1) & 7) != r)
	{
		hs0038_data_buf[w] = val;
		w = (w + 1) & 7;
	}
}

static int get_data(unsigned int *val)
{
	if (r == w)
	{
		return -1;
	}
	else
	{
		*val = hs0038_data_buf[r];
		r = (r + 1) & 7;		
		return 0;
	}
}

static int has_data(void)
{
	if (r == w)
		return 0;
	else
		return 1;
}

/* 0 : 成功, *val中记录数据
 * -1: 没接收完毕
 * -2: 解析错误
 */
int hs0038_parse_data(unsigned int *val)
{
	u64 tmp;
	unsigned char data[4];
	int i, j, m;
	
	/* 判断是否重复码 */
	if (hs0038_edge_cnt == 4)
	{
		tmp = hs0038_edge_time[1] - hs0038_edge_time[0];
		if (tmp > 8000000 && tmp < 10000000)
		{
			tmp = hs0038_edge_time[2] - hs0038_edge_time[1];
			if (tmp < 3000000)
			{
				/* 获得了重复码 */
				*val = hs0038_data;
				return 0;
			}
		}
	}

	/* 接收到了66次中断 */
	m = 3;
	if (hs0038_edge_cnt >= 68)
	{
		/* 解析到了数据 */
		for (i = 0; i < 4; i++)
		{
			data[i] = 0;
			/* 先接收到bit0 */
			for (j = 0; j < 8; j++)
			{
				/* 数值: 1 */	
				if (hs0038_edge_time[m+1] - hs0038_edge_time[m] > 1000000)
					data[i] |= (1<<j);
				m += 2;
			}
		}

		/* 检验数据 */
		data[1] = ~data[1];
		if (data[0] != data[1])
		{
			printk("%s %s line %d, %x, %x, %x\n", __FILE__, __FUNCTION__, __LINE__, data[0], data[1], ~data[1]);
			return -2;
		}

		data[3] = ~data[3];
		if (data[2] != data[3])
		{
			printk("%s %s line %d, %x, %x, %x\n", __FILE__, __FUNCTION__, __LINE__, data[2], data[3], ~data[3]);
			return -2;
		}

		hs0038_data = (data[0] << 8) | (data[2]);
		*val = hs0038_data;
		return 0;
	}
	else
	{
		/* 数据没接收完毕 */
		return -1;
	}	
}
	

static irqreturn_t hs0038_isr(int irq, void *dev_id)
{
	unsigned int val;
	int ret;
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
		hs0038_edge_time[hs0038_edge_cnt++] = ktime_get_boottime_ns();
#else
		hs0038_edge_time[hs0038_edge_cnt++] = ktime_get_boot_ns();
#endif

	/* 判断超时 */
	
	if (hs0038_edge_cnt >= 2)
	{
		if (hs0038_edge_time[hs0038_edge_cnt-1] - hs0038_edge_time[hs0038_edge_cnt-2] > 30000000)
		{
			/* 超时 */
			hs0038_edge_time[0] = hs0038_edge_time[hs0038_edge_cnt-1];
			hs0038_edge_cnt = 1;			
			return IRQ_HANDLED; // IRQ_WAKE_THREAD;
		}
	}

	ret = hs0038_parse_data(&val);
	if (!ret)
	{
		/* 解析成功 */
		hs0038_edge_cnt = 0;
		// printk("get ir code = 0x%x\n", val);
		put_data(val);
		wake_up(&hs0038_wq);
	}
	else if (ret == -2)
	{
		/* 解析失败 */
		hs0038_edge_cnt = 0;
	}
	
	return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}



/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t hs0038_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	unsigned int val;
	
	if (size != 4)
		return -EINVAL;

	wait_event_interruptible(hs0038_wq, has_data());

	get_data(&val);

	copy_to_user(buf, &val, 4);
		
	return 4;
}

static unsigned int hs0038_drv_poll(struct file *fp, poll_table * wait)
{
//	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
//	poll_wait(fp, &hs0038_wait, wait);
	return 0;
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations hs0038_fops = {
	.owner	 = THIS_MODULE,
	.read    = hs0038_drv_read,
	.poll    = hs0038_drv_poll,
};




/* 1. 从platform_device获得GPIO
 * 2. gpio=>irq
 * 3. request_irq
 */
static int hs0038_probe(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 1. 获得硬件信息 */
	hs0038_data_pin = gpiod_get(&pdev->dev, NULL, 0);
	if (IS_ERR(hs0038_data_pin))
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	irq = gpiod_to_irq(hs0038_data_pin);

	request_irq(irq, hs0038_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "hs0038", NULL);

	/* 2. device_create */
	device_create(hs0038_class, NULL, MKDEV(major, 0), NULL, "myhs0038");

	return 0;
}

static int hs0038_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(hs0038_class, MKDEV(major, 0));
	free_irq(irq, NULL);
	gpiod_put(hs0038_data_pin);
//	gpiod_put(hs0038_test_pin);

	return 0;
}


static const struct of_device_id ask100_hs0038[] = {
    { .compatible = "100ask,hs0038" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver hs0038_driver = {
    .probe      = hs0038_probe,
    .remove     = hs0038_remove,
    .driver     = {
        .name   = "100ask_hs0038",
        .of_match_table = ask100_hs0038,
    },
};

/* 2. 在入口函数注册platform_driver */
static int __init hs0038_init(void)
{
    int err;
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 注册file_operations 	*/
	major = register_chrdev(0, "hs0038", &hs0038_fops);  

	hs0038_class = class_create(THIS_MODULE, "hs0038_class");
	if (IS_ERR(hs0038_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "hs0038");
		return PTR_ERR(hs0038_class);
	}

	init_waitqueue_head(&hs0038_wq);

	
    err = platform_driver_register(&hs0038_driver); 
	
	return err;
}

/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 *     卸载platform_driver
 */
static void __exit hs0038_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister(&hs0038_driver);
	class_destroy(hs0038_class);
	unregister_chrdev(major, "hs0038");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(hs0038_init);
module_exit(hs0038_exit);

MODULE_LICENSE("GPL");

app.c

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


/*
 * ./hs0038_test /dev/myhs0038
 *
 */
int main(int argc, char **argv)
{
	int fd;
	unsigned int data;

	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
//	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}


	while (1)
	{
		if (read(fd, &data, 4) == 4)
		{
			printf("get IR code  : 0x%x\n", data);
		}
		else 
		{
			printf("get IR code: -1\n");
		}
		//sleep(5);
	}
	
	close(fd);
	
	return 0;
}


子系统

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

driver.c

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/version.h>
#include <linux/input.h>

static struct gpio_desc *hs0038_data_pin;
static int irq;
static unsigned int hs0038_data = 0;  
static u64 hs0038_edge_time[100];
static int hs0038_edge_cnt = 0;

static struct input_dev *hs0038_input_dev;

/* 0 : 成功, *val中记录数据
 * -1: 没接收完毕
 * -2: 解析错误
 */
int hs0038_parse_data(unsigned int *val)
{
	u64 tmp;
	unsigned char data[4];
	int i, j, m;
	
	/* 判断是否重复码 */
	if (hs0038_edge_cnt == 4)
	{
		tmp = hs0038_edge_time[1] - hs0038_edge_time[0];
		if (tmp > 8000000 && tmp < 10000000)
		{
			tmp = hs0038_edge_time[2] - hs0038_edge_time[1];
			if (tmp < 3000000)
			{
				/* 获得了重复码 */
				*val = hs0038_data;
				return 0;
			}
		}
	}

	/* 接收到了66次中断 */
	m = 3;
	if (hs0038_edge_cnt >= 68)
	{
		/* 解析到了数据 */
		for (i = 0; i < 4; i++)
		{
			data[i] = 0;
			/* 先接收到bit0 */
			for (j = 0; j < 8; j++)
			{
				/* 数值: 1 */	
				if (hs0038_edge_time[m+1] - hs0038_edge_time[m] > 1000000)
					data[i] |= (1<<j);
				m += 2;
			}
		}

		/* 检验数据 */
		data[1] = ~data[1];
		if (data[0] != data[1])
		{
			//printk("%s %s line %d, %x, %x, %x\n", __FILE__, __FUNCTION__, __LINE__, data[0], data[1], ~data[1]);
			return -2;
		}

		data[3] = ~data[3];
		if (data[2] != data[3])
		{
			//printk("%s %s line %d, %x, %x, %x\n", __FILE__, __FUNCTION__, __LINE__, data[2], data[3], ~data[3]);
			return -2;
		}

		hs0038_data = (data[0] << 8) | (data[2]);
		*val = hs0038_data;
		return 0;
	}
	else
	{
		/* 数据没接收完毕 */
		return -1;
	}	
}
	

static irqreturn_t hs0038_isr(int irq, void *dev_id)
{
	unsigned int val;
	int ret;
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
		hs0038_edge_time[hs0038_edge_cnt++] = ktime_get_boottime_ns();
#else
		hs0038_edge_time[hs0038_edge_cnt++] = ktime_get_boot_ns();
#endif

	/* 判断超时 */
	
	if (hs0038_edge_cnt >= 2)
	{
		if (hs0038_edge_time[hs0038_edge_cnt-1] - hs0038_edge_time[hs0038_edge_cnt-2] > 30000000)
		{
			/* 超时 */
			hs0038_edge_time[0] = hs0038_edge_time[hs0038_edge_cnt-1];
			hs0038_edge_cnt = 1;			
			return IRQ_HANDLED; // IRQ_WAKE_THREAD;
		}
	}

	ret = hs0038_parse_data(&val);
	if (!ret)
	{
		/* 解析成功 */
		hs0038_edge_cnt = 0;

		/* D. 输入系统: 上报数据 */
		input_event(hs0038_input_dev, EV_KEY, val, 1);
		input_event(hs0038_input_dev, EV_KEY, val, 0);
		input_sync(hs0038_input_dev);		
		//input_event(hs0038_input_dev, EV_SYN, 0, 0);//分隔符相当于
	}
	else if (ret == -2)
	{
		/* 解析失败 */
		hs0038_edge_cnt = 0;
	}
	return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}



/* 1. 从platform_device获得GPIO
 * 2. gpio=>irq
 * 3. request_irq
 */
static int hs0038_probe(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 1. 获得硬件信息 */
	hs0038_data_pin = gpiod_get(&pdev->dev, NULL, 0);
	if (IS_ERR(hs0038_data_pin))
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}
	irq = gpiod_to_irq(hs0038_data_pin);
	request_irq(irq, hs0038_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "hs0038", NULL);

	/* 输入系统的代码 */
	/* 参考: drivers\input\keyboard\gpio_keys.c */
	/* A. 分配input_dev */
	hs0038_input_dev = devm_input_allocate_device(&pdev->dev);

	/* B. 设置input_dev */
	hs0038_input_dev->name = "hs0038";
	hs0038_input_dev->phys = "hs0038";

	/* B.1 能产生哪类事件 */
	__set_bit(EV_KEY, hs0038_input_dev->evbit);
	__set_bit(EV_REP, hs0038_input_dev->evbit);//允许重复按键类型
	
	/* B.2 能产生哪些事件 */
	//__set_bit(KEY_0, hs0038_input_dev->keybit);
	memset(hs0038_input_dev->keybit, 0xff, sizeof(hs0038_input_dev->keybit));
	
	/* C. 注册input_dev */
	input_register_device(hs0038_input_dev);

	return 0;
}

static int hs0038_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	input_unregister_device(hs0038_input_dev);

	free_irq(irq, NULL);
	gpiod_put(hs0038_data_pin);

	return 0;
}

static const struct of_device_id ask100_hs0038[] = {
    { .compatible = "sch,hs0038" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver hs0038_driver = {
    .probe      = hs0038_probe,
    .remove     = hs0038_remove,
    .driver     = {
        .name   = "100ask_hs0038",
        .of_match_table = ask100_hs0038,
    },
};

/* 2. 在入口函数注册platform_driver */
static int __init hs0038_init(void)
{
    int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    err = platform_driver_register(&hs0038_driver); 
	return err;
}

/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 *     卸载platform_driver
 */
static void __exit hs0038_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister(&hs0038_driver);

}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(hs0038_init);
module_exit(hs0038_exit);

MODULE_LICENSE("GPL");




app.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>
#include <linux/input.h>

/*
 * ./hs0038_test /dev/myhs0038
 *
 */
int main(int argc, char **argv)
{
	int fd;
	//unsigned int data;
	struct input_event data;

	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
//	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while (1)
	{
		if (read(fd, &data, sizeof(data)) == sizeof(data))
		{
			printf("get IR code  :\n");
			printf(" Type: 0x%x\n", data.type);
			printf(" Code: 0x%x\n", data.code);
			printf(" Val : 0x%x\n", data.value);
		}
		else 
		{
			printf("get IR code: -1\n");
		}
		//sleep(5);
	}
	
	close(fd);
	
	return 0;
}


44_AT24C02模块驱动(IIC)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

&i2c1 {
	at24c02 {
		compatible = "sch,at24c02";
		reg = <0x50>;
	};
};

driver.c

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>

#define IOC_AT24C02_READ  100
#define IOC_AT24C02_WRITE 101

static struct i2c_client *at24c02_client;
static int major;
static struct class *at24c02_class;

static long at24c02_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int err;
	unsigned char ker_buf[2];
	unsigned char *usr_buf = (unsigned char *)arg;
	struct i2c_msg msgs[2];

    err=copy_from_user(ker_buf,usr_buf,2);

    switch (cmd)
    {
      case IOC_AT24C02_READ:
	  	   /* 读AT24C02 */
	  	   msgs[0].addr=at24c02_client->addr;
		   msgs[0].flags=0;/* 写 */
	       msgs[0].len=1;
		   msgs[0].buf=ker_buf;

		   msgs[1].addr=at24c02_client->addr;
		   msgs[1].flags=I2C_M_RD;/* 读 */
	       msgs[1].len=1;
		   msgs[1].buf=ker_buf+1;
		   
	  	   i2c_transfer(at24c02_client->adapter, msgs, 2);
	  	 
		   err=copy_to_user(usr_buf,ker_buf,2);		   
		   break;
	  case IOC_AT24C02_WRITE:
	  	
           /* 写AT24C02 */
	  	   msgs[0].addr=at24c02_client->addr;
		   msgs[0].flags=0;/* 写 */
	       msgs[0].len=2;
		   msgs[0].buf=ker_buf;

		   i2c_transfer(at24c02_client->adapter, msgs, 1);		   
		   break;
	}
	return 0;
}

/* 定义自己的file_operations结构体                                              */
static struct file_operations at24c02_fops = {
	.owner	 = THIS_MODULE,
	.unlocked_ioctl = at24c02_ioctl,
};

static int at24c02_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	at24c02_client = client;

	/* register_chrdev */
	major = register_chrdev(0, "at24c02", &at24c02_fops);  

	/* class_create */
	at24c02_class = class_create(THIS_MODULE, "at24c02_class");

	/* device_create */
	device_create(at24c02_class, NULL, MKDEV(major, 0), NULL, "myat24c02");

	return 0;
}

static int at24c02_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    device_destroy(at24c02_class, MKDEV(major, 0));
	class_destroy(at24c02_class);
	unregister_chrdev(major, "at24c02");
	return 0;
}

static const struct of_device_id at24c02_of_match[] = {
	{.compatible = "sch,at24c02"},
	{}
};

static const struct i2c_device_id at24c02_ids[] = {
	{ "xxxxyyy",	(kernel_ulong_t)NULL },
	{ /* END OF LIST */ }
};

static struct i2c_driver at24c02_drv = {
	.driver = {
		.name = "my24c02",
		.of_match_table	 = at24c02_of_match,
	},
	.probe = at24c02_probe,
	.remove = at24c02_remove,
	.id_table = at24c02_ids,
};

static int at24c02_init(void)
{
	printk("%s %s %d \n", __FILE__, __FUNCTION__, __LINE__);
	return i2c_add_driver(&at24c02_drv);
}

static void at24c02_exit(void)
{
	i2c_del_driver(&at24c02_drv);
}

module_init(at24c02_init);
module_exit(at24c02_exit);

MODULE_LICENSE("GPL");

app.c

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

#define IOC_AT24C02_READ  100
#define IOC_AT24C02_WRITE 101

/*
 * at24c02_test /dev/myat24c02 r 10
 * at24c02_test /dev/myat24c02 w 10 123
 */
int main(int argc, char ** argv)
{
	int fd;
	char buf[2];

	if ((argc != 4) && (argc != 5))
	{
		printf("Usage: %s <dev> r <addr>\n", argv[0]);
		printf("       %s <dev> w <addr> <val>\n", argv[0]);
		return -1;
	}

    fd=open(argv[1],O_RDWR);
	if (fd < 0)
	{
		printf(" can not open %s\n", argv[1]);
		return -1;
	}
	
	if(argv[2][0]=='r')
	{
		buf[0]=strtoul(argv[3], NULL, 0);
        ioctl(fd, IOC_AT24C02_READ, buf);
		printf("Read addr 0x%x, get data 0x%x\n", buf[0], buf[1]);
	}
	else
	{
		buf[0]=strtoul(argv[3], NULL, 0);
        buf[1]=strtoul(argv[4], NULL, 0);
		ioctl(fd, IOC_AT24C02_WRITE, buf);
	}

	close(fd);
	return 0;

}

在这里插入图片描述

45_OLED模块驱动(spi)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

cat /sys/kernel/debug/gpio
ls /sys/bus/platform/devices/

调试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

pc 代表出错的位置 1ec代表总的大小
arm-linux-objdump -D at24c02_drv.ko > at24c02_drv.dis
vi at24c02_drv.dis

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自然醒欧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值