源文件位置:kerne\lc1810\drivers\misc\akm8975.c
源代码分析:
采用模块化方法进行注册,通过module_init(akm8975_init)调用akm8975_init(void)通过i2c_add_driver(&akm8975_driver)添加akm8975的驱动到系统内核中。
akm8975_driver结构体包含以下几个函数,也就是对i2c_driver定义结构体的填充。
static struct i2c_driver akm8975_driver = {
.probe =akm8975_probe, 系统加电后初始化配置函数
.remove =akm8975_remove, 卸载函数
#ifndef CONFIG_HAS_EARLYSUSPEND 电源管理
}
akm8975_probe函数是加电后自动执行的第一个函数,该函数实现了设备启动配电后模块注册到系统内核的过程,对相关的寄存器及要使用的工作队列进行初始化。
1、首先通过client->dev.platform_data判断用户定义的私有数据是否为空。这个platform_data是在"设备"驱动取得的。
2、通过i2c_check_functionality(client->adapter, I2C_FUNC_I2C)验证i2c适配器功能是否正常。
3、通过函数kzalloc(sizeof(struct akm8975_data), GFP_KERNEL);对akm8975_data module data分配内存空间及清零。
4、通过akm->pdata = client->dev.platform_data;把设备的数据赋给新分配的空间。
5、INIT_WORK(&akm->work,akm_work_func);在akm结构体中创建一个work的结构体变量,往这个结构体变量中添加处理函数入口地址akm_work_func。
static voidakm_work_func(struct work_struct *work)
{ enable_irq(akm->this_client->irq);}开中断检测中断事件准备处理中断。
6、err = akm8975_power_on(akm);加电
7、akm8975_init_client(client);初始化设备。首先通过i2c得到设备数据地址,请求中断。通过函数init_waitqueue_head初始化队列头,添加中断的事件到队列中。互斥设置所有标志位为1.设备初始化完成。
8、声明input设备。Input_dev封装在akm8975_data变量当中,调用akm->input_dev = input_allocate_device();对其需要分配空间,成功后调用set_bit(EV_ABS,akm->input->dev)对其声明上报数据类型.
调用input_set_abs_params()函数设置上报数据的范围。完成后利用函数input_register_device(akm->input_dev);注册输入设备。
9、err = misc_register(&akmd_device);
err = misc_register(&akm_aot_device);注册两个misc设备akmd_device和akm_ato_device.
两个设备的定义如下:
static struct miscdeviceakm_aot_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "akm8975_aot",
.fops = &akm_aot_fops,
};
static struct miscdevice akmd_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "akm8975_dev",
.fops = &akmd_fops,
};这里注册了两个misc设备,具体作用还没有搞明白,一个通过akm_aot_ioctl,主要是获取和设置sensor状态(打开和关闭)和延时,另外一个通过akmd_ioctl实现数据与内核层的写入和读取。
阅读akm_aot_ioctl代码,ECS_IOCTL_APP_SET_MVFLAG,表示设置原始的磁场矢量标志符。调用copy_from_user将参数argp拷贝到flag中.设置完成后,在下一个开关语句中,直接赋值实现设置标识位为相应的状态,例如:
caseECS_IOCTL_APP_SET_MVFLAG:
mv_flag = flag;
break;
阅读akmd_ioctl()函数。首先分析ECS_IOCTL_READ,分别在以下三个开关函数中实现read功能。首先,判断参数有效后,调用copy_from_user将参数argp拷贝到rwbuf。然后,判断rwbuf区域没有越界后,调用ret = akm8975_i2c_rxdata(akm, &rwbuf[1], rwbuf[0])读数据。最后,调用copy_to_user将rwbuf拷贝到argp参数。 ECS_IOCTL_READ,ECS_IOCTL_WRITE主要是数据的传输过程,在extenel层中获取数据的方式包括从akm8975的寄存器或者是eprom上,在AK8975Driver.c文件中调用。
在akmd_ioctl()函数中,case ECS_IOCTL_SET_YPR:调用copy_from_user(&value, argp, sizeof(value))将参数argp拷贝到value中。在第二个开关中调用akm8975_ecs_report_value(akm, value);
上报点给上层。/* Report magnetic sensor information */
if (m_flag) {
input_report_abs(data->input_dev, ABS_RX, rbuf[0]);
input_report_abs(data->input_dev, ABS_RY, rbuf[1]);
input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);
input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]);
}
case ECS_IOCTL_GET_OPEN_STATUS:
wait_event_interruptible(open_wq,
(atomic_read(&open_flag) != 0));
status = atomic_read(&open_flag);
break;
源代码定义了等待队列,staticDECLARE_WAIT_QUEUE_HEAD(open_wq);当cmd为ECS_IOCTL_GET_OPEN_STATUS时,读取状态,等待中断发生,有中断时把事件写入open_wq,放到队列中等待处理。
另外经过网上查资料,ECS_IOCTL_GET_TEST, ECS_IOCTL_SET_TEST, ECS_IOCTL_GET_TEST_RESULT, ECS_IOCTL_SET_TEST_RESULT,控制命令,这些命令对应的应该主要是原厂提供的算法库。
10、err = device_create_file(&client->dev, &dev_attr_akm_ms1);使用这个函数时要引用 device_create所返回的device*指针,作用是在/sys/class/下创建一个属性文件,从而通过对这个属性文件进行读写就能完成对应的数据操作。http://www.hovercool.com/en/Special:Print?topic=Class_create%2C_device_create%2C_device_create_file#a_.E4.B8.89.E3.80.81device_create_file
在代码中定义了一个static DEVICE_ATTR(akm_ms1, S_IWUSR | S_IRUGO, akm8975_show, akm8975_store); 定义了一个akm8975_show读方法和写方法km8975_store。然后利用device_create_file创建一个属性文件,通过属性文件进行读写完成对设备的属性操作。
static ssize_t akm8975_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client,
AK8975_REG_CNTL));//Operation mode setting
}
static ssize_t akm8975_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
strict_strtoul(buf, 10, &val);
if (val > 0xff)
return -EINVAL;
i2c_smbus_write_byte_data(client, AK8975_REG_CNTL, val);//write Operation mode
return count;
}
11、电源函数初始化。
akm->early_suspend.suspend = akm8975_early_suspend;
akm->early_suspend.resume = akm8975_early_resume;
register_early_suspend(&akm->early_suspend);