GPIO子系统和字符设备驱动实现点灯
.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "led.h"
#include <linux/device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#define CNAME "myled"
//向上层提交目录结构体
struct class* clas;
//字符设备结构体
struct device* devic;
//设备号
dev_t dev;
//主设备号
int major;
//次设备号
int minjor=0;
char kbuf[128] = {0};
//设备树节点结构体
struct device_node* node;
int i=0;
struct cdev* cdev;
#if 1
int major =0;//动态申请设备号
#else
int major = 500; //静态申请设备号
#endif
int count =6;
dev_t dev;
//设置gpio变量
int gpiono1,gpiono2,gpiono3,gpiono4,gpiono5,gpiono6;
int mycdev_open(struct inode *inode, struct file *file)
{
int minjor;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
//通过inode结构体取到次设备号
minjor = MINOR(inode->i_rdev);
//将次设备号放到file结构体私有数据中
file->private_data = (void*)minjor;
return 0;
}
ssize_t mycdev_write(struct file *file, const char __user *user, size_t size, loff_t *loff)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int minjor;
//利用私有数据传参拿到open函数渠道的次设备号
minjor = (int)file->private_data;
//判断次设备号
switch(minjor)
{
//次设备号0
case 0:
//判断顶二个参数传过来的命令码
switch(cmd)
{
case LED_ON:
//让gpio输入高电平
gpio_set_value(gpiono1, 1);
break;
case LED_OFF:
//让gpio输入低电平
gpio_set_value(gpiono1, 0);
break;
}
break;
//次设备号0
case 1:
switch(cmd)
{
case LED_ON:
//让gpio输入高电平
gpio_set_value(gpiono2, 1);
break;
case LED_OFF:
//让gpio输入低电平
gpio_set_value(gpiono2, 0);
break;
}
break;
//次设备号2
case 2:
switch(cmd)
{
case LED_ON:
//让gpio输入高电平
gpio_set_value(gpiono3, 1);
break;
case LED_OFF:
//让gpio输入低电平
gpio_set_value(gpiono3, 0);
break;
}
break;
//次设备号3
case 3:
switch(cmd)
{
case LED_ON:
//让gpio输入高电平
gpio_set_value(gpiono4, 1);
break;
case LED_OFF:
//让gpio输入低电平
gpio_set_value(gpiono4, 0);
break;
}
break;
//次设备号4
case 4:
switch(cmd)
{
case LED_ON:
//让gpio输入高电平
gpio_set_value(gpiono5, 1);
break;
case LED_OFF:
//让gpio输入低电平
gpio_set_value(gpiono5, 0);
break;
}
break;
//次设备号5
case 5:
switch(cmd)
{
case LED_ON:
//让gpio输入高电平
gpio_set_value(gpiono6, 1);
break;
case LED_OFF:
//让gpio输入低电平
gpio_set_value(gpiono6, 0);
break;
}
break;
}
return 0;
}
//操作方法结构体
const struct file_operations fops =
{
.open = mycdev_open,
.release = mycdev_close,
.read = mycdev_read,
.write = mycdev_write,
.unlocked_ioctl = my_ioctl,
};
//入口
static int __init mycdev_init(void)
{
int ret;
//1.分配cdev字符设备结构体指针
cdev = cdev_alloc();
if(NULL==cdev)
{
goto ERR1;
}
//2.初始化字符设备
//
cdev_init(cdev, &fops);
//3.动态申请设备号
if(major == 0)
{ //申请到的设备号 从0开始 数量 名字
ret = alloc_chrdev_region(&dev, minjor, count, CNAME); // //proc/devices可以查看
if(ret)
{
printk("alloc_chrdev_rejion is error\n");
ret = -ENOMEM;
goto ERR2;
}
//获取主设备号
major = MAJOR(dev);
//获取次设备号
minjor = MINOR(dev);
}
//静态动态申请设备号
else{ //静态指定设备号 个数 名字
ret = register_chrdev_region(MKDEV(major,minjor),count,CNAME);//proc/devices可以查看
if(ret)
{
printk("register is error\n");
ret = ENOMEM;
goto ERR2;
}
}
//4.驱动的注册 结构体 设备号 数量
ret = cdev_add(cdev, MKDEV(major, minjor), count);
if(ret)
{
printk("cdev_add is err\n");
ret = EIO;
goto ERR3;
}
//5.自动创建设备节点
printk("major=%d\n", major);
//向上层提交目录获取目录句柄指针
clas = class_create(THIS_MODULE,"lisi"); // /sys/class可查看
if(IS_ERR(clas))
{
ret = PTR_ERR(clas);
goto ERR4;
}
for(i=0; i<6; i++)
{
//向上层提交设备节点 句柄 设备号 名字
devic = device_create(clas, NULL,MKDEV(major,i), NULL,"myled%d", i); // /dev可以查看
if(IS_ERR(clas))
{
ret = PTR_ERR(clas);
goto ERR5;
}
}
//根据名字找到设备树节点信息结构体
node = of_find_node_by_name(NULL, "myleds");
//根据键名获取gpio号 设备树节点信息结构体 键名 索引号
gpiono1 = of_get_named_gpio(node,"myled1",0);
gpiono2 = of_get_named_gpio(node,"myled2",0);
gpiono3 = of_get_named_gpio(node,"myled3",0);
gpiono4 = of_get_named_gpio(node,"myled4",0);
gpiono5 = of_get_named_gpio(node,"myled5",0);
gpiono6 = of_get_named_gpio(node,"myled6",0);
//申请指定gpio号的使用权
//
gpio_request(gpiono1, NULL);
gpio_request(gpiono2, NULL);
gpio_request(gpiono3, NULL);
gpio_request(gpiono4, NULL);
gpio_request(gpiono5, NULL);
gpio_request(gpiono6, NULL);
//设置gpio为输出
gpio_direction_output(gpiono1, 0);
gpio_direction_output(gpiono2, 0);
gpio_direction_output(gpiono3, 0);
gpio_direction_output(gpiono4, 0);
gpio_direction_output(gpiono5, 0);
gpio_direction_output(gpiono6, 0);
return 0;
ERR5:
for(--i; i>=0; i--)
{
device_destroy(clas, i);
}
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major,minjor),count);
ERR2:
kfree(cdev);
ERR1:
return EIO;
}
//出口
static void __exit mycdev_exit(void)
{
//
gpio_set_value(gpiono1,0);
gpio_set_value(gpiono2,0);
gpio_set_value(gpiono3,0);
gpio_set_value(gpiono4,0);
gpio_set_value(gpiono5,0);
gpio_set_value(gpiono6,0);
//释放gpio编号
gpio_free(gpiono1);
gpio_free(gpiono2);
gpio_free(gpiono3);
gpio_free(gpiono4);
gpio_free(gpiono5);
gpio_free(gpiono6);
//1。销毁设备节点信息
for(i=0; i<6; i++)
{
device_destroy(clas, MKDEV(major,i));
}
//2.销毁目录信息
class_destroy(clas);
//3.驱动的注销
cdev_del(cdev);
//4.销毁设备号
unregister_chrdev_region(MKDEV(major,minjor),count);
//5.释放cdev结构体
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
.h
#ifndef __MYLED_H__
#define __MYLED_H__
#define LED_ON _IOW('a',1,int)
#define LED_OFF _IOW('a',0,int)
typedef enum{
LED1,
LED2,
LED3,
LED4,
LED5,
LED6
}led_t;
#endif
test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "led.h"
char buf[128] = {"lisi"};
int fd1 = -1;
int fd2 = -1;
int fd3 = -1;
int fd4 = -1;
int fd5 = -1;
int fd6 = -1;
int main(int argc, char const *argv[])
{
fd1 = open("/dev/myled0", O_RDWR);
if (-1 == fd1)
{
perror("open is error\n");
exit(1);
}
fd2 = open("/dev/myled1", O_RDWR);
if (-1 == fd2)
{
perror("open is error\n");
exit(1);
}
fd3 = open("/dev/myled2", O_RDWR);
if (-1 == fd3)
{
perror("open is error\n");
exit(1);
}
fd4 = open("/dev/myled3", O_RDWR);
if (-1 == fd4)
{
perror("open is error\n");
exit(1);
}
fd5 = open("/dev/myled4", O_RDWR);
if (-1 == fd5)
{
perror("open is error\n");
exit(1);
}
fd6 = open("/dev/myled5", O_RDWR);
if (-1 == fd6)
{
perror("open is error\n");
exit(1);
}
int whitch;
while (1)
{
whitch = LED1;
ioctl(fd1,LED_ON);
sleep(1);
ioctl(fd1,LED_OFF);
sleep(1);
whitch = LED2;
ioctl(fd2,LED_ON);
sleep(1);
ioctl(fd2,LED_OFF);
sleep(1);
whitch = LED3;
ioctl(fd3,LED_ON);
sleep(1);
ioctl(fd3,LED_OFF);
sleep(1);
ioctl(fd4,LED_ON);
sleep(1);
ioctl(fd4,LED_OFF);
ioctl(fd5,LED_ON);
sleep(1);
ioctl(fd5,LED_OFF);
ioctl(fd6,LED_ON);
sleep(1);
ioctl(fd6,LED_OFF);
}
close(fd1);
close(fd2);
close(fd3);
close(fd4);
close(fd5);
close(fd6);
return 0;
}
实验现象