1、应用场景
新项目中有一个8pin的输入/输入引脚,通过io命令设置工作模式,读取输入输出电平过于复杂,于是就写了一个驱动用于控制GPIO工作模式,读取GPIO输入/输出电平。
2、驱动代码
文件名gpio-rockchip-cdev.c
#include <dt-bindings/gpio/gpio.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#define GPIO_OUT_HIGH 1
#define GPIO_OUT_LOW 0
#define GPIO_IN_HIGH 1
#define GPIO_IN_LOW 0
#define GPIOPIN_NAME "gpiopin"
#define IOCTL_INPUT_MOD 3
#define IOCTL_OUNPUT_MOD 2
#define MAX_GPIO_PIN_COUNT 8
struct gpiopin_dev{
struct cdev cdev; //charater dev
struct device *device; //device
struct gpio_desc *gpiod; //gpio number
int gpiostate; //gpio state
};
struct UsrMesg{
int gpioval;
};
static struct gpiopin_dev gpiopin[8];
static struct class *gpiopin_class;
static dev_t DevMajNum = 0;
/* open gpiopin device */
static int gpiopin_open(struct inode *inode ,struct file *filp)
{
int minor = iminor(inode);
printk("minor = %d\n",minor);
if (minor < 0 || minor >= MAX_GPIO_PIN_COUNT) {
return -ENODEV;
}
filp->private_data = &gpiopin[minor];//device sub device id
return 0;
}
/*read data from gpiopin input device*/
static ssize_t gpiopin_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
{
int ret = 0;
int RetValue = 0,PinStat = 0;
struct gpiopin_dev *dev=filp->private_data;
PinStat=gpiod_get_direction(dev->gpiod);//if Pinstat equal 1,Gpio pin working at input state
if(PinStat == 1){
printk("wx debug gipo working input mode\n");
RetValue=gpiod_get_value(dev->gpiod);//Get gpio pin receive value
printk("wx debug gpio input value %d\n",RetValue);
ret = copy_to_user(buf,&RetValue,sizeof(RetValue));
return ret;
}
return PinStat;
}
/*write data to gpiopin output mode*/
static ssize_t gpiopin_write(struct file *filp,const char __user *buf,size_t cnt ,loff_t *offt) //return value mast equal cnt
{
int ret = 0;
unsigned char PinStat = 0,DataBuf[20];
struct gpiopin_dev *dev=filp->private_data;
printk("wx devug gpio driver input char number=%ld\n",cnt);
printk("wx debug write to gpio pin\n");
PinStat=gpiod_get_direction(dev->gpiod);//if Pinstat equal 1,Gpio pin input state
printk("Pinstat = %d\n",PinStat);
if(PinStat == 0){
printk("wx debug gipo working output mode\n");
ret = copy_from_user(DataBuf,buf,cnt);
if(ret < 0)
{
printk("wx debug Kernel Read/Write Error");
return -EFAULT;
}
printk("wx debug ret = %d\n",ret);
printk("wx debug Databuf[0]=%d\n",DataBuf[0]);
if(DataBuf[0] == GPIO_OUT_HIGH || DataBuf[0] == '1'){
gpiod_set_value(dev->gpiod,GPIO_OUT_HIGH);
/*
* gpiod_set_value的有效值由设备树中决定
* gpios = <&gpio1 RK_PD6 GPIO_ACTIVE_HIGH>; GPIO_ACTIVE_HIGH决定了输出高电平的值
*/
printk("wx debug set gpio output high level\n");
}else if(DataBuf[0] == GPIO_IN_LOW || DataBuf[0] == '0'){
gpiod_set_value(dev->gpiod,GPIO_OUT_LOW);
printk("wx debug set gpio output low level\n");
}
}
return cnt;
}
static long gpiopin_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct UsrMesg MyUsrMesg;
struct gpiopin_dev *dev=filp->private_data;
printk("wx debug Start ioctl function\n");
printk("wx debug ioctl input cmd = %d\n",cmd);
switch (cmd){
case IOCTL_OUNPUT_MOD :
//struct UsrMesg MyUsrMesg;
ret = copy_from_user(&MyUsrMesg,(struct UsrMesg __user*)arg,sizeof(MyUsrMesg));
if(ret)
break;
ret = gpiod_direction_output(dev->gpiod,MyUsrMesg.gpioval);
if(ret < 0){
printk("can't set gpio output mode\n");
break;
}
break;
case IOCTL_INPUT_MOD :
ret = gpiod_direction_input(dev->gpiod);
if(ret < 0){
printk("can't set gpio input mode\n");
}
break;
default :
printk("Input Error Cmd");
return -EINVAL ;
}
return 0;
}
static struct file_operations gpiopin_fops = {
.owner = THIS_MODULE,
.open = gpiopin_open,
.read = gpiopin_read,
.write = gpiopin_write,
.unlocked_ioctl = gpiopin_ioctl,
};
static int gpiopin_probe(struct platform_device *pdev)
{
int ret = 0, index = 0;
const char *state;
struct fwnode_handle *child;
struct device *dev = &pdev->dev; //pointer device
printk("WX debug rk-gpio start probe\n");
if(DevMajNum){
register_chrdev_region(DevMajNum,MAX_GPIO_PIN_COUNT,GPIOPIN_NAME);
}else{ //auto allcate device id
alloc_chrdev_region(&DevMajNum,0,MAX_GPIO_PIN_COUNT,GPIOPIN_NAME);
}
printk("wx debug gpiopin major=%d\n",DevMajNum);
//create class
gpiopin_class= class_create(THIS_MODULE,GPIOPIN_NAME);
if(IS_ERR(gpiopin_class)){
return PTR_ERR(gpiopin_class);
}
printk("WX debug gpiopin class create succssfully\n");
device_for_each_child_node(dev, child) {
gpiopin[index].gpiostate = GPIO_OUT_HIGH;
/*init cdev*/
gpiopin[index].cdev.owner = THIS_MODULE;
cdev_init(&gpiopin[index].cdev,&gpiopin_fops);
ret = cdev_add(&gpiopin[index].cdev,MKDEV(MAJOR(DevMajNum),index),1);
if(ret < 0 )
printk("wx debug cdev add failed\n");
/*obtain gpio resource*/
gpiopin[index].gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
GPIOD_ASIS,NULL);
if (IS_ERR(gpiopin[index].gpiod)) {
fwnode_handle_put(child);
return PTR_ERR(gpiopin[index].gpiod);
}
printk("wx debug request gpio source\n");
/*obtain device tree default-state attribute*/
if (!fwnode_property_read_string(child, "default-state",
&state)) {
if (!strcmp(state, "on"))
gpiopin[index].gpiostate = GPIO_OUT_HIGH;
else
gpiopin[index].gpiostate = GPIO_OUT_LOW;
}
printk("wx debug gpiopin[%d].gpiostate = %d",index,gpiopin[index].gpiostate);
ret = gpiod_direction_output(gpiopin[index].gpiod,gpiopin[index].gpiostate);
if(ret != 0){
printk("ret = %d wx debug can't set gpio!\n",ret);
}
/*create device*/
gpiopin[index].device = device_create(gpiopin_class, NULL, MKDEV(MAJOR(DevMajNum), index), NULL, "gpiopin%d", index);
if (IS_ERR(gpiopin[index].device)) {
pr_err("wx debug Failed to create device for gpiopin%d\n", index);
// 在这里处理创建失败的情况
} else {
pr_info("wx debug Device for gpiopin%d created successfully\n", index);
// 在这里处理创建成功的情况
}
printk("WX debug gpiopin class create and device bind class succssfully\n");
index ++;
}
return 0;
}
static int gpiopin_remove(struct platform_device *dev)
{
int i;
for (i =0 ; i < MAX_GPIO_PIN_COUNT ; i++){
gpiod_set_value(gpiopin[i].gpiod ,0);
device_destroy(gpiopin_class, MKDEV(MAJOR(DevMajNum), i));
cdev_del(&gpiopin[i].cdev);
}
class_destroy(gpiopin_class);
return 0;
}
static const struct of_device_id gpiopin_of_match[] = {
{ .compatible = "rk-gpio" },
{}
};
//platform driver struct
static struct platform_driver gpiopin_driver = {
.driver = {
.name = "rk-gpio", //compatible device
.of_match_table = gpiopin_of_match,
},
.probe = gpiopin_probe,
.remove = gpiopin_remove,
};
static int __init gpiopin_driver_init(void)
{
return platform_driver_register(&gpiopin_driver); //source platform driver struct
}
static void __exit gpiopin_driver_exit(void)
{
platform_driver_unregister(&gpiopin_driver);
}
module_init(gpiopin_driver_init);
module_exit(gpiopin_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wangxiang");
3、配置设备树
gpio_pins: gpio-pins{
status = "okay";
compatible = "rk-gpio";
pinctrl-0 = <&decenta_gpio_pins>;
pinctrl-1 = <&pwr_led>;
pinctrl-names = "default","default";
gpio_pin1{
gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
gpio_pin2{
gpios = <&gpio1 RK_PB7 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
gpio_pin3{
gpios = <&gpio1 RK_PD6 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
gpio_pin4{
gpios = <&gpio1 RK_PD7 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
work_led {
gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};//配置PINCTL
4、修改Makefile
vim RK3588_LINUX_SDK/kernel/drivers/leds/Makefile
在最后一行添加
obj-y += gpio-rockchip-cdev.o
5、应用层代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#define IOCTL_INPUT_MOD 3
#define IOCTL_OUTPUT_MOD 2
struct UsrMesg {
int gpioval;
};
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <gpio_device> [read | write <value> | ioctl <mode>]\n", argv[0]);
printf(" <gpio_device>: Path to the device file (e.g., /dev/pingpio)\n");
printf(" [read]: Read the current GPIO value\n");
printf(" [write <value>]: Write a value (0 or 1) to the GPIO (output mode)\n");
printf(" [ioctl <mode>]: Set the GPIO mode (2 for input, 3 for output)\n");
return 1;
}
const char *device = argv[1];
int fd = open(device, O_RDWR);
if (fd == -1) {
perror("Failed to open device");
return 1;
}
if (argc >= 3 && strcmp(argv[2], "read") == 0) {
// Read the current GPIO value using read
int value;
if (read(fd, &value, sizeof(value)) == -1) {
perror("Read failed");
close(fd);
return 1;
}
printf("Current GPIO value: %d\n", value);
} else if (argc >= 4 && strcmp(argv[2], "write") == 0) {
// Write a value to the GPIO (output mode) using write
if (argc < 4) {
printf("Usage: %s <gpio_device> write <value>\n", argv[0]);
close(fd);
return 1;
}
int value = atoi(argv[3]);
if (write(fd, &value, sizeof(value)) == -1) {
perror("Write failed");
close(fd);
return 1;
}
printf("Write %d to GPIO\n", value);
} else if (argc >= 4 && strcmp(argv[2], "ioctl") == 0) {
// Set the GPIO mode using ioctl
if (argc < 4) {
printf("Usage: %s <gpio_device> ioctl <mode>\n", argv[0]);
close(fd);
return 1;
}
struct UsrMesg usrmesg;
if (argc >=5 ){
usrmesg.gpioval = atoi(argv[4]);
}else{
usrmesg.gpioval = 0;
}
int mode = atoi(argv[3]);
printf("wx debug ioctl mode=%d\n",mode);
printf("wx debug ioctl output value=%d\n",usrmesg.gpioval);
if (ioctl(fd, mode,&usrmesg) == -1) {//ioctl 函数的第三个参数应该为结构体的地址,负责会报错 Invaild add
perror("IOCTL failed");
close(fd);
return 1;
}
printf("Set GPIO mode to %d\n", mode);
} else {
printf("Invalid command. Use 'read' to read the GPIO value, 'write <value>' to set it, or 'ioctl <mode>' to set the GPIO mode.\n");
}
close(fd);
return 0;
}
应用程序使用方法
./your_program /dev/pingpio read
./your_program /dev/pingpio write 1
./your_program /dev/pingpio ioctl 3设置GPIO为输入模式