通过修改设备树,使用GPIO子系统控制LED灯的亮灭
驱动文件:dt_gpiod_led.c
#include <linux/init.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/module.h>
#define MAX 3
#define ON _IOW('l', 1, int)
#define OFF _IOW('l',0,int)
enum{
LED1,
LED2,
LED3
};
/* 设备树信息
myleds {
leds=<&gpioe 10 0>,<&gpiof 10 0>,<&gpioe 8 0>;
};
*/
int myled_open(struct inode *, struct file *);
ssize_t myled_read(struct file *, char __user *, size_t, loff_t *);
ssize_t myled_write(struct file *, const char __user *, size_t, loff_t *);
long myled_ioctl(struct file *, unsigned int, unsigned long);
int myled_close(struct inode *, struct file *);
struct dev_struct {
int major;
int minor;
int count;
dev_t devno;
char *name;
struct class *cls;
struct device *dev;
struct cdev *cdev;
struct file_operations fops;
};
struct dev_struct myled = {
.major=0,
.minor=0,
.count=3,
.name="myled",
.fops={
.open=myled_open,
.read=myled_read,
.write=myled_write,
.unlocked_ioctl=myled_ioctl,
.release=myled_close,
},
};
struct device_node *nd;
struct gpio_desc *gpiono[MAX];
int myled_up(void);
void myled_del(void);
static int __init myled_init(void)
{
int i;
int ret = myled_up();
if(ret<0){
goto out1;
}
//解析设备树中led节点
nd=of_find_node_by_path("/myleds");
if(nd==NULL){
printk("解析设备树节点失败\n");
ret = -EINVAL;
goto out2;
}
//从设备树节点获取并申请gpio编号
for (i = 0;i<MAX;i++){
gpiono[i]=gpiod_get_from_of_node(nd,"leds",i,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpiono[i])){
printk("申请gpio编号失败\n");
ret=PTR_ERR(gpiono[i]);
goto out3;
}
}
return 0;
out3:
for (i--; i >= 0;i--){
gpiod_put(gpiono[i]);
}
out2:
myled_del();
out1:
return ret;
}
static void __exit myled_exit(void)
{
int i;
for(i=0;i<MAX;i++){
gpiod_set_value(gpiono[i], 0);
}
for (i = 0;i<MAX;i++){
gpiod_put(gpiono[i]);
}
myled_del();
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
/**************************函数实现*******************************/
int myled_up(void)
{
int ret;
int i;
// 申请字符设备
myled.cdev=cdev_alloc();
if(myled.cdev==NULL){
printk("字符设备申请失败\n");
ret = -ENOMEM;
goto ERR1;
}
//初始化字符设备
cdev_init(myled.cdev,&myled.fops);
// 申请设备号
if(myled.major==0){
ret = alloc_chrdev_region(&myled.devno, myled.minor,myled.count,myled.name);
if(ret<0){
printk("申请设备号失败\n");
goto ERR2;
}
myled.major = MAJOR(myled.devno);
}else if(myled.major>0){
ret = register_chrdev_region(MKDEV(myled.major,myled.minor),myled.count,myled.name);
if(ret<0){
printk("申请设备号失败\n");
goto ERR2;
}
myled.devno = MKDEV(myled.major, myled.minor);
}
//向内核注册字符设备
ret=cdev_add(myled.cdev,myled.devno,myled.count);
if(ret<0){
printk("注册字符设备失败\n");
goto ERR3;
}
//自动创建设备节点
//向上层提交目录
myled.cls=class_create(THIS_MODULE,myled.name);
if(IS_ERR(myled.cls)){
printk("向上层提交目录失败\n");
ret = PTR_ERR(myled.cls);
goto ERR4;
}
//向上层提交节点信息
for (i = myled.minor; i < myled.minor+myled.count;i++){
myled.dev = device_create(myled.cls, NULL, MKDEV(myled.major,i), NULL,\
"%s%d", myled.name, i);
if(IS_ERR(myled.dev)){
printk("向上层提交节点信息失败\n");
ret = PTR_ERR(myled.dev);
goto ERR5;
}
}
return 0;
ERR5:
for (i--; i >= myled.major;i--){
device_destroy(myled.cls,MKDEV(myled.major,i));
}
class_destroy(myled.cls);
ERR4:
cdev_del(myled.cdev);
ERR3:
unregister_chrdev_region(myled.devno,myled.count);
ERR2:
kfree(myled.cdev);
ERR1:
return ret;
}
void myled_del(void)
{
int i;
for (i = myled.minor; i < myled.minor+myled.count;i++){
device_destroy(myled.cls,MKDEV(myled.major,i));
}
class_destroy(myled.cls);
cdev_del(myled.cdev);
unregister_chrdev_region(myled.devno,myled.count);
kfree(myled.cdev);
}
int myled_open(struct inode *inode, struct file *file)
{
return 0;
}
ssize_t myled_read(struct file *file, char __user *ubuf, size_t size, loff_t *off)
{
return 0;
}
ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *off)
{
return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret,value;
ret = copy_from_user(&value,(void *)arg,_IOC_SIZE(cmd));
if(ret){
printk("copy_from_user failed\n");
return -EINVAL;
}
switch (cmd)
{
case ON:
switch(value){
case LED1:
gpiod_set_value(gpiono[0],1);
break;
case LED2:
gpiod_set_value(gpiono[1],1);
break;
case LED3:
gpiod_set_value(gpiono[2],1);
break;
}
break;
case OFF:
switch(value){
case LED1:
gpiod_set_value(gpiono[0],0);
break;
case LED2:
gpiod_set_value(gpiono[1],0);
break;
case LED3:
gpiod_set_value(gpiono[2],0);
break;
}
break;
}
return 0;
}
int myled_close(struct inode *inode, struct file *file)
{
return 0;
}
测试文件:test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
enum{
LED1,
LED2,
LED3,
};
#define ON _IOW('l', 1, int)
#define OFF _IOW('l',0,int)
typedef struct {
char *cmd;
unsigned long type;
int devno;
} cmd_t;
cmd_t opt[6] = {
[0]={"LED1ON",ON,LED1},
[1]={"LED1OFF",OFF,LED1},
[2]={"LED2ON",ON,LED2},
[3]={"LED2OFF",OFF,LED2},
[4]={"LED3ON",ON,LED3},
[5]={"LED3OFF",OFF,LED3}
};
int find_command(const char *str,unsigned long *which,int *dev)
{
int i;
for (i = 0; i < 6; i++)
{
if(strcmp(opt[i].cmd,str)==0){
*which = opt[i].type;
*dev = opt[i].devno;
return 0;
}
}
return -1;
}
int main(int argc,const char * argv[])
{
int ret;
char buf[64] = {0};
int devno=0;
unsigned long which=0;
int fd = open("/dev/myled0", O_RDWR);
if(fd<0){
PRINT_ERR("open error");
}
while(1){
printf("Please input ( LEDxON / LEDxOFF )[x=1,2,...] >> ");
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';
ret=find_command(buf,&which, &devno);
if(ret<0){
continue;
}
ret=ioctl(fd, which, &devno);
if(ret<0){
PRINT_ERR("ioctl error");
return -1;
}
which = devno = 0;
}
close(fd);
return 0;
}