这里写目录标题
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-based | legacy |
---|---|
获得GPIO | |
gpiod_get | gpio_request |
gpiod_get_index | |
gpiod_get_array | gpio_request_array |
devm_gpiod_get | |
devm_gpiod_get_index | |
devm_gpiod_get_array | |
设置方向 | |
gpiod_direction_input | gpio_direction_input |
gpiod_direction_output | gpio_direction_output |
读值、写值 | |
gpiod_get_value | gpio_get_value |
gpiod_set_value | gpio_set_value |
释放GPIO | |
gpio_free | gpio_free |
gpiod_put | gpio_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结构体:
- 设置
- 注册
环形缓冲区
阻塞
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