通过字符设备文件控制GPIO电平通知单片机信号。
获取GPIO号
notify_gpio = of_get_named_gpio(pdev->dev.of_node, "notify_gpio", 0);
if((ret = gpio_init()) < 0)
{
printk("[gale] %s: notify pin init failed\n", __func__);
goto err_gpio_init;
}
注册字符设备
#define GPIO_MAJOR 199 // major device NO.
#define GPIO_MINOR 0 // minor device NO.
#define DEVICE_NAME "gpio_notify" //driver device node.
static const struct file_operations gpio_fops =
{
.owner = THIS_MODULE,
.open = gpio_open,
.release = gpio_release,
.unlocked_ioctl = gpio_ioctl,
.fasync = gpio_fasync,
};
static int gpio_init(void)
{
int ret;
dev_t my_dev_no;
struct cdev *gpio_cdev;
gpio_cdev = cdev_alloc();
if(gpio_cdev == NULL)
{
printk(KERN_EMERG "Cannot alloc cdev\n");
goto request_failed;
}
cdev_init(gpio_cdev,&gpio_fops);
gpio_cdev->owner=THIS_MODULE;
ret = alloc_chrdev_region(&my_dev_no,0,1,DEVICE_NAME);
if(ret < 0)
{
printk(KERN_EMERG "alloc_chrdev_region failed\n");
goto request_failed;
}
ret=cdev_add(gpio_cdev,my_dev_no,1);
ret = register_chrdev(GPIO_MAJOR, DEVICE_NAME, &gpio_fops);
if(ret < 0)
{
printk(KERN_EMERG "GPIO register failed\n");
goto request_failed;
}
gpio_class = class_create(THIS_MODULE, DEVICE_NAME);
device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, GPIO_MINOR), NULL, DEVICE_NAME);
printk("[gale] %s: OK ###############################\n", __func__);
return ret;
request_failed:
return ret;
}
ioctl函数实现如下:收到信号设置对应的高低电平
static int gpio_ioctl(struct file *filp,unsigned int cmd)
{
if(0 == notify_gpio)
return -1;
switch(cmd)
{
case SET_OUTPUT_LOW://0
{
printk("\n\n%s: set mcu wakeup(pin2) to low level\n",current->comm);
gpio_direction_output(notify_gpio, 0);
break;
}
case SET_OUTPUT_HIGH://1
{
printk("\n\n%s: set mcu wakeup(pin2) to high level\n",current->comm);
gpio_direction_output(notify_gpio, 1);
break;
}
case GET_VALUE://2
{
return gpio_get_value(notify_gpio);
}
case SET_INPUT://3
{
gpio_direction_input(notify_gpio);
break;
}
default:
{
printk(KERN_EMERG "GPIO command mistake!!!\n");
break;
}
}
return 0;
}
异步通知函数初始化,用于指定kill_fasync函数的目标进程ID
static int gpio_fasync (int fd, struct file *filp, int mode)
{
printk("quectel low consume: gpio_fasync\n");
return fasync_helper(fd, filp, mode, &gpio_async);
}
指定数据内容存储文件位置,位置在:/sys/devices/soc:quec,quectel-low-consume/irq_edge
static volatile char edge[8] = "rising";
static ssize_t irq_edge_show(struct device* dev, struct device_attribute *attr, char* buf)
{
ssize_t ret = 0;
sprintf(buf, "%s", edge);
ret = strlen(buf) + 1;
return ret;
}
static DEVICE_ATTR(irq_edge, S_IRUGO, irq_edge_show, NULL);
分2个部分:GPIO中断函数里面使用kill_fasync发信号给字符设备;其他地方使用ioctl给字符设备发命令;
1、GPIO中断函数里面使用kill_fasync发信号给字符设备;
static irqreturn_t quectel_wakeup_irq_func(void)
{
int value = gpio_get_value(wakeup_gpio);
if(value == LOW_VALUE)
{
strcpy(edge, "falling");
kill_fasync(&gpio_async, SIGIO, POLL_IN);
}
else if(value == HIGH_VALUE)
{
strcpy(edge, "rising");
kill_fasync(&gpio_async, SIGIO, POLL_IN );
}
return IRQ_HANDLED;
}
2、其他地方收到信号并获取数据内容,然后使用ioctl给字符设备发命令;
#define WAKEUP_IO "/dev/gpio_notify"
pm_register(WAKEUP_IO, pm_handle);
static int pm_register(char *path, void (*handle)(int signal))
{
int oflags;
signal(SIGIO, handle); /* 应用程序注册信号处理函数, SIGIO 表示 Io 有数据可供读取; */
io_fd = open(path, O_RDWR);
if (io_fd < 0)
return -1;
fcntl(io_fd, F_SETOWN, getpid());/* 信号发给谁,是通过这段程序告诉内核的 */
oflags = fcntl(io_fd, F_GETFL);//读取 Oflags;
fcntl(io_fd, F_SETFL, oflags | FASYNC);/* 改变 Oflags 为异步通知 FASYNC, 改变 FASYNC标志, 最终调用到驱动的 fasync -> fasync_helper */
handle(SIGIO);
return 0;
}
信号处理函数
#define IRQ_EDGE "/sys/devices/soc:quec,quectel-low-consume/irq_edge"
#define ACTIVE 0
static void pm_handle(int signal)
{
int fd;
int count = 0;
char edge[8] = {0};
fd = open(IRQ_EDGE, O_RDONLY | O_NONBLOCK);
if (fd < 0)
return "null";
count = read(fd, edge, sizeof(edge)); //读取数据内容
printf("%s:%s(%d)\n", IRQ_EDGE, edge, count);
close(fd);
if (strncmp(edge, "falling", 7) == 0) {
ioctl(io_fd, !ACTIVE);
} else if (strncmp(edge, "rising", 6) == 0) {
ioctl(io_fd, ACTIVE);
}
这样的GPIO方式需防止GPIO冲突导致信号发送不成功的偶现低概率问题。