Notify microcontroller GPIO control signal level by a character device file.
Driver code
Get GPIO No.
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;
}
Register character device
#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 function is implemented as follows: a received signal corresponding to high and low settings
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;
}
Asynchronous notification function initializes for the target process ID specified kill_fasync function
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);
}
Specified data file content storage location, location: / 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);
Control Code
Divided into two parts: GPIO interrupt function which sends a signal to use kill_fasync character device; used elsewhere ioctl command issued to a character device;
1, GPIO interrupt function which sends a signal to use kill_fasync character device;
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, the rest of the received signal and acquire the content data, and then using the ioctl command issued to a character device;
#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;
}
Signal processing function
#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);
}
This way GPIO GPIO need to prevent conflict led to sporadic low probability problem of signal sent unsuccessful.