想自己读取mx31-pdk板子的电压值,以便能进行电压的显示。
该板子的电源控制芯片为飞思卡尔自己的13783,其开发包中已带了电源管理的驱动,这里首先略微分析一下该驱动代码,然后搞明白应该如何来读取该电压值。
首先要找到关于电压控制的驱动代码,经过寻找发现有关电压控制的驱动代码的存放路径为/drivers/mxc/pmic/mc13783/pmic_battery.c,浏览该代码可以发现
一.关于驱动的注册的代码
在pmic_battery.c的最底部有以下代码:
static int __init pmic_battery_init(void)
{
pr_debug("PMIC Battery driver loading.../n");
return platform_driver_register(&pmic_battery_driver_ldm);
}
static void __exit pmic_battery_exit(void)
{
platform_driver_unregister(&pmic_battery_driver_ldm);
pr_debug("PMIC Battery driver successfully unloaded/n");
}
module_init(pmic_battery_init);
module_exit(pmic_battery_exit);
MODULE_DESCRIPTION("pmic_battery driver");
从以上代码可看出这里进行了电源管理驱动pmic_battery_driver_ldm的注册,而关于这个驱动的定义同样在该文件中,定义为:
static struct platform_driver pmic_battery_driver_ldm = {
.driver = {
.name = "pmic_battery",
.bus = &platform_bus_type,
},
.suspend = pmic_battery_suspend,
.resume = pmic_battery_resume,
.probe = pmic_battery_probe,
.remove = pmic_battery_remove,
};
其中pmic_battery_probe是该驱动注册时要调用的回调函数,这个函数很重要,在这个函数里可以进行设备device的保存,以及资源的获得,以及其他的。
二.Driver驱动pmic_battery_probe回调函数阅读
static int pmic_battery_probe(struct platform_device *pdev)
{
int ret = 0;
struct class_device *temp_class;
pmic_battery_major = register_chrdev(0, PMIC_BATTERY_STRING,
&pmic_battery_fops);
if (pmic_battery_major < 0) {
printk(KERN_ERR "Unable to get a major for pmic_battery/n");
return pmic_battery_major;
}
init_waitqueue_head(&suspendq);
pmic_battery_class = class_create(THIS_MODULE, PMIC_BATTERY_STRING);
if (IS_ERR(pmic_battery_class)) {
printk(KERN_ERR "Error creating PMIC battery class./n");
ret = PTR_ERR(pmic_battery_class);
goto err_out1;
}
temp_class = class_device_create(pmic_battery_class, NULL,
MKDEV(pmic_battery_major, 0),
NULL, PMIC_BATTERY_STRING);
if (IS_ERR(temp_class)) {
printk(KERN_ERR "Error creating PMIC battery class device./n");
ret = PTR_ERR(temp_class);
goto err_out2;
}
pmic_batt_led_control(true);
pmic_batt_set_5k_pull(true);
printk(KERN_INFO "PMIC Battery successfully probed/n");
return ret;
err_out2:
class_destroy(pmic_battery_class);
err_out1:
unregister_chrdev(pmic_battery_major, PMIC_BATTERY_STRING);
return ret;
}
其中在mc13783/pmic_battery_defs.h有关于字符串的定义:
#define PMIC_BATTERY_STRING "pmic_battery"
可以看出,register_chrdev(0, PMIC_BATTERY_STRING, &pmic_battery_fops);注册了一个字符设备,名字为pmic_battery,同时指定了它的fileoperations结构体为pmic_battery_fops,关于该结构体定义同样子该c文件中,如下:
static struct file_operations pmic_battery_fops = {
.owner = THIS_MODULE,
.ioctl = pmic_battery_ioctl,
.open = pmic_battery_open,
.release = pmic_battery_release,
};
可以看出该设备驱动的用户层操作接口只有三个,还包括打开和关闭的两个必须函数,真正进行操作的函数只有pmic_battery_ioctl一个函数。
三.设备驱动pmic_battery_ioctl函数阅读
static int pmic_battery_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
t_charger_setting *chgr_setting = NULL;
unsigned short c_current;
unsigned int bc_info;
t_eol_setting *eol_setting;
if (_IOC_TYPE(cmd) != 'p')
return -ENOTTY;
switch (cmd) {
case PMIC_BATT_CHARGER_CONTROL:
if ((chgr_setting = kmalloc(sizeof(t_charger_setting),
GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
sizeof(t_charger_setting))) {
kfree(chgr_setting);
return -EFAULT;
}
if (chgr_setting->on != false) {
CHECK_ERROR_KFREE(pmic_batt_enable_charger
(chgr_setting->chgr,
chgr_setting->c_voltage,
chgr_setting->c_current),
(kfree(chgr_setting)));
} else {
CHECK_ERROR(pmic_batt_disable_charger
(chgr_setting->chgr));
}
kfree(chgr_setting);
break;
case PMIC_BATT_SET_CHARGER:
if ((chgr_setting = kmalloc(sizeof(t_charger_setting),
GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
sizeof(t_charger_setting))) {
kfree(chgr_setting);
return -EFAULT;
}
CHECK_ERROR_KFREE(pmic_batt_set_charger(chgr_setting->chgr,
chgr_setting->c_voltage,
chgr_setting->
c_current),
(kfree(chgr_setting)));
kfree(chgr_setting);
break;
case PMIC_BATT_GET_CHARGER:
if ((chgr_setting = kmalloc(sizeof(t_charger_setting),
GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
sizeof(t_charger_setting))) {
kfree(chgr_setting);
return -EFAULT;
}
CHECK_ERROR_KFREE(pmic_batt_get_charger_setting
(chgr_setting->chgr, &chgr_setting->c_voltage,
&chgr_setting->c_current),
(kfree(chgr_setting)));
if (copy_to_user
((t_charger_setting *) arg, chgr_setting,
sizeof(t_charger_setting))) {
return -EFAULT;
}
kfree(chgr_setting);
break;
case PMIC_BATT_GET_CHARGER_SENSOR:
{
t_sensor_bits sensor;
pmic_get_sensors(&sensor);
if (copy_to_user
((unsigned int *)arg, &sensor.sense_chgdets,
sizeof(unsigned int)))
return -EFAULT;
break;
}
case PMIC_BATT_GET_BATTERY_VOLTAGE:
CHECK_ERROR(pmic_batt_get_batt_voltage(&c_current));
bc_info = (unsigned int)c_current * 2300 / 1023 + 2400;
if (copy_to_user((unsigned int *)arg, &bc_info,
sizeof(unsigned int)))
return -EFAULT;
break;
case PMIC_BATT_GET_BATTERY_CURRENT:
CHECK_ERROR(pmic_batt_get_batt_current(&c_current));
bc_info = (unsigned int)c_current * 5750 / 1023;
if (copy_to_user((unsigned int *)arg, &bc_info,
sizeof(unsigned int)))
return -EFAULT;
break;
case PMIC_BATT_GET_BATTERY_TEMPERATURE:
CHECK_ERROR(pmic_batt_get_batt_temperature(&c_current));
bc_info = (unsigned int)c_current;
if (copy_to_user((unsigned int *)arg, &bc_info,
sizeof(unsigned int)))
return -EFAULT;
break;
case PMIC_BATT_GET_CHARGER_VOLTAGE:
CHECK_ERROR(pmic_batt_get_charge_voltage(&c_current));
bc_info = (unsigned int)c_current * 23000 / 1023;
if (copy_to_user((unsigned int *)arg, &bc_info,
sizeof(unsigned int)))
return -EFAULT;
break;
case PMIC_BATT_GET_CHARGER_CURRENT:
CHECK_ERROR(pmic_batt_get_charge_current(&c_current));
bc_info = (unsigned int)c_current * 5750 / 1023;
if (copy_to_user((unsigned int *)arg, &bc_info,
sizeof(unsigned int)))
return -EFAULT;
break;
case PMIC_BATT_EOL_CONTROL:
if ((eol_setting = kmalloc(sizeof(t_eol_setting), GFP_KERNEL))
== NULL) {
return -ENOMEM;
}
if (copy_from_user(eol_setting, (t_eol_setting *) arg,
sizeof(t_eol_setting))) {
kfree(eol_setting);
return -EFAULT;
}
if (eol_setting->enable != false) {
CHECK_ERROR_KFREE(pmic_batt_bp_enable_eol
(eol_setting->typical),
(kfree(chgr_setting)));
} else {
CHECK_ERROR_KFREE(pmic_batt_disable_eol(),
(kfree(chgr_setting)));
}
kfree(eol_setting);
break;
case PMIC_BATT_SET_OUT_CONTROL:
CHECK_ERROR(pmic_batt_set_out_control((t_control) arg));
break;
case PMIC_BATT_SET_THRESHOLD:
CHECK_ERROR(pmic_batt_set_threshold((int)arg));
break;
case PMIC_BATT_LED_CONTROL:
CHECK_ERROR(pmic_batt_led_control((bool) arg));
break;
case PMIC_BATT_REV_SUPP_CONTROL:
CHECK_ERROR(pmic_batt_set_reverse_supply((bool) arg));
break;
case PMIC_BATT_UNREG_CONTROL:
CHECK_ERROR(pmic_batt_set_unregulated((bool) arg));
break;
default:
return -EINVAL;
}
return 0;
}
这是标准的设备驱动函数,而用户的ioctl函数原型为int ioctl(int fd, ind cmd, …);适合对设备的一些特性进行控制,其中fd就是用户程序打开设备时使用open函数返回的文件描述符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。当需要…这个参数时,把该参数的地址传递给static int pmic_battery_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)的arg,以便进行数据的回传(感觉是这样)。所以应用层只用调用标准的函数ioctl(int fd, ind cmd, …)并传递正确的cmd参数就可以了。这里就又有一个重要的事,就是分析以上代码,找出传递读出电压voltage的正确的cmd参数。
查看代码发现有如下一段:
case PMIC_BATT_GET_BATTERY_VOLTAGE:
CHECK_ERROR(pmic_batt_get_batt_voltage(&c_current));
bc_info = (unsigned int)c_current * 2300 / 1023 + 2400;
if (copy_to_user((unsigned int *)arg, &bc_info,
sizeof(unsigned int)))
return -EFAULT;
break;
其中有一函数pmic_batt_get_batt_voltage(&c_current),查看后原型如下:
PMIC_STATUS pmic_batt_get_batt_voltage(unsigned short *b_voltage)
{
t_channel channel;
unsigned short result[8];
if (suspend_flag == 1)
return PMIC_ERROR;
channel = BATTERY_VOLTAGE;
CHECK_ERROR(pmic_adc_convert(channel, result));
*b_voltage = result[0];
return PMIC_SUCCESS;
}
通过以上函数可以得出传递对了PMIC_BATT_GET_BATTERY_VOLTAGE这个cmd参数,驱动会把电压值bc_info传递给用户地址空间的(unsigned int *)arg,从而获得电压数据。
四.参数PMIC_BATT_GET_BATTERY_VOLTAGE的定义
通过查看代码,在 <asm/arch/pmic_battery.h>中有如下定义:
#define PMIC_BATT_GET_BATTERY_VOLTAGE _IOR('p', 0xcb, int)
而在/include/asm-generic/ioctl.h中有如下定义:
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
#define _IOC_READ 2U
#define _IOC(dir,type,nr,size) /
(((dir) << _IOC_DIRSHIFT) | /
((type) << _IOC_TYPESHIFT) | /
((nr) << _IOC_NRSHIFT) | /
((size) << _IOC_SIZESHIFT))
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
以上宏定义完成的对cnd参数PMIC_BATT_GET_BATTERY_VOLTAGE的定义,至此也就完成了用户空间读取电压值的所有必须元素,可以开始进行用户空间程序的编写。
五.编写程序读取电量voltage
编写一函数voltage-read.c如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/wait.h>
//#include <asm/arch/pmic_status.h>
///#include <asm/arch/pmic_battery.h>
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
#define _IOC_READ 2U
#define _IOC(dir,type,nr,size) /
(((dir) << _IOC_DIRSHIFT) | /
((type) << _IOC_TYPESHIFT) | /
((nr) << _IOC_NRSHIFT) | /
((size) << _IOC_SIZESHIFT))
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define PMIC_BATT_GET_BATTERY_VOLTAGE _IOR('p', 0xcb, int)
main()
{
unsigned int c_voltage;
int fd,status;
fd = open("/dev/pmic_battery", O_RDWR);
if (fd < 0) {
printf("/dev/pmic_battery open error!!");
return -1;
}
status = ioctl(fd, PMIC_BATT_GET_BATTERY_VOLTAGE, &c_voltage);
if (status < 0) {
printf("/dev/pmic_battery read error!!");
return -1;
}
printf("/dev/pmic_battery voltage is %d!!",c_voltage);
return 1;
}
交叉编译之后,在板子上就可以运行,并获得了int型的ad数据,经过转换就可以获得电池的电压值。