AXP233驱动调试记录
问题描述
遇到的最关键的问题就是:
AXP233是挂在了I2C-0的设备节点上,因为现在的V5的限制,I2C-0这个设备节点,无法在应用层直接操作。
解决办法:
只能开发一个内核驱动,在驱动中抛出应用层可以操作的接口,应用层调用这个接口,这样就可以曲线救国进行控制AXP233了。
具体修改方案
linux-4.4内核的power相关的驱动位置:linux-4.4\drivers\power
power目录下有个axp目录,可见axpXXX系列的电源驱动都放在了这个目录下面,
打开之后,可以看到axp目录中有axp22x的目录,
位置:linux-4.4\drivers\power\axp\axp22x
这个axp22x的目录就是存放axp22x所有的源代码。
axp22x.h和axp22x.c就是首先要看的源代码。
目前为止axp22x中实现了axp221s、axp227、axp223三个电源管理芯片的驱动。是一个大合集。
然后就可以在这个基础上做些工作了:
1、使用misc_register来注册一个特殊的字符设备,给应用层抛出可操作的接口。
misc_device是特殊字符设备。注册驱动程序时采用misc_register函数注册,此函数中会自动创建设备节点,即设备文件。无需mknod指令创建设备文件。因为misc_register()会调用class_device_creat或者device_creat().
https://www.cnblogs.com/ggzhangxiaochao/p/12894883.html 这个解释的挺好的
我主要添加的代码:
/********************************************************* * 20230920 zh add * 控制AXP233电源管理芯片,为应用层程序提供控制接口 **********************************************************/ static ssize_t axp233_ctrl_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { char my_data[] = "Hello from Kernel!\n"; size_t len = strlen(my_data); if (*ppos >= len) return 0; if (count > len - *ppos) count = len - *ppos; if (copy_to_user(buf, my_data + *ppos, count)) { return -EFAULT; } *ppos += count; return count; } static ssize_t axp233_ctrl_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char user_data[256]; if (count >= sizeof(user_data)) { return -EINVAL; } if (copy_from_user(user_data, buf, count)) { return -EFAULT; } user_data[count] = '\0'; printk("[zh] recv from User user_data: %s\n", user_data); printk("[zh] recv from User count: %d\n", count); if(strcasecmp(user_data, "power_off") == 0){ printk("[zh] 关机 \n"); // axp22x_power_off(); pr_info("[axp] off zhenghui ############!\n"); axp_regmap_set_bits(axp22x_pm_power->regmap, AXP22X_OFF_CTL, 0x80); } return count; } static const struct file_operations axp233_ctrl_fops = { .owner = THIS_MODULE, .read = axp233_ctrl_read, .write = axp233_ctrl_write, }; static struct miscdevice axp233_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "axp233_misc_device", .fops = &axp233_ctrl_fops, }; static int axp233_ctrl_init(void) { int ret = misc_register(&axp233_misc_device); if (ret) { pr_err("[zh]Failed to register misc device\n"); return ret; } pr_info("[zh]Misc device registered: %s\n", axp233_misc_device.name); return 0; } static void axp233_ctrl_exit(void) { misc_deregister(&axp233_misc_device); pr_info("[zh]Misc device unregistered\n"); } // end
根据AXP233手册里描述,关机调用需要给寄存器REG32H[7]写入1,即可操作AXP233关机
根据axp22x.c代码的风格,里面写使用的是axp_regmap_set_bits函数,读数据使用的是axp_regmap_read函数
而且在axp22x.h头文件中定义了:
#define AXP22X_OFF_CTL (0x32)
所以可以模仿着写,即可:
axp_regmap_set_bits(axp22x_pm_power->regmap, AXP22X_OFF_CTL, 0x80);
===== 应用层 =====
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> int main() { int fd; char buffer[256]; ssize_t bytes_read; fd = open("/dev/axp233_misc_device", O_RDWR); if (fd == -1) { perror("打开 axp233_misc_device 失败 \n"); return 1; } printf("######## #### #### #### #### \n"); printf("正在读数据:\n"); bytes_read = read(fd, buffer, sizeof(buffer)); if (bytes_read == -1) { perror("读数据失败 \n"); close(fd); return 1; } buffer[bytes_read] = '\0'; printf("读取到的数据: %s\n", buffer); printf("######## #### #### #### #### \n"); printf("######## #### #### #### #### \n"); printf("正在写数据:\n"); const char *data_to_send = "power_off"; ssize_t bytes_written = write(fd, data_to_send, strlen(data_to_send)); if (bytes_written == -1) { perror("写数据失败 \n"); } else { printf("写数据: %s \n", data_to_send); } close(fd); printf("######## #### #### #### #### \n"); return 0; }
===== 然后就可以进行交互了 =====
root@xxx:/mnt/appslog# root@xxxx:/mnt/appslog# ./my_app ######## #### #### #### #### 正在读数据: 读取到的数据: Hello from Kernel! ######## #### #### #### #### ######## #### #### #### #### 正 87.297512] [zh] recv from User user_data: power_off [ 87.316995] [zh] recv from User count: 9 据: [ 87.321409] sunxi_i2s_preapre,SNDRV_PCM_STATE_XRUN:playback xrun. [ 87.321409] [zh] 关机 [ 87.332323] [axp] off zhenghui ############!