From 6ba0b70b19e2f0b5db94b35aff6596a50e8309d9 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Mon, 13 Nov 2017 19:22:13 +0200 Subject: [PATCH 01/14] [Lesson 6] mpu6050: improved driver implementation Instead of sysfs class used sysfs kobject. Files-data created at once. Reading of data is done by one function. Controlled resources on initialization failure. --- mpu6050/mpu6050.c | 250 +++++++++++++++++++++------------------------- 1 file changed, 113 insertions(+), 137 deletions(-) diff --git a/mpu6050/mpu6050.c b/mpu6050/mpu6050.c index b6ef64b..707a9fb 100644 --- a/mpu6050/mpu6050.c +++ b/mpu6050/mpu6050.c @@ -4,52 +4,70 @@ #include #include #include +#include #include "mpu6050-regs.h" +enum mpu6050_data_index { + INDEX_ACCEL_X = 0, + INDEX_ACCEL_Y, + INDEX_ACCEL_Z, + INDEX_GYRO_X, + INDEX_GYRO_Y, + INDEX_GYRO_Z, + INDEX_TEMPERATURE, + INDEX_COUNT +}; + struct mpu6050_data { struct i2c_client *drv_client; - int accel_values[3]; - int gyro_values[3]; - int temperature; + int data[INDEX_COUNT]; }; static struct mpu6050_data g_mpu6050_data; +static size_t get_attribute_index(struct kobj_attribute const *attribute); + static int mpu6050_read_data(void) { int temp; - struct i2c_client *drv_client = g_mpu6050_data.drv_client; + const struct i2c_client *drv_client = g_mpu6050_data.drv_client; if (drv_client == 0) return -ENODEV; /* accel */ - g_mpu6050_data.accel_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_XOUT_H)); - g_mpu6050_data.accel_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_YOUT_H)); - g_mpu6050_data.accel_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_ZOUT_H)); + g_mpu6050_data.data[INDEX_ACCEL_X] = + (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_XOUT_H)); + g_mpu6050_data.data[INDEX_ACCEL_Y] = + (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_YOUT_H)); + g_mpu6050_data.data[INDEX_ACCEL_Z] = + (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_ZOUT_H)); /* gyro */ - g_mpu6050_data.gyro_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_XOUT_H)); - g_mpu6050_data.gyro_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_YOUT_H)); - g_mpu6050_data.gyro_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_ZOUT_H)); + g_mpu6050_data.data[INDEX_GYRO_X] = + (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_XOUT_H)); + g_mpu6050_data.data[INDEX_GYRO_Y] = + (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_YOUT_H)); + g_mpu6050_data.data[INDEX_GYRO_Z] = + (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_ZOUT_H)); /* Temperature in degrees C = * (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ temp = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_TEMP_OUT_H)); - g_mpu6050_data.temperature = (temp + 12420 + 170) / 340; + g_mpu6050_data.data[INDEX_TEMPERATURE] = (temp + 12420 + 170) / 340; dev_info(&drv_client->dev, "sensor data read:\n"); dev_info(&drv_client->dev, "ACCEL[X,Y,Z] = [%d, %d, %d]\n", - g_mpu6050_data.accel_values[0], - g_mpu6050_data.accel_values[1], - g_mpu6050_data.accel_values[2]); + g_mpu6050_data.data[INDEX_ACCEL_X], + g_mpu6050_data.data[INDEX_ACCEL_Y], + g_mpu6050_data.data[INDEX_ACCEL_Z]); dev_info(&drv_client->dev, "GYRO[X,Y,Z] = [%d, %d, %d]\n", - g_mpu6050_data.gyro_values[0], - g_mpu6050_data.gyro_values[1], - g_mpu6050_data.gyro_values[2]); + g_mpu6050_data.data[INDEX_GYRO_X], + g_mpu6050_data.data[INDEX_GYRO_Y], + g_mpu6050_data.data[INDEX_GYRO_Z]); dev_info(&drv_client->dev, "TEMP = %d\n", - g_mpu6050_data.temperature); + g_mpu6050_data.data[INDEX_TEMPERATURE]); return 0; } @@ -114,7 +132,7 @@ MODULE_DEVICE_TABLE(i2c, mpu6050_idtable); static struct i2c_driver mpu6050_i2c_driver = { .driver = { - .name = "gl_mpu6050", + .name = "mpu6050", }, .probe = mpu6050_probe, @@ -122,79 +140,83 @@ static struct i2c_driver mpu6050_i2c_driver = { .id_table = mpu6050_idtable, }; -static ssize_t accel_x_show(struct class *class, - struct class_attribute *attr, char *buf) -{ - mpu6050_read_data(); - - sprintf(buf, "%d\n", g_mpu6050_data.accel_values[0]); - return strlen(buf); -} - -static ssize_t accel_y_show(struct class *class, - struct class_attribute *attr, char *buf) +static ssize_t data_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { + size_t index = get_attribute_index(attr); mpu6050_read_data(); - sprintf(buf, "%d\n", g_mpu6050_data.accel_values[1]); + if (index < INDEX_COUNT) + sprintf(buf, "%d\n", g_mpu6050_data.data[index]); + else + buf[0] = '\0'; return strlen(buf); } -static ssize_t accel_z_show(struct class *class, - struct class_attribute *attr, char *buf) -{ - mpu6050_read_data(); +static const struct kobj_attribute g_kobj_attributes[INDEX_COUNT] = { + __ATTR(accel_x, S_IRUGO, data_show, NULL), + __ATTR(accel_y, S_IRUGO, data_show, NULL), + __ATTR(accel_z, S_IRUGO, data_show, NULL), + __ATTR(gyro_x, S_IRUGO, data_show, NULL), + __ATTR(gyro_y, S_IRUGO, data_show, NULL), + __ATTR(gyro_z, S_IRUGO, data_show, NULL), + __ATTR(temperature, S_IRUGO, data_show, NULL) +}; - sprintf(buf, "%d\n", g_mpu6050_data.accel_values[2]); - return strlen(buf); -} +static const struct attribute *g_attributes[INDEX_COUNT + 1] = { + &g_kobj_attributes[INDEX_ACCEL_X].attr, + &g_kobj_attributes[INDEX_ACCEL_Y].attr, + &g_kobj_attributes[INDEX_ACCEL_Z].attr, + &g_kobj_attributes[INDEX_GYRO_X].attr, + &g_kobj_attributes[INDEX_GYRO_Y].attr, + &g_kobj_attributes[INDEX_GYRO_Z].attr, + &g_kobj_attributes[INDEX_TEMPERATURE].attr, + NULL, +}; -static ssize_t gyro_x_show(struct class *class, - struct class_attribute *attr, char *buf) +// Gets a number of attributes + 1 (null-terminated) +static size_t const attributes_count(void) { - mpu6050_read_data(); - - sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[0]); - return strlen(buf); + #if 1 + size_t const count = INDEX_COUNT + 1; + #else + size_t const full_size = sizeof(g_attributes); + static size_t const count = full_size ? full_size / sizeof(g_attributes[0]) : 0; + #endif + return count; } -static ssize_t gyro_y_show(struct class *class, - struct class_attribute *attr, char *buf) +// Gets index of attribute in common array of data +static size_t get_attribute_index(struct kobj_attribute const *attribute) { - mpu6050_read_data(); - - sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[1]); - return strlen(buf); + size_t const count = attributes_count(); + size_t index = 0; + if (count) + { + index = (size_t)(attribute - &g_kobj_attributes[0]); + if (index >= count) + { + pr_err("%s wrong computing of data index, corrected: %ld -> %ld\n", + THIS_MODULE->name, (long)index, (long)(count - 1)); + index = count - 1; + } + } + return index; } -static ssize_t gyro_z_show(struct class *class, - struct class_attribute *attr, char *buf) -{ - mpu6050_read_data(); +// Module initialization +static struct kobject *g_kobject = NULL; - sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[2]); - return strlen(buf); -} - -static ssize_t temp_show(struct class *class, - struct class_attribute *attr, char *buf) +static void free_sysfs(void) { - mpu6050_read_data(); + if (g_kobject) { + sysfs_remove_files(g_kobject, g_attributes); + kobject_put(g_kobject); - sprintf(buf, "%d\n", g_mpu6050_data.temperature); - return strlen(buf); + pr_info("mpu6050: sysfs data destroyed\n"); + } } -CLASS_ATTR(accel_x, 0444, &accel_x_show, NULL); -CLASS_ATTR(accel_y, 0444, &accel_y_show, NULL); -CLASS_ATTR(accel_z, 0444, &accel_z_show, NULL); -CLASS_ATTR(gyro_x, 0444, &gyro_x_show, NULL); -CLASS_ATTR(gyro_y, 0444, &gyro_y_show, NULL); -CLASS_ATTR(gyro_z, 0444, &gyro_z_show, NULL); -CLASS_ATTR(temperature, 0444, &temp_show, NULL); - -static struct class *attr_class; - static int mpu6050_init(void) { int ret; @@ -207,79 +229,33 @@ static int mpu6050_init(void) } pr_info("mpu6050: i2c driver created\n"); - /* Create class */ - attr_class = class_create(THIS_MODULE, "mpu6050"); - if (IS_ERR(attr_class)) { - ret = PTR_ERR(attr_class); - pr_err("mpu6050: failed to create sysfs class: %d\n", ret); - return ret; + g_kobject = kobject_create_and_add("mpu6050", kernel_kobj); + if (!g_kobject) { + ret = -ENOMEM; + pr_err("mpu6050: failed to create sysfs kobject: %d\n", ret); + goto error1; } - pr_info("mpu6050: sysfs class created\n"); - /* Create accel_x */ - ret = class_create_file(attr_class, &class_attr_accel_x); - if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute accel_x: %d\n", ret); - return ret; - } - /* Create accel_y */ - ret = class_create_file(attr_class, &class_attr_accel_y); - if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute accel_y: %d\n", ret); - return ret; - } - /* Create accel_z */ - ret = class_create_file(attr_class, &class_attr_accel_z); - if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute accel_z: %d\n", ret); - return ret; - } - /* Create gyro_x */ - ret = class_create_file(attr_class, &class_attr_gyro_x); + ret = sysfs_create_files(g_kobject, g_attributes); if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute gyro_x: %d\n", ret); - return ret; - } - /* Create gyro_y */ - ret = class_create_file(attr_class, &class_attr_gyro_y); - if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute gyro_y: %d\n", ret); - return ret; - } - /* Create gyro_z */ - ret = class_create_file(attr_class, &class_attr_gyro_z); - if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute gyro_z: %d\n", ret); - return ret; + pr_err("mpu6050: failed to create sysfs data attributes: %d\n", ret); + goto error2; } - /* Create temperature */ - ret = class_create_file(attr_class, &class_attr_temperature); - if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute temperature: %d\n", ret); - return ret; - } - - pr_info("mpu6050: sysfs class attributes created\n"); + pr_info("mpu6050: sysfs data attributes created\n"); pr_info("mpu6050: module loaded\n"); return 0; + +error2: + free_sysfs(); +error1: + i2c_del_driver(&mpu6050_i2c_driver); + return ret; } static void mpu6050_exit(void) { - if (attr_class) { - class_remove_file(attr_class, &class_attr_accel_x); - class_remove_file(attr_class, &class_attr_accel_y); - class_remove_file(attr_class, &class_attr_accel_z); - class_remove_file(attr_class, &class_attr_gyro_x); - class_remove_file(attr_class, &class_attr_gyro_y); - class_remove_file(attr_class, &class_attr_gyro_z); - class_remove_file(attr_class, &class_attr_temperature); - pr_info("mpu6050: sysfs class attributes removed\n"); - - class_destroy(attr_class); - pr_info("mpu6050: sysfs class destroyed\n"); - } + free_sysfs(); i2c_del_driver(&mpu6050_i2c_driver); pr_info("mpu6050: i2c driver deleted\n"); @@ -290,7 +266,7 @@ static void mpu6050_exit(void) module_init(mpu6050_init); module_exit(mpu6050_exit); -MODULE_AUTHOR("Andriy.Khulap "); +MODULE_AUTHOR("Andriy.Khulap +Sergii.Romantsov"); MODULE_DESCRIPTION("mpu6050 I2C acc&gyro"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.2"); From b0ee4c41f45966988fb42d0e510ed46b70ca535c Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Tue, 14 Nov 2017 20:02:13 +0200 Subject: [PATCH 02/14] [Lesson 6] mpu6050: added data storage. Added functionality to hold 10 last reads of data from mpu6050 device. --- mpu6050/mpu6050.c | 82 ++++++++++++++-------------- mpu6050/mpu6050_data.h | 119 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 42 deletions(-) create mode 100644 mpu6050/mpu6050_data.h diff --git a/mpu6050/mpu6050.c b/mpu6050/mpu6050.c index 707a9fb..fdb8635 100644 --- a/mpu6050/mpu6050.c +++ b/mpu6050/mpu6050.c @@ -5,70 +5,65 @@ #include #include #include +#include #include "mpu6050-regs.h" +#include "mpu6050_data.h" - -enum mpu6050_data_index { - INDEX_ACCEL_X = 0, - INDEX_ACCEL_Y, - INDEX_ACCEL_Z, - INDEX_GYRO_X, - INDEX_GYRO_Y, - INDEX_GYRO_Z, - INDEX_TEMPERATURE, - INDEX_COUNT -}; - -struct mpu6050_data { - struct i2c_client *drv_client; - int data[INDEX_COUNT]; -}; - -static struct mpu6050_data g_mpu6050_data; +#define ELEMENTS_COUNT 10 +static struct mpu6050_data_holder g_mpu6050_data; static size_t get_attribute_index(struct kobj_attribute const *attribute); -static int mpu6050_read_data(void) +static int mpu6050_read_data(bool debug) { int temp; const struct i2c_client *drv_client = g_mpu6050_data.drv_client; + struct mpu6050_data_elements element; if (drv_client == 0) return -ENODEV; /* accel */ - g_mpu6050_data.data[INDEX_ACCEL_X] = + element.data[INDEX_ACCEL_X] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_XOUT_H)); - g_mpu6050_data.data[INDEX_ACCEL_Y] = + element.data[INDEX_ACCEL_Y] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_YOUT_H)); - g_mpu6050_data.data[INDEX_ACCEL_Z] = + element.data[INDEX_ACCEL_Z] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_ZOUT_H)); /* gyro */ - g_mpu6050_data.data[INDEX_GYRO_X] = + element.data[INDEX_GYRO_X] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_XOUT_H)); - g_mpu6050_data.data[INDEX_GYRO_Y] = + element.data[INDEX_GYRO_Y] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_YOUT_H)); - g_mpu6050_data.data[INDEX_GYRO_Z] = + element.data[INDEX_GYRO_Z] = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_ZOUT_H)); /* Temperature in degrees C = * (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ temp = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_TEMP_OUT_H)); - g_mpu6050_data.data[INDEX_TEMPERATURE] = (temp + 12420 + 170) / 340; - - dev_info(&drv_client->dev, "sensor data read:\n"); - dev_info(&drv_client->dev, "ACCEL[X,Y,Z] = [%d, %d, %d]\n", - g_mpu6050_data.data[INDEX_ACCEL_X], - g_mpu6050_data.data[INDEX_ACCEL_Y], - g_mpu6050_data.data[INDEX_ACCEL_Z]); - dev_info(&drv_client->dev, "GYRO[X,Y,Z] = [%d, %d, %d]\n", - g_mpu6050_data.data[INDEX_GYRO_X], - g_mpu6050_data.data[INDEX_GYRO_Y], - g_mpu6050_data.data[INDEX_GYRO_Z]); - dev_info(&drv_client->dev, "TEMP = %d\n", - g_mpu6050_data.data[INDEX_TEMPERATURE]); - + element.data[INDEX_TEMPERATURE] = (temp + 12420 + 170) / 340; + + // Extra data + element.extra_data[INDEX_TIMESTAMP] = jiffies_to_msecs(get_jiffies_64()); + + add_mpu6050_element(&g_mpu6050_data, &element); + + if (debug) { + dev_info(&drv_client->dev, "sensor data read:\n"); + dev_info(&drv_client->dev, "ACCEL[X,Y,Z] = [%d, %d, %d]\n", + element.data[INDEX_ACCEL_X], + element.data[INDEX_ACCEL_Y], + element.data[INDEX_ACCEL_Z]); + dev_info(&drv_client->dev, "GYRO[X,Y,Z] = [%d, %d, %d]\n", + element.data[INDEX_GYRO_X], + element.data[INDEX_GYRO_Y], + element.data[INDEX_GYRO_Z]); + dev_info(&drv_client->dev, "TEMP = %d\n", + element.data[INDEX_TEMPERATURE]); + dev_info(&drv_client->dev, "TIMESTAMP = %llu\n", + element.extra_data[INDEX_TIMESTAMP]); + } return 0; } @@ -144,10 +139,10 @@ static ssize_t data_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { size_t index = get_attribute_index(attr); - mpu6050_read_data(); + mpu6050_read_data(true); - if (index < INDEX_COUNT) - sprintf(buf, "%d\n", g_mpu6050_data.data[index]); + if (index < INDEX_COUNT && g_mpu6050_data.element_iter_current) + sprintf(buf, "%d\n", g_mpu6050_data.element_iter_current->data.data[index]); else buf[0] = '\0'; return strlen(buf); @@ -243,6 +238,8 @@ static int mpu6050_init(void) } pr_info("mpu6050: sysfs data attributes created\n"); + init_mpu6050_data(&g_mpu6050_data, ELEMENTS_COUNT); + pr_info("mpu6050: module loaded\n"); return 0; @@ -255,6 +252,7 @@ static int mpu6050_init(void) static void mpu6050_exit(void) { + free_mpu6050_data(&g_mpu6050_data); free_sysfs(); i2c_del_driver(&mpu6050_i2c_driver); diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h new file mode 100644 index 0000000..0de5738 --- /dev/null +++ b/mpu6050/mpu6050_data.h @@ -0,0 +1,119 @@ +#ifndef __MPU6050_DATA_H__ +#define __MPU6050_DATA_H__ + +#include + + +enum mpu6050_data_index { + INDEX_ACCEL_X = 0, + INDEX_ACCEL_Y, + INDEX_ACCEL_Z, + INDEX_GYRO_X, + INDEX_GYRO_Y, + INDEX_GYRO_Z, + INDEX_TEMPERATURE, + INDEX_COUNT, + /* Another level of data*/ + INDEX_TIMESTAMP = 0, + INDEX_EXTRA_COUNT +}; + +struct mpu6050_data_elements { + int data[INDEX_COUNT]; + u64 extra_data[INDEX_EXTRA_COUNT]; +}; + +struct mpu6050_data_list { + struct list_head list; + struct mpu6050_data_elements data; +}; + +struct mpu6050_data_holder { + struct i2c_client *drv_client; + size_t elements_count; + struct mpu6050_data_list *element_iter_current; + struct mpu6050_data_list list; +}; + +static void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count) +{ + size_t const element_size = sizeof(struct mpu6050_data_list); + if (!data) + return; + data->elements_count = 0; + data->element_iter_current = NULL; + + INIT_LIST_HEAD(&data->list.list); + + do { + struct mpu6050_data_list *element = kzalloc(element_size, GFP_KERNEL); + + pr_info("%s %s: element %p\n", + THIS_MODULE->name, __FUNCTION__, element); + INIT_LIST_HEAD(&element->list); + list_add(&element->list, &data->list.list); + ++data->elements_count; + } while (data->elements_count < elements_count); + + data->element_iter_current = NULL; +} + +static void free_mpu6050_data(struct mpu6050_data_holder *data) +{ + struct mpu6050_data_list *node, *tmp; + if (!data) + return; + + list_for_each_entry_safe(node, tmp, &data->list.list, list) { + pr_info("%s %s: freeing node %p", THIS_MODULE->name, __FUNCTION__, node); + list_del(&node->list); + kfree(node); + } +} + +static void add_mpu6050_element(struct mpu6050_data_holder *data, + struct mpu6050_data_elements *element) +{ + int is_last = false; + if (!data || !element) + return; + if (!data->element_iter_current) { + data->element_iter_current = + list_first_entry(&data->list.list, struct mpu6050_data_list, list); + } + else { + // check if its the last element + is_last = list_is_last(&data->element_iter_current->list, &data->list.list); + if (is_last) { + struct mpu6050_data_list *first = + list_first_entry(&data->list.list, struct mpu6050_data_list, list); + if (first) { + // move first to the last and use it + list_move_tail(&first->list, &data->list.list); + data->element_iter_current = list_next_entry( + data->element_iter_current, list); + } + } + else { + data->element_iter_current = list_next_entry( + data->element_iter_current, list); + } + } + + if (data->element_iter_current) { + memcpy( data->element_iter_current->data.data, element->data, + sizeof(data->element_iter_current->data.data)); + memcpy( data->element_iter_current->data.extra_data, element->extra_data, + sizeof(data->element_iter_current->data.extra_data)); + //pr_info("%s %s: was_last: %d next %p", THIS_MODULE->name, __FUNCTION__, + // is_last, data->element_iter_current); + } + else { + //pr_err("%s %s: data holder is not initialized properly", + // THIS_MODULE->name, __FUNCTION__); + return; + } +} + + +#endif// __MPU6050_DATA_H__ \ No newline at end of file From b1d5e7328766ff3a8f7ac523fdae0197c3e58571 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Tue, 14 Nov 2017 21:32:00 +0200 Subject: [PATCH 03/14] [Lesson 6] mpu6050: improved device-data reading. Data from device mpu6050 will be read not often than 1 time per second. --- mpu6050/mpu6050.c | 14 +++++++++++++- mpu6050/mpu6050_data.h | 9 +++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mpu6050/mpu6050.c b/mpu6050/mpu6050.c index fdb8635..d81b637 100644 --- a/mpu6050/mpu6050.c +++ b/mpu6050/mpu6050.c @@ -19,10 +19,22 @@ static int mpu6050_read_data(bool debug) { int temp; const struct i2c_client *drv_client = g_mpu6050_data.drv_client; + struct mpu6050_data_elements* current_element = + get_active_element(&g_mpu6050_data); struct mpu6050_data_elements element; + u64 msecs = jiffies_to_msecs(get_jiffies_64()); if (drv_client == 0) return -ENODEV; + if (current_element) { + msecs = msecs - current_element->extra_data[INDEX_TIMESTAMP]; + if (msecs < MSEC_PER_SEC) { + if (debug) + dev_info(&drv_client->dev, "data reading skipped: %llu\n", + msecs); + return 0; + } + } /* accel */ element.data[INDEX_ACCEL_X] = @@ -45,7 +57,7 @@ static int mpu6050_read_data(bool debug) element.data[INDEX_TEMPERATURE] = (temp + 12420 + 170) / 340; // Extra data - element.extra_data[INDEX_TIMESTAMP] = jiffies_to_msecs(get_jiffies_64()); + element.extra_data[INDEX_TIMESTAMP] = msecs; add_mpu6050_element(&g_mpu6050_data, &element); diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index 0de5738..1041fd4 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -115,5 +115,14 @@ static void add_mpu6050_element(struct mpu6050_data_holder *data, } } +struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *data) +{ + struct mpu6050_data_elements* element = NULL; + if (!data || !data->element_iter_current) + return element; + element = &data->element_iter_current->data; + return element; +} + #endif// __MPU6050_DATA_H__ \ No newline at end of file From 3c872ce6cf1e5b543c7cb475e4657403641b8ef7 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Wed, 15 Nov 2017 14:27:38 +0200 Subject: [PATCH 04/14] [Lesson 6] mpu6050: misprint in timestamp Fixed misprint in storing timestamp: it was possibility to store diff of timestamps instead of real value. --- mpu6050/mpu6050.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mpu6050/mpu6050.c b/mpu6050/mpu6050.c index d81b637..0fdaeef 100644 --- a/mpu6050/mpu6050.c +++ b/mpu6050/mpu6050.c @@ -27,11 +27,11 @@ static int mpu6050_read_data(bool debug) if (drv_client == 0) return -ENODEV; if (current_element) { - msecs = msecs - current_element->extra_data[INDEX_TIMESTAMP]; - if (msecs < MSEC_PER_SEC) { + u64 diff = msecs - current_element->extra_data[INDEX_TIMESTAMP]; + if (diff < MSEC_PER_SEC) { if (debug) dev_info(&drv_client->dev, "data reading skipped: %llu\n", - msecs); + diff); return 0; } } From e28e105840bdf4e3c80b759ef9a6c70ce7b97322 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Wed, 15 Nov 2017 14:38:38 +0200 Subject: [PATCH 05/14] [Lesson 6] mpu6050: added one-line charecter-device Added charetcer-device that allows to read mpu6050-device data in one-line format. --- mpu6050/mpu6050.c | 16 +++- mpu6050/mpu6050_cdev.h | 191 +++++++++++++++++++++++++++++++++++++++++ mpu6050/mpu6050_data.h | 5 +- 3 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 mpu6050/mpu6050_cdev.h diff --git a/mpu6050/mpu6050.c b/mpu6050/mpu6050.c index 0fdaeef..88ef24c 100644 --- a/mpu6050/mpu6050.c +++ b/mpu6050/mpu6050.c @@ -9,10 +9,14 @@ #include "mpu6050-regs.h" #include "mpu6050_data.h" +#include "mpu6050_cdev.h" +//data #define ELEMENTS_COUNT 10 static struct mpu6050_data_holder g_mpu6050_data; + +//functionality static size_t get_attribute_index(struct kobj_attribute const *attribute); static int mpu6050_read_data(bool debug) @@ -227,6 +231,7 @@ static void free_sysfs(void) static int mpu6050_init(void) { int ret; + struct cdevs_holder *cdevs = get_cdevs(); /* Create i2c driver */ ret = i2c_add_driver(&mpu6050_i2c_driver); @@ -250,11 +255,19 @@ static int mpu6050_init(void) } pr_info("mpu6050: sysfs data attributes created\n"); - init_mpu6050_data(&g_mpu6050_data, ELEMENTS_COUNT); + init_mpu6050_data(&g_mpu6050_data, ELEMENTS_COUNT, mpu6050_read_data); + + ret = init_cdevs(cdevs, &g_mpu6050_data); + if (ret) { + pr_err("mpu6050: failed to create cdevs: %d\n", ret); + goto error3; + } pr_info("mpu6050: module loaded\n"); return 0; +error3: + free_mpu6050_data(&g_mpu6050_data); error2: free_sysfs(); error1: @@ -264,6 +277,7 @@ static int mpu6050_init(void) static void mpu6050_exit(void) { + free_cdevs(get_cdevs()); free_mpu6050_data(&g_mpu6050_data); free_sysfs(); diff --git a/mpu6050/mpu6050_cdev.h b/mpu6050/mpu6050_cdev.h new file mode 100644 index 0000000..422fd92 --- /dev/null +++ b/mpu6050/mpu6050_cdev.h @@ -0,0 +1,191 @@ +#include +#include +#include + +#include "mpu6050_data.h" + + +struct cdev_instance { + unsigned int major; + dev_t dev_no; + dev_t read_all; + struct cdev *cdev; +}; + +struct cdevs_holder { + struct cdev_instance cdev_line; + struct cdev_instance cdev_full; + struct mpu6050_data_holder *data; +}; + +static int open_cdev(struct inode *, struct file *); +static int release_cdev(struct inode *, struct file *); +static ssize_t read_line(struct file *, char __user *, size_t, loff_t *); +static ssize_t read_full(struct file *, char __user *, size_t, loff_t *); + +struct file_operations g_fops_line = { + read: read_line, + open: open_cdev, + release: release_cdev, + owner: THIS_MODULE +}; + +struct file_operations g_fops_full = { + read: read_full, + open: open_cdev, + release: release_cdev, + owner: THIS_MODULE +}; + +static struct cdevs_holder *get_cdevs(void) +{ + static struct cdevs_holder g_cdevs_holder; + return &g_cdevs_holder; +} + +static struct cdev_instance *get_cdev(dev_t dev_no) +{ + struct cdevs_holder *cdevs = get_cdevs(); + struct cdev_instance *result = NULL; + if (!cdevs) + return result; + if (cdevs->cdev_line.dev_no == dev_no) + result = &cdevs->cdev_line; + else if (cdevs->cdev_full.dev_no == dev_no) + result = &cdevs->cdev_full; + return result; +} + +static void free_cdevs(struct cdevs_holder *cdevs) +{ + if (!cdevs) + return; + if (cdevs->cdev_line.cdev) + { + unregister_chrdev_region(cdevs->cdev_line.dev_no, 1); + cdev_del(cdevs->cdev_line.cdev); + cdevs->cdev_line.cdev = NULL; + } + if (cdevs->cdev_full.cdev) + { + unregister_chrdev_region(cdevs->cdev_full.dev_no, 1); + cdev_del(cdevs->cdev_full.cdev); + cdevs->cdev_full.cdev = NULL; + } + pr_info("%s %s unintialized majors %d/%d \n", THIS_MODULE->name, + __FUNCTION__, cdevs->cdev_line.major, cdevs->cdev_full.major); +} + +static int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) +{ + int result = -1; + if (!cdevs || !data) + return result; + + result = alloc_chrdev_region(&cdevs->cdev_line.dev_no, 0, 1, "mpu6050_line"); + if (result < 0) + goto error1; + cdevs->cdev_line.major = MAJOR(cdevs->cdev_line.dev_no); + cdevs->cdev_line.read_all = false; + //cdevs->cdev_line.dev = MKDEV(cdevs->cdev_line.major, 0); + cdevs->cdev_line.cdev = cdev_alloc(); + cdevs->cdev_line.cdev->ops = &g_fops_line; + cdevs->cdev_line.cdev->owner = THIS_MODULE; + result = cdev_add(cdevs->cdev_line.cdev, cdevs->cdev_line.dev_no, 1); + if (result < 0) + goto error1; + + result = alloc_chrdev_region(&cdevs->cdev_full.dev_no, 0, 1, "mpu6050_full"); + if (result < 0) + goto error1; + cdevs->cdev_full.major = MAJOR(cdevs->cdev_full.dev_no); + cdevs->cdev_line.read_all = false; + //cdevs->cdev_full.dev = MKDEV(cdevs->cdev_full.major, 0); + cdevs->cdev_full.cdev = cdev_alloc(); + cdevs->cdev_full.cdev->ops = &g_fops_full; + cdevs->cdev_full.cdev->owner = THIS_MODULE; + result = cdev_add(cdevs->cdev_full.cdev, cdevs->cdev_full.dev_no, 1); + if (result < 0) + goto error1; + + cdevs->data = data; + pr_info("%s %s intialized majors %d/%d \n", THIS_MODULE->name, __FUNCTION__, + cdevs->cdev_line.major, cdevs->cdev_full.major); + return 0; + +error1: + free_cdevs(cdevs); + return result; +} + + +static int open_cdev(struct inode *node, struct file *file) +{ + pr_info("%s %s node %p/%u\n", + THIS_MODULE->name, __FUNCTION__, node, node ? node->i_rdev : 0); + if (node) { + struct cdev_instance *cdev = get_cdev(node->i_rdev); + if (cdev) + cdev->read_all = false; + } + + return 0; +} + +static int release_cdev(struct inode *node, struct file *file) +{ + pr_info("%s %s node %p\n", + THIS_MODULE->name, __FUNCTION__, node); + return 0; +} + +static char const format_line[] = "%lu: gyro=%d:%d:%d acc=%d:%d:%d\n"; +static size_t const size_line = sizeof(format_line) + 21 + 6 * 11; + +static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +{ + unsigned long result = 0; + struct cdevs_holder *holder = get_cdevs(); + struct cdev_instance *cdev; + if (!holder && !holder->cdev_line.cdev + && !holder->data && !holder->data->read_data) + return result; + cdev = &holder->cdev_line; + if (cdev->read_all) + return result; + + holder->data->read_data(true); + if (!holder->data->element_iter_current) + return result; + + if (count >= size_line) { + char buffer[size_line]; + size_t length = 1; + struct mpu6050_data_elements *element = + &holder->data->element_iter_current->data; + int printed = snprintf(buffer, size_line, format_line, + (unsigned long)element->extra_data[INDEX_TIMESTAMP], + element->data[INDEX_GYRO_X], element->data[INDEX_GYRO_Y], + element->data[INDEX_GYRO_Z], element->data[INDEX_ACCEL_X], + element->data[INDEX_ACCEL_Y], element->data[INDEX_ACCEL_Z]); + length = printed + 1; + length = length < size_line ? length : size_line; + buffer[length - 1] = '\0'; + result = copy_to_user(buffer_to, buffer, length); + if (!result) + result = length; + cdev->read_all = true; + } + else { + //TODO: implement dyn-buffer + } + pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu \n", + THIS_MODULE->name, __FUNCTION__, + buffer_to, (long)count, (long)size_line, (long)result); + return result; +} + +static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +{ + return 0; +} diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index 1041fd4..a4c5123 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -33,15 +33,18 @@ struct mpu6050_data_holder { size_t elements_count; struct mpu6050_data_list *element_iter_current; struct mpu6050_data_list list; + int (*read_data)(bool debug); }; -static void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count) +static void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, + int (*read_data)(bool debug)) { size_t const element_size = sizeof(struct mpu6050_data_list); if (!data) return; data->elements_count = 0; data->element_iter_current = NULL; + data->read_data = read_data; INIT_LIST_HEAD(&data->list.list); From d7471028550fec93e3887a444e0e0f3a19390c67 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Sun, 19 Nov 2017 21:08:22 +0200 Subject: [PATCH 06/14] [Lesson 6] mpu6050: implementation moved from h-files Implementation from header-files (mpu6050_cdev.h, mpu6050_data.h) is moved to source-files. --- mpu6050/Makefile | 1 + mpu6050/mpu6050_cdev.c | 167 ++++++++++++++++++++++++++++++++++++++++ mpu6050/mpu6050_cdev.h | 170 +---------------------------------------- mpu6050/mpu6050_data.c | 92 ++++++++++++++++++++++ mpu6050/mpu6050_data.h | 99 ++---------------------- 5 files changed, 272 insertions(+), 257 deletions(-) create mode 100644 mpu6050/mpu6050_cdev.c create mode 100644 mpu6050/mpu6050_data.c diff --git a/mpu6050/Makefile b/mpu6050/Makefile index 2d47b2f..04ee08d 100644 --- a/mpu6050/Makefile +++ b/mpu6050/Makefile @@ -4,6 +4,7 @@ ifneq ($(KERNELRELEASE),) obj-m := mpu6050.o +mpu6050-objs := mpu6050_cdev.o mpu6050_data.o else diff --git a/mpu6050/mpu6050_cdev.c b/mpu6050/mpu6050_cdev.c new file mode 100644 index 0000000..70a1c27 --- /dev/null +++ b/mpu6050/mpu6050_cdev.c @@ -0,0 +1,167 @@ +#include "mpu6050_cdev.h" + +static int open_cdev(struct inode *, struct file *); +static int release_cdev(struct inode *, struct file *); +static ssize_t read_line(struct file *, char __user *, size_t, loff_t *); +static ssize_t read_full(struct file *, char __user *, size_t, loff_t *); + + +struct file_operations g_fops_line = { + read: read_line, + open: open_cdev, + release: release_cdev, + owner: THIS_MODULE +}; + +struct file_operations g_fops_full = { + read: read_full, + open: open_cdev, + release: release_cdev, + owner: THIS_MODULE +}; + +struct cdev_instance *get_cdev(dev_t dev_no) +{ + struct cdevs_holder *cdevs = get_cdevs(); + struct cdev_instance *result = NULL; + if (!cdevs) + return result; + if (cdevs->cdev_line.dev_no == dev_no) + result = &cdevs->cdev_line; + else if (cdevs->cdev_full.dev_no == dev_no) + result = &cdevs->cdev_full; + return result; +} + +void free_cdevs(struct cdevs_holder *cdevs) +{ + if (!cdevs) + return; + if (cdevs->cdev_line.cdev) + { + unregister_chrdev_region(cdevs->cdev_line.dev_no, 1); + cdev_del(cdevs->cdev_line.cdev); + cdevs->cdev_line.cdev = NULL; + } + if (cdevs->cdev_full.cdev) + { + unregister_chrdev_region(cdevs->cdev_full.dev_no, 1); + cdev_del(cdevs->cdev_full.cdev); + cdevs->cdev_full.cdev = NULL; + } + pr_info("%s %s unintialized majors %d/%d \n", THIS_MODULE->name, + __FUNCTION__, cdevs->cdev_line.major, cdevs->cdev_full.major); +} + +int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) +{ + int result = -1; + if (!cdevs || !data) + return result; + + result = alloc_chrdev_region(&cdevs->cdev_line.dev_no, 0, 1, "mpu6050_line"); + if (result < 0) + goto error1; + cdevs->cdev_line.major = MAJOR(cdevs->cdev_line.dev_no); + cdevs->cdev_line.read_all = false; + //cdevs->cdev_line.dev = MKDEV(cdevs->cdev_line.major, 0); + cdevs->cdev_line.cdev = cdev_alloc(); + cdevs->cdev_line.cdev->ops = &g_fops_line; + cdevs->cdev_line.cdev->owner = THIS_MODULE; + result = cdev_add(cdevs->cdev_line.cdev, cdevs->cdev_line.dev_no, 1); + if (result < 0) + goto error1; + + result = alloc_chrdev_region(&cdevs->cdev_full.dev_no, 0, 1, "mpu6050_full"); + if (result < 0) + goto error1; + cdevs->cdev_full.major = MAJOR(cdevs->cdev_full.dev_no); + cdevs->cdev_line.read_all = false; + //cdevs->cdev_full.dev = MKDEV(cdevs->cdev_full.major, 0); + cdevs->cdev_full.cdev = cdev_alloc(); + cdevs->cdev_full.cdev->ops = &g_fops_full; + cdevs->cdev_full.cdev->owner = THIS_MODULE; + result = cdev_add(cdevs->cdev_full.cdev, cdevs->cdev_full.dev_no, 1); + if (result < 0) + goto error1; + + cdevs->data = data; + pr_info("%s %s intialized majors %d/%d \n", THIS_MODULE->name, __FUNCTION__, + cdevs->cdev_line.major, cdevs->cdev_full.major); + return 0; + +error1: + free_cdevs(cdevs); + return result; +} + +static int open_cdev(struct inode *node, struct file *file) +{ + pr_info("%s %s node %p/%u\n", + THIS_MODULE->name, __FUNCTION__, node, node ? node->i_rdev : 0); + if (node) { + struct cdev_instance *cdev = get_cdev(node->i_rdev); + if (cdev) + cdev->read_all = false; + } + + return 0; +} + +static int release_cdev(struct inode *node, struct file *file) +{ + pr_info("%s %s node %p\n", + THIS_MODULE->name, __FUNCTION__, node); + return 0; +} + +static char const format_line[] = "%lu: gyro=%d:%d:%d acc=%d:%d:%d\n"; +static size_t const size_line = sizeof(format_line) + 21 + 6 * 11; + +static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +{ + unsigned long result = 0; + struct cdevs_holder *holder = get_cdevs(); + struct cdev_instance *cdev; + if (!holder && !holder->cdev_line.cdev + && !holder->data && !holder->data->read_data) + return result; + cdev = &holder->cdev_line; + if (cdev->read_all) + return result; + + holder->data->read_data(true); + if (!holder->data->element_iter_current) + return result; + + if (count >= size_line) { + char buffer[size_line]; + size_t length = 1; + struct mpu6050_data_elements *element = + &holder->data->element_iter_current->data; + int printed = snprintf(buffer, size_line, format_line, + (unsigned long)element->extra_data[INDEX_TIMESTAMP], + element->data[INDEX_GYRO_X], element->data[INDEX_GYRO_Y], + element->data[INDEX_GYRO_Z], element->data[INDEX_ACCEL_X], + element->data[INDEX_ACCEL_Y], element->data[INDEX_ACCEL_Z]); + length = printed + 1; + length = length < size_line ? length : size_line; + buffer[length - 1] = '\0'; + result = copy_to_user(buffer_to, buffer, length); + if (!result) + result = length; + cdev->read_all = true; + } + else { + //TODO: implement dyn-buffer + } + pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu \n", + THIS_MODULE->name, __FUNCTION__, + buffer_to, (long)count, (long)size_line, (long)result); + return result; +} + +static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +{ + return 0; +} diff --git a/mpu6050/mpu6050_cdev.h b/mpu6050/mpu6050_cdev.h index 422fd92..f235ffa 100644 --- a/mpu6050/mpu6050_cdev.h +++ b/mpu6050/mpu6050_cdev.h @@ -18,174 +18,12 @@ struct cdevs_holder { struct mpu6050_data_holder *data; }; -static int open_cdev(struct inode *, struct file *); -static int release_cdev(struct inode *, struct file *); -static ssize_t read_line(struct file *, char __user *, size_t, loff_t *); -static ssize_t read_full(struct file *, char __user *, size_t, loff_t *); - -struct file_operations g_fops_line = { - read: read_line, - open: open_cdev, - release: release_cdev, - owner: THIS_MODULE -}; - -struct file_operations g_fops_full = { - read: read_full, - open: open_cdev, - release: release_cdev, - owner: THIS_MODULE -}; - -static struct cdevs_holder *get_cdevs(void) +static inline struct cdevs_holder *get_cdevs(void) { static struct cdevs_holder g_cdevs_holder; return &g_cdevs_holder; } -static struct cdev_instance *get_cdev(dev_t dev_no) -{ - struct cdevs_holder *cdevs = get_cdevs(); - struct cdev_instance *result = NULL; - if (!cdevs) - return result; - if (cdevs->cdev_line.dev_no == dev_no) - result = &cdevs->cdev_line; - else if (cdevs->cdev_full.dev_no == dev_no) - result = &cdevs->cdev_full; - return result; -} - -static void free_cdevs(struct cdevs_holder *cdevs) -{ - if (!cdevs) - return; - if (cdevs->cdev_line.cdev) - { - unregister_chrdev_region(cdevs->cdev_line.dev_no, 1); - cdev_del(cdevs->cdev_line.cdev); - cdevs->cdev_line.cdev = NULL; - } - if (cdevs->cdev_full.cdev) - { - unregister_chrdev_region(cdevs->cdev_full.dev_no, 1); - cdev_del(cdevs->cdev_full.cdev); - cdevs->cdev_full.cdev = NULL; - } - pr_info("%s %s unintialized majors %d/%d \n", THIS_MODULE->name, - __FUNCTION__, cdevs->cdev_line.major, cdevs->cdev_full.major); -} - -static int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) -{ - int result = -1; - if (!cdevs || !data) - return result; - - result = alloc_chrdev_region(&cdevs->cdev_line.dev_no, 0, 1, "mpu6050_line"); - if (result < 0) - goto error1; - cdevs->cdev_line.major = MAJOR(cdevs->cdev_line.dev_no); - cdevs->cdev_line.read_all = false; - //cdevs->cdev_line.dev = MKDEV(cdevs->cdev_line.major, 0); - cdevs->cdev_line.cdev = cdev_alloc(); - cdevs->cdev_line.cdev->ops = &g_fops_line; - cdevs->cdev_line.cdev->owner = THIS_MODULE; - result = cdev_add(cdevs->cdev_line.cdev, cdevs->cdev_line.dev_no, 1); - if (result < 0) - goto error1; - - result = alloc_chrdev_region(&cdevs->cdev_full.dev_no, 0, 1, "mpu6050_full"); - if (result < 0) - goto error1; - cdevs->cdev_full.major = MAJOR(cdevs->cdev_full.dev_no); - cdevs->cdev_line.read_all = false; - //cdevs->cdev_full.dev = MKDEV(cdevs->cdev_full.major, 0); - cdevs->cdev_full.cdev = cdev_alloc(); - cdevs->cdev_full.cdev->ops = &g_fops_full; - cdevs->cdev_full.cdev->owner = THIS_MODULE; - result = cdev_add(cdevs->cdev_full.cdev, cdevs->cdev_full.dev_no, 1); - if (result < 0) - goto error1; - - cdevs->data = data; - pr_info("%s %s intialized majors %d/%d \n", THIS_MODULE->name, __FUNCTION__, - cdevs->cdev_line.major, cdevs->cdev_full.major); - return 0; - -error1: - free_cdevs(cdevs); - return result; -} - - -static int open_cdev(struct inode *node, struct file *file) -{ - pr_info("%s %s node %p/%u\n", - THIS_MODULE->name, __FUNCTION__, node, node ? node->i_rdev : 0); - if (node) { - struct cdev_instance *cdev = get_cdev(node->i_rdev); - if (cdev) - cdev->read_all = false; - } - - return 0; -} - -static int release_cdev(struct inode *node, struct file *file) -{ - pr_info("%s %s node %p\n", - THIS_MODULE->name, __FUNCTION__, node); - return 0; -} - -static char const format_line[] = "%lu: gyro=%d:%d:%d acc=%d:%d:%d\n"; -static size_t const size_line = sizeof(format_line) + 21 + 6 * 11; - -static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count, loff_t *off) -{ - unsigned long result = 0; - struct cdevs_holder *holder = get_cdevs(); - struct cdev_instance *cdev; - if (!holder && !holder->cdev_line.cdev - && !holder->data && !holder->data->read_data) - return result; - cdev = &holder->cdev_line; - if (cdev->read_all) - return result; - - holder->data->read_data(true); - if (!holder->data->element_iter_current) - return result; - - if (count >= size_line) { - char buffer[size_line]; - size_t length = 1; - struct mpu6050_data_elements *element = - &holder->data->element_iter_current->data; - int printed = snprintf(buffer, size_line, format_line, - (unsigned long)element->extra_data[INDEX_TIMESTAMP], - element->data[INDEX_GYRO_X], element->data[INDEX_GYRO_Y], - element->data[INDEX_GYRO_Z], element->data[INDEX_ACCEL_X], - element->data[INDEX_ACCEL_Y], element->data[INDEX_ACCEL_Z]); - length = printed + 1; - length = length < size_line ? length : size_line; - buffer[length - 1] = '\0'; - result = copy_to_user(buffer_to, buffer, length); - if (!result) - result = length; - cdev->read_all = true; - } - else { - //TODO: implement dyn-buffer - } - pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu \n", - THIS_MODULE->name, __FUNCTION__, - buffer_to, (long)count, (long)size_line, (long)result); - return result; -} - -static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count, loff_t *off) -{ - return 0; -} +struct cdev_instance *get_cdev(dev_t dev_no); +int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data); +void free_cdevs(struct cdevs_holder *cdevs); diff --git a/mpu6050/mpu6050_data.c b/mpu6050/mpu6050_data.c new file mode 100644 index 0000000..6f89f3e --- /dev/null +++ b/mpu6050/mpu6050_data.c @@ -0,0 +1,92 @@ +#include "mpu6050_data.h" + +void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, + int (*read_data)(bool debug)) +{ + size_t const element_size = sizeof(struct mpu6050_data_list); + if (!data) + return; + data->elements_count = 0; + data->element_iter_current = NULL; + data->read_data = read_data; + + INIT_LIST_HEAD(&data->list.list); + + do { + struct mpu6050_data_list *element = kzalloc(element_size, GFP_KERNEL); + + pr_info("%s %s: element %p\n", + THIS_MODULE->name, __FUNCTION__, element); + INIT_LIST_HEAD(&element->list); + list_add(&element->list, &data->list.list); + ++data->elements_count; + } while (data->elements_count < elements_count); + + data->element_iter_current = NULL; +} + +void free_mpu6050_data(struct mpu6050_data_holder *data) +{ + struct mpu6050_data_list *node, *tmp; + if (!data) + return; + + list_for_each_entry_safe(node, tmp, &data->list.list, list) { + pr_info("%s %s: freeing node %p", THIS_MODULE->name, __FUNCTION__, node); + list_del(&node->list); + kfree(node); + } +} + +void add_mpu6050_element(struct mpu6050_data_holder *data, + struct mpu6050_data_elements *element) +{ + int is_last = false; + if (!data || !element) + return; + if (!data->element_iter_current) { + data->element_iter_current = + list_first_entry(&data->list.list, struct mpu6050_data_list, list); + } + else { + // check if its the last element + is_last = list_is_last(&data->element_iter_current->list, &data->list.list); + if (is_last) { + struct mpu6050_data_list *first = + list_first_entry(&data->list.list, struct mpu6050_data_list, list); + if (first) { + // move first to the last and use it + list_move_tail(&first->list, &data->list.list); + data->element_iter_current = list_next_entry( + data->element_iter_current, list); + } + } + else { + data->element_iter_current = list_next_entry( + data->element_iter_current, list); + } + } + + if (data->element_iter_current) { + memcpy( data->element_iter_current->data.data, element->data, + sizeof(data->element_iter_current->data.data)); + memcpy( data->element_iter_current->data.extra_data, element->extra_data, + sizeof(data->element_iter_current->data.extra_data)); + //pr_info("%s %s: was_last: %d next %p", THIS_MODULE->name, __FUNCTION__, + // is_last, data->element_iter_current); + } + else { + //pr_err("%s %s: data holder is not initialized properly", + // THIS_MODULE->name, __FUNCTION__); + return; + } +} + +struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *data) +{ + struct mpu6050_data_elements* element = NULL; + if (!data || !data->element_iter_current) + return element; + element = &data->element_iter_current->data; + return element; +} diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index a4c5123..5bb26f2 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -2,6 +2,8 @@ #define __MPU6050_DATA_H__ #include +#include +#include enum mpu6050_data_index { @@ -36,96 +38,11 @@ struct mpu6050_data_holder { int (*read_data)(bool debug); }; -static void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, - int (*read_data)(bool debug)) -{ - size_t const element_size = sizeof(struct mpu6050_data_list); - if (!data) - return; - data->elements_count = 0; - data->element_iter_current = NULL; - data->read_data = read_data; - - INIT_LIST_HEAD(&data->list.list); - - do { - struct mpu6050_data_list *element = kzalloc(element_size, GFP_KERNEL); - - pr_info("%s %s: element %p\n", - THIS_MODULE->name, __FUNCTION__, element); - INIT_LIST_HEAD(&element->list); - list_add(&element->list, &data->list.list); - ++data->elements_count; - } while (data->elements_count < elements_count); - - data->element_iter_current = NULL; -} - -static void free_mpu6050_data(struct mpu6050_data_holder *data) -{ - struct mpu6050_data_list *node, *tmp; - if (!data) - return; - - list_for_each_entry_safe(node, tmp, &data->list.list, list) { - pr_info("%s %s: freeing node %p", THIS_MODULE->name, __FUNCTION__, node); - list_del(&node->list); - kfree(node); - } -} - -static void add_mpu6050_element(struct mpu6050_data_holder *data, - struct mpu6050_data_elements *element) -{ - int is_last = false; - if (!data || !element) - return; - if (!data->element_iter_current) { - data->element_iter_current = - list_first_entry(&data->list.list, struct mpu6050_data_list, list); - } - else { - // check if its the last element - is_last = list_is_last(&data->element_iter_current->list, &data->list.list); - if (is_last) { - struct mpu6050_data_list *first = - list_first_entry(&data->list.list, struct mpu6050_data_list, list); - if (first) { - // move first to the last and use it - list_move_tail(&first->list, &data->list.list); - data->element_iter_current = list_next_entry( - data->element_iter_current, list); - } - } - else { - data->element_iter_current = list_next_entry( - data->element_iter_current, list); - } - } - - if (data->element_iter_current) { - memcpy( data->element_iter_current->data.data, element->data, - sizeof(data->element_iter_current->data.data)); - memcpy( data->element_iter_current->data.extra_data, element->extra_data, - sizeof(data->element_iter_current->data.extra_data)); - //pr_info("%s %s: was_last: %d next %p", THIS_MODULE->name, __FUNCTION__, - // is_last, data->element_iter_current); - } - else { - //pr_err("%s %s: data holder is not initialized properly", - // THIS_MODULE->name, __FUNCTION__); - return; - } -} - -struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *data) -{ - struct mpu6050_data_elements* element = NULL; - if (!data || !data->element_iter_current) - return element; - element = &data->element_iter_current->data; - return element; -} - +void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, + int (*read_data)(bool debug)); +void free_mpu6050_data(struct mpu6050_data_holder *data); +void add_mpu6050_element(struct mpu6050_data_holder *data, + struct mpu6050_data_elements *element); +struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *data); #endif// __MPU6050_DATA_H__ \ No newline at end of file From ff9c9b711f9cfd4397738d2f349838193b070b32 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Wed, 22 Nov 2017 19:19:13 +0200 Subject: [PATCH 07/14] [Lesson 6] mpu6050: restored module loadability File with initialization-functions wasn't compiled. --- mpu6050/Makefile | 2 +- mpu6050/{mpu6050.c => mpu6050_main.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename mpu6050/{mpu6050.c => mpu6050_main.c} (100%) diff --git a/mpu6050/Makefile b/mpu6050/Makefile index 04ee08d..abbb13e 100644 --- a/mpu6050/Makefile +++ b/mpu6050/Makefile @@ -4,7 +4,7 @@ ifneq ($(KERNELRELEASE),) obj-m := mpu6050.o -mpu6050-objs := mpu6050_cdev.o mpu6050_data.o +mpu6050-objs := mpu6050_main.o mpu6050_cdev.o mpu6050_data.o else diff --git a/mpu6050/mpu6050.c b/mpu6050/mpu6050_main.c similarity index 100% rename from mpu6050/mpu6050.c rename to mpu6050/mpu6050_main.c From 9b15c7f6626b6c8191252226f065816e422bf593 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Wed, 22 Nov 2017 19:45:11 +0200 Subject: [PATCH 08/14] [Lesson 6] mpu6050: review fixes Avoided usage of c++-style comments. Added author. --- mpu6050/mpu6050_cdev.c | 4 +--- mpu6050/mpu6050_data.c | 14 ++++++++------ mpu6050/mpu6050_data.h | 2 +- mpu6050/mpu6050_main.c | 15 ++++++++------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/mpu6050/mpu6050_cdev.c b/mpu6050/mpu6050_cdev.c index 70a1c27..090aa8b 100644 --- a/mpu6050/mpu6050_cdev.c +++ b/mpu6050/mpu6050_cdev.c @@ -64,7 +64,6 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) goto error1; cdevs->cdev_line.major = MAJOR(cdevs->cdev_line.dev_no); cdevs->cdev_line.read_all = false; - //cdevs->cdev_line.dev = MKDEV(cdevs->cdev_line.major, 0); cdevs->cdev_line.cdev = cdev_alloc(); cdevs->cdev_line.cdev->ops = &g_fops_line; cdevs->cdev_line.cdev->owner = THIS_MODULE; @@ -77,7 +76,6 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) goto error1; cdevs->cdev_full.major = MAJOR(cdevs->cdev_full.dev_no); cdevs->cdev_line.read_all = false; - //cdevs->cdev_full.dev = MKDEV(cdevs->cdev_full.major, 0); cdevs->cdev_full.cdev = cdev_alloc(); cdevs->cdev_full.cdev->ops = &g_fops_full; cdevs->cdev_full.cdev->owner = THIS_MODULE; @@ -153,7 +151,7 @@ static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count cdev->read_all = true; } else { - //TODO: implement dyn-buffer + /* TODO: implement dyn-buffer */ } pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu \n", THIS_MODULE->name, __FUNCTION__, diff --git a/mpu6050/mpu6050_data.c b/mpu6050/mpu6050_data.c index 6f89f3e..5a8c7d9 100644 --- a/mpu6050/mpu6050_data.c +++ b/mpu6050/mpu6050_data.c @@ -49,13 +49,13 @@ void add_mpu6050_element(struct mpu6050_data_holder *data, list_first_entry(&data->list.list, struct mpu6050_data_list, list); } else { - // check if its the last element + /* check if its the last element */ is_last = list_is_last(&data->element_iter_current->list, &data->list.list); if (is_last) { struct mpu6050_data_list *first = list_first_entry(&data->list.list, struct mpu6050_data_list, list); if (first) { - // move first to the last and use it + /* move first to the last and use it */ list_move_tail(&first->list, &data->list.list); data->element_iter_current = list_next_entry( data->element_iter_current, list); @@ -72,12 +72,14 @@ void add_mpu6050_element(struct mpu6050_data_holder *data, sizeof(data->element_iter_current->data.data)); memcpy( data->element_iter_current->data.extra_data, element->extra_data, sizeof(data->element_iter_current->data.extra_data)); - //pr_info("%s %s: was_last: %d next %p", THIS_MODULE->name, __FUNCTION__, - // is_last, data->element_iter_current); + /* pr_info("%s %s: was_last: %d next %p", THIS_MODULE->name, __FUNCTION__, + is_last, data->element_iter_current); + */ } else { - //pr_err("%s %s: data holder is not initialized properly", - // THIS_MODULE->name, __FUNCTION__); + /* pr_err("%s %s: data holder is not initialized properly", + THIS_MODULE->name, __FUNCTION__); + */ return; } } diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index 5bb26f2..0af2111 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -45,4 +45,4 @@ void add_mpu6050_element(struct mpu6050_data_holder *data, struct mpu6050_data_elements *element); struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *data); -#endif// __MPU6050_DATA_H__ \ No newline at end of file +#endif /* __MPU6050_DATA_H__ */ \ No newline at end of file diff --git a/mpu6050/mpu6050_main.c b/mpu6050/mpu6050_main.c index 88ef24c..446e4e5 100644 --- a/mpu6050/mpu6050_main.c +++ b/mpu6050/mpu6050_main.c @@ -11,12 +11,12 @@ #include "mpu6050_data.h" #include "mpu6050_cdev.h" -//data +/* data */ #define ELEMENTS_COUNT 10 static struct mpu6050_data_holder g_mpu6050_data; -//functionality +/* functionality */ static size_t get_attribute_index(struct kobj_attribute const *attribute); static int mpu6050_read_data(bool debug) @@ -60,7 +60,7 @@ static int mpu6050_read_data(bool debug) temp = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_TEMP_OUT_H)); element.data[INDEX_TEMPERATURE] = (temp + 12420 + 170) / 340; - // Extra data + /* Extra data */ element.extra_data[INDEX_TIMESTAMP] = msecs; add_mpu6050_element(&g_mpu6050_data, &element); @@ -185,7 +185,7 @@ static const struct attribute *g_attributes[INDEX_COUNT + 1] = { NULL, }; -// Gets a number of attributes + 1 (null-terminated) +/* Gets a number of attributes + 1 (null-terminated) */ static size_t const attributes_count(void) { #if 1 @@ -197,7 +197,7 @@ static size_t const attributes_count(void) return count; } -// Gets index of attribute in common array of data +/* Gets index of attribute in common array of data */ static size_t get_attribute_index(struct kobj_attribute const *attribute) { size_t const count = attributes_count(); @@ -215,7 +215,7 @@ static size_t get_attribute_index(struct kobj_attribute const *attribute) return index; } -// Module initialization +/* Module initialization */ static struct kobject *g_kobject = NULL; static void free_sysfs(void) @@ -290,7 +290,8 @@ static void mpu6050_exit(void) module_init(mpu6050_init); module_exit(mpu6050_exit); -MODULE_AUTHOR("Andriy.Khulap +Sergii.Romantsov"); +MODULE_AUTHOR("Andriy.Khulap "); +MODULE_AUTHOR("Sergii.Romantsov "); MODULE_DESCRIPTION("mpu6050 I2C acc&gyro"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.2"); From b59194d5c8f8377198859690c1f593b0fc7d6dde Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Sat, 25 Nov 2017 13:53:46 +0200 Subject: [PATCH 09/14] [Lesson 6] mpu6050: cdev to dump last 10 reads added. Added support to dump latest 10 reads from mpu6050 by charecter device. --- mpu6050/mpu6050_cdev.c | 111 ++++++++++++++++++++++++++++++++++------- mpu6050/mpu6050_cdev.h | 8 +-- mpu6050/mpu6050_data.c | 20 +++++++- mpu6050/mpu6050_data.h | 6 ++- mpu6050/mpu6050_main.c | 6 +-- 5 files changed, 121 insertions(+), 30 deletions(-) diff --git a/mpu6050/mpu6050_cdev.c b/mpu6050/mpu6050_cdev.c index 090aa8b..0717182 100644 --- a/mpu6050/mpu6050_cdev.c +++ b/mpu6050/mpu6050_cdev.c @@ -20,6 +20,24 @@ struct file_operations g_fops_full = { owner: THIS_MODULE }; +static void read_data(struct cdevs_holder *holder) +{ + if (!holder || !holder->data || !holder->data->read_data) + { + pr_err("%s %s can't read data %p/%p \n", THIS_MODULE->name, + __FUNCTION__, holder, holder ? holder->data : NULL); + return; + } + + holder->data->read_data(true); +} + +struct cdevs_holder *get_cdevs(void) +{ + static struct cdevs_holder g_cdevs_holder; + return &g_cdevs_holder; +} + struct cdev_instance *get_cdev(dev_t dev_no) { struct cdevs_holder *cdevs = get_cdevs(); @@ -67,6 +85,7 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) cdevs->cdev_line.cdev = cdev_alloc(); cdevs->cdev_line.cdev->ops = &g_fops_line; cdevs->cdev_line.cdev->owner = THIS_MODULE; + cdevs->cdev_line.data_iter = NULL; result = cdev_add(cdevs->cdev_line.cdev, cdevs->cdev_line.dev_no, 1); if (result < 0) goto error1; @@ -79,6 +98,7 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) cdevs->cdev_full.cdev = cdev_alloc(); cdevs->cdev_full.cdev->ops = &g_fops_full; cdevs->cdev_full.cdev->owner = THIS_MODULE; + cdevs->cdev_full.data_iter = NULL; result = cdev_add(cdevs->cdev_full.cdev, cdevs->cdev_full.dev_no, 1); if (result < 0) goto error1; @@ -100,7 +120,12 @@ static int open_cdev(struct inode *node, struct file *file) if (node) { struct cdev_instance *cdev = get_cdev(node->i_rdev); if (cdev) + { cdev->read_all = false; + cdev->data_iter = NULL; + } + + read_data(get_cdevs()); } return 0; @@ -116,27 +141,15 @@ static int release_cdev(struct inode *node, struct file *file) static char const format_line[] = "%lu: gyro=%d:%d:%d acc=%d:%d:%d\n"; static size_t const size_line = sizeof(format_line) + 21 + 6 * 11; -static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +static ssize_t read_to_user_buffer(struct mpu6050_data_elements *element, + char __user *buffer_to, size_t count) { - unsigned long result = 0; - struct cdevs_holder *holder = get_cdevs(); - struct cdev_instance *cdev; - if (!holder && !holder->cdev_line.cdev - && !holder->data && !holder->data->read_data) + ssize_t result = 0; + if (!element || !buffer_to || !count) return result; - cdev = &holder->cdev_line; - if (cdev->read_all) - return result; - - holder->data->read_data(true); - if (!holder->data->element_iter_current) - return result; - if (count >= size_line) { char buffer[size_line]; size_t length = 1; - struct mpu6050_data_elements *element = - &holder->data->element_iter_current->data; int printed = snprintf(buffer, size_line, format_line, (unsigned long)element->extra_data[INDEX_TIMESTAMP], element->data[INDEX_GYRO_X], element->data[INDEX_GYRO_Y], @@ -148,11 +161,36 @@ static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count result = copy_to_user(buffer_to, buffer, length); if (!result) result = length; - cdev->read_all = true; + else + result = 0; } else { /* TODO: implement dyn-buffer */ } + return result; +} + + +static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +{ + unsigned long result = 0; + struct cdevs_holder *holder = get_cdevs(); + struct cdev_instance *cdev; + struct mpu6050_data_elements *element; + if (!holder || !holder->cdev_line.cdev + || !holder->data) + return result; + cdev = &holder->cdev_line; + if (cdev->read_all) + return result; + + if (!holder->data->element_iter_current) + return result; + + element = &holder->data->element_iter_current->data; + result = read_to_user_buffer(element, buffer_to, count); + + cdev->read_all = true; pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu \n", THIS_MODULE->name, __FUNCTION__, buffer_to, (long)count, (long)size_line, (long)result); @@ -161,5 +199,42 @@ static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count, loff_t *off) { - return 0; + unsigned long result = 0; + struct cdevs_holder *holder = get_cdevs(); + struct cdev_instance *cdev; + struct mpu6050_data_elements *element; + if (!holder || !holder->cdev_full.cdev + || !holder->data) + return result; + cdev = &holder->cdev_full; + /* all data are read */ + if (cdev->read_all) + return result; + + /* no data to get */ + if (!holder->data->element_iter_current) + return result; + + if (!cdev->data_iter) + cdev->data_iter = get_first_element(holder->data); + else + cdev->data_iter = get_next_element(cdev->data_iter); + + pr_info("%s %s element %p\n", + THIS_MODULE->name, __FUNCTION__, cdev->data_iter); + + if (!cdev->data_iter) + return result; + + element = &cdev->data_iter->data; + result = read_to_user_buffer(element, buffer_to, count); + + if (holder->data->element_iter_current == cdev->data_iter) + cdev->read_all = true; + + pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu, read_all: %s \n", + THIS_MODULE->name, __FUNCTION__, + buffer_to, (long)count, (long)size_line, (long)result, + cdev->read_all ? "yes" : "no"); + return result; } diff --git a/mpu6050/mpu6050_cdev.h b/mpu6050/mpu6050_cdev.h index f235ffa..caf7e52 100644 --- a/mpu6050/mpu6050_cdev.h +++ b/mpu6050/mpu6050_cdev.h @@ -10,6 +10,7 @@ struct cdev_instance { dev_t dev_no; dev_t read_all; struct cdev *cdev; + struct mpu6050_data_list *data_iter; }; struct cdevs_holder { @@ -18,12 +19,7 @@ struct cdevs_holder { struct mpu6050_data_holder *data; }; -static inline struct cdevs_holder *get_cdevs(void) -{ - static struct cdevs_holder g_cdevs_holder; - return &g_cdevs_holder; -} - +struct cdevs_holder *get_cdevs(void); struct cdev_instance *get_cdev(dev_t dev_no); int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data); void free_cdevs(struct cdevs_holder *cdevs); diff --git a/mpu6050/mpu6050_data.c b/mpu6050/mpu6050_data.c index 5a8c7d9..89ed12e 100644 --- a/mpu6050/mpu6050_data.c +++ b/mpu6050/mpu6050_data.c @@ -84,7 +84,7 @@ void add_mpu6050_element(struct mpu6050_data_holder *data, } } -struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *data) +struct mpu6050_data_elements *get_active_element(struct mpu6050_data_holder *data) { struct mpu6050_data_elements* element = NULL; if (!data || !data->element_iter_current) @@ -92,3 +92,21 @@ struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *dat element = &data->element_iter_current->data; return element; } + +struct mpu6050_data_list *get_first_element(struct mpu6050_data_holder *data) +{ + struct mpu6050_data_list* element = NULL; + if (!data || !data->element_iter_current) + return element; + element = list_first_entry(&data->list.list, struct mpu6050_data_list, list); + return element; +} + +struct mpu6050_data_list *get_next_element(struct mpu6050_data_list *element) +{ + struct mpu6050_data_list* next = NULL; + if (!element) + return next; + next = list_next_entry(element, list); + return next; +} diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index 0af2111..ab7bf79 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -43,6 +43,8 @@ void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, void free_mpu6050_data(struct mpu6050_data_holder *data); void add_mpu6050_element(struct mpu6050_data_holder *data, struct mpu6050_data_elements *element); -struct mpu6050_data_elements* get_active_element(struct mpu6050_data_holder *data); +struct mpu6050_data_elements *get_active_element(struct mpu6050_data_holder *data); +struct mpu6050_data_list *get_first_element(struct mpu6050_data_holder *data); +struct mpu6050_data_list *get_next_element(struct mpu6050_data_list *element); -#endif /* __MPU6050_DATA_H__ */ \ No newline at end of file +#endif /* __MPU6050_DATA_H__ */ diff --git a/mpu6050/mpu6050_main.c b/mpu6050/mpu6050_main.c index 446e4e5..a277188 100644 --- a/mpu6050/mpu6050_main.c +++ b/mpu6050/mpu6050_main.c @@ -228,7 +228,7 @@ static void free_sysfs(void) } } -static int mpu6050_init(void) +static int __init mpu6050_init(void) { int ret; struct cdevs_holder *cdevs = get_cdevs(); @@ -275,7 +275,7 @@ static int mpu6050_init(void) return ret; } -static void mpu6050_exit(void) +static void __exit mpu6050_exit(void) { free_cdevs(get_cdevs()); free_mpu6050_data(&g_mpu6050_data); @@ -294,4 +294,4 @@ MODULE_AUTHOR("Andriy.Khulap "); MODULE_AUTHOR("Sergii.Romantsov "); MODULE_DESCRIPTION("mpu6050 I2C acc&gyro"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.2"); +MODULE_VERSION("0.3"); From 38fb790382c2cc8a2fafea8a974e5524a1dc1e1b Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Sun, 26 Nov 2017 19:25:13 +0200 Subject: [PATCH 10/14] [Lesson 6] mpu6050: data-structure is incapsulated. Structure of data and access to them are incapsulated to allow exclusive access and synchronization. --- mpu6050/mpu6050_cdev.c | 46 +++++++++--------- mpu6050/mpu6050_cdev.h | 2 +- mpu6050/mpu6050_data.c | 105 ++++++++++++++++++++++++++++++++--------- mpu6050/mpu6050_data.h | 20 ++++---- mpu6050/mpu6050_main.c | 35 +++++++------- 5 files changed, 138 insertions(+), 70 deletions(-) diff --git a/mpu6050/mpu6050_cdev.c b/mpu6050/mpu6050_cdev.c index 0717182..1be4235 100644 --- a/mpu6050/mpu6050_cdev.c +++ b/mpu6050/mpu6050_cdev.c @@ -24,8 +24,9 @@ static void read_data(struct cdevs_holder *holder) { if (!holder || !holder->data || !holder->data->read_data) { - pr_err("%s %s can't read data %p/%p \n", THIS_MODULE->name, - __FUNCTION__, holder, holder ? holder->data : NULL); + pr_err("%s %s can't read data %p/%p/%p \n", THIS_MODULE->name, + __FUNCTION__, holder, holder ? holder->data : NULL, + holder && holder->data ? holder->data->read_data : NULL); return; } @@ -85,7 +86,7 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) cdevs->cdev_line.cdev = cdev_alloc(); cdevs->cdev_line.cdev->ops = &g_fops_line; cdevs->cdev_line.cdev->owner = THIS_MODULE; - cdevs->cdev_line.data_iter = NULL; + cdevs->cdev_line.first_element = true; result = cdev_add(cdevs->cdev_line.cdev, cdevs->cdev_line.dev_no, 1); if (result < 0) goto error1; @@ -98,7 +99,7 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) cdevs->cdev_full.cdev = cdev_alloc(); cdevs->cdev_full.cdev->ops = &g_fops_full; cdevs->cdev_full.cdev->owner = THIS_MODULE; - cdevs->cdev_full.data_iter = NULL; + cdevs->cdev_full.first_element = true; result = cdev_add(cdevs->cdev_full.cdev, cdevs->cdev_full.dev_no, 1); if (result < 0) goto error1; @@ -122,7 +123,7 @@ static int open_cdev(struct inode *node, struct file *file) if (cdev) { cdev->read_all = false; - cdev->data_iter = NULL; + cdev->first_element = true; } read_data(get_cdevs()); @@ -176,7 +177,8 @@ static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count unsigned long result = 0; struct cdevs_holder *holder = get_cdevs(); struct cdev_instance *cdev; - struct mpu6050_data_elements *element; + struct mpu6050_data_elements element; + bool has_element; if (!holder || !holder->cdev_line.cdev || !holder->data) return result; @@ -184,11 +186,11 @@ static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count if (cdev->read_all) return result; - if (!holder->data->element_iter_current) + has_element = get_active_element(holder->data, &element); + if (!has_element) return result; - element = &holder->data->element_iter_current->data; - result = read_to_user_buffer(element, buffer_to, count); + result = read_to_user_buffer(&element, buffer_to, count); cdev->read_all = true; pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu \n", @@ -202,7 +204,8 @@ static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count unsigned long result = 0; struct cdevs_holder *holder = get_cdevs(); struct cdev_instance *cdev; - struct mpu6050_data_elements *element; + struct mpu6050_data_elements element; + bool element_read = false; if (!holder || !holder->cdev_full.cdev || !holder->data) return result; @@ -211,25 +214,22 @@ static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count if (cdev->read_all) return result; - /* no data to get */ - if (!holder->data->element_iter_current) - return result; - - if (!cdev->data_iter) - cdev->data_iter = get_first_element(holder->data); + if (cdev->first_element) + element_read = get_first_element(holder->data, &element); else - cdev->data_iter = get_next_element(cdev->data_iter); + element_read = get_next_element(holder->data, &element); - pr_info("%s %s element %p\n", - THIS_MODULE->name, __FUNCTION__, cdev->data_iter); + pr_info("%s %s element read: %d\n", + THIS_MODULE->name, __FUNCTION__, element_read); - if (!cdev->data_iter) + if (element_read) + cdev->first_element = false; + else return result; - element = &cdev->data_iter->data; - result = read_to_user_buffer(element, buffer_to, count); + result = read_to_user_buffer(&element, buffer_to, count); - if (holder->data->element_iter_current == cdev->data_iter) + if (is_last_element(holder->data)) cdev->read_all = true; pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu, read_all: %s \n", diff --git a/mpu6050/mpu6050_cdev.h b/mpu6050/mpu6050_cdev.h index caf7e52..2574d51 100644 --- a/mpu6050/mpu6050_cdev.h +++ b/mpu6050/mpu6050_cdev.h @@ -10,7 +10,7 @@ struct cdev_instance { dev_t dev_no; dev_t read_all; struct cdev *cdev; - struct mpu6050_data_list *data_iter; + bool first_element; }; struct cdevs_holder { diff --git a/mpu6050/mpu6050_data.c b/mpu6050/mpu6050_data.c index 89ed12e..68e13a2 100644 --- a/mpu6050/mpu6050_data.c +++ b/mpu6050/mpu6050_data.c @@ -1,14 +1,32 @@ #include "mpu6050_data.h" -void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, +struct mpu6050_data_holder_internal { + struct mpu6050_data_holder public_holder; + size_t elements_count; + struct mpu6050_data_list *element_iter_current; + struct mpu6050_data_list *element_iter; + struct mpu6050_data_list list; +}; + +static struct mpu6050_data_holder_internal g_mpu6050_data; + +struct mpu6050_data_holder* get_mpu6050_data(void) +{ + return (struct mpu6050_data_holder*)&g_mpu6050_data; +} + +void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, int (*read_data)(bool debug)) { size_t const element_size = sizeof(struct mpu6050_data_list); + struct mpu6050_data_holder_internal *data = + (struct mpu6050_data_holder_internal *)data_; if (!data) return; data->elements_count = 0; data->element_iter_current = NULL; - data->read_data = read_data; + data->element_iter = NULL; + data->public_holder.read_data = read_data; INIT_LIST_HEAD(&data->list.list); @@ -25,9 +43,11 @@ void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, data->element_iter_current = NULL; } -void free_mpu6050_data(struct mpu6050_data_holder *data) +void free_mpu6050_data(struct mpu6050_data_holder *data_) { struct mpu6050_data_list *node, *tmp; + struct mpu6050_data_holder_internal *data = + (struct mpu6050_data_holder_internal *)data_; if (!data) return; @@ -38,10 +58,12 @@ void free_mpu6050_data(struct mpu6050_data_holder *data) } } -void add_mpu6050_element(struct mpu6050_data_holder *data, +void add_mpu6050_element(struct mpu6050_data_holder *data_, struct mpu6050_data_elements *element) { int is_last = false; + struct mpu6050_data_holder_internal *data = + (struct mpu6050_data_holder_internal *)data_; if (!data || !element) return; if (!data->element_iter_current) { @@ -84,29 +106,70 @@ void add_mpu6050_element(struct mpu6050_data_holder *data, } } -struct mpu6050_data_elements *get_active_element(struct mpu6050_data_holder *data) +bool get_active_element(struct mpu6050_data_holder *data_, + struct mpu6050_data_elements *element) { - struct mpu6050_data_elements* element = NULL; - if (!data || !data->element_iter_current) - return element; - element = &data->element_iter_current->data; - return element; + bool result = false; + struct mpu6050_data_holder_internal *data = + (struct mpu6050_data_holder_internal *)data_; + if (!data || !data->element_iter_current || !element) + return result; + *element = data->element_iter_current->data; + result = true; + return result; } -struct mpu6050_data_list *get_first_element(struct mpu6050_data_holder *data) +bool get_first_element(struct mpu6050_data_holder *data_, + struct mpu6050_data_elements *element) { - struct mpu6050_data_list* element = NULL; - if (!data || !data->element_iter_current) - return element; - element = list_first_entry(&data->list.list, struct mpu6050_data_list, list); - return element; + struct mpu6050_data_holder_internal *data = + (struct mpu6050_data_holder_internal *)data_; + struct mpu6050_data_list* first = NULL; + bool result = false; + if (!data || !data->element_iter_current || !element) + return result; + first = list_first_entry(&data->list.list, struct mpu6050_data_list, list); + if (!first) + return result; + data->element_iter = first; + *element = first->data; + result = true; + return result; } -struct mpu6050_data_list *get_next_element(struct mpu6050_data_list *element) +bool get_next_element(struct mpu6050_data_holder *data_, + struct mpu6050_data_elements *element) { + struct mpu6050_data_holder_internal *data = + (struct mpu6050_data_holder_internal *)data_; struct mpu6050_data_list* next = NULL; - if (!element) - return next; - next = list_next_entry(element, list); - return next; + bool result = false; + if (!data || !data->element_iter_current || + !element || !data->element_iter) + return result; + if (data->element_iter == data->element_iter_current) + next = data->element_iter; + else + next = list_next_entry(data->element_iter, list); + + if (!next) + return result; + + data->element_iter = next; + *element = next->data; + result = true; + return result; } + +bool is_last_element(struct mpu6050_data_holder *data_) +{ + struct mpu6050_data_holder_internal *data = + (struct mpu6050_data_holder_internal *)data_; + bool result = true; + if (!data || !data->element_iter_current || !data->element_iter) + return result; + if (data->element_iter != data->element_iter_current) + result = false; + + return result; +} \ No newline at end of file diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index ab7bf79..cc4c28d 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -32,19 +32,21 @@ struct mpu6050_data_list { struct mpu6050_data_holder { struct i2c_client *drv_client; - size_t elements_count; - struct mpu6050_data_list *element_iter_current; - struct mpu6050_data_list list; int (*read_data)(bool debug); }; -void init_mpu6050_data(struct mpu6050_data_holder *data, size_t elements_count, +struct mpu6050_data_holder* get_mpu6050_data(void); +void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, int (*read_data)(bool debug)); -void free_mpu6050_data(struct mpu6050_data_holder *data); -void add_mpu6050_element(struct mpu6050_data_holder *data, +void free_mpu6050_data(struct mpu6050_data_holder *data_); +void add_mpu6050_element(struct mpu6050_data_holder *data_, struct mpu6050_data_elements *element); -struct mpu6050_data_elements *get_active_element(struct mpu6050_data_holder *data); -struct mpu6050_data_list *get_first_element(struct mpu6050_data_holder *data); -struct mpu6050_data_list *get_next_element(struct mpu6050_data_list *element); +bool get_active_element(struct mpu6050_data_holder *data_, + struct mpu6050_data_elements *element); +bool get_first_element(struct mpu6050_data_holder *data_, + struct mpu6050_data_elements *element); +bool get_next_element(struct mpu6050_data_holder *data_, + struct mpu6050_data_elements *element); +bool is_last_element(struct mpu6050_data_holder *data_); #endif /* __MPU6050_DATA_H__ */ diff --git a/mpu6050/mpu6050_main.c b/mpu6050/mpu6050_main.c index a277188..628d086 100644 --- a/mpu6050/mpu6050_main.c +++ b/mpu6050/mpu6050_main.c @@ -13,8 +13,6 @@ /* data */ #define ELEMENTS_COUNT 10 -static struct mpu6050_data_holder g_mpu6050_data; - /* functionality */ static size_t get_attribute_index(struct kobj_attribute const *attribute); @@ -22,16 +20,17 @@ static size_t get_attribute_index(struct kobj_attribute const *attribute); static int mpu6050_read_data(bool debug) { int temp; - const struct i2c_client *drv_client = g_mpu6050_data.drv_client; - struct mpu6050_data_elements* current_element = - get_active_element(&g_mpu6050_data); + const struct i2c_client *drv_client = get_mpu6050_data()->drv_client; + struct mpu6050_data_elements current_element; + bool has_element = + get_active_element(get_mpu6050_data(), ¤t_element); struct mpu6050_data_elements element; u64 msecs = jiffies_to_msecs(get_jiffies_64()); if (drv_client == 0) return -ENODEV; - if (current_element) { - u64 diff = msecs - current_element->extra_data[INDEX_TIMESTAMP]; + if (has_element) { + u64 diff = msecs - current_element.extra_data[INDEX_TIMESTAMP]; if (diff < MSEC_PER_SEC) { if (debug) dev_info(&drv_client->dev, "data reading skipped: %llu\n", @@ -63,7 +62,7 @@ static int mpu6050_read_data(bool debug) /* Extra data */ element.extra_data[INDEX_TIMESTAMP] = msecs; - add_mpu6050_element(&g_mpu6050_data, &element); + add_mpu6050_element(get_mpu6050_data(), &element); if (debug) { dev_info(&drv_client->dev, "sensor data read:\n"); @@ -121,7 +120,7 @@ static int mpu6050_probe(struct i2c_client *drv_client, i2c_smbus_write_byte_data(drv_client, REG_PWR_MGMT_1, 0); i2c_smbus_write_byte_data(drv_client, REG_PWR_MGMT_2, 0); - g_mpu6050_data.drv_client = drv_client; + get_mpu6050_data()->drv_client = drv_client; dev_info(&drv_client->dev, "i2c driver probed\n"); return 0; @@ -129,7 +128,7 @@ static int mpu6050_probe(struct i2c_client *drv_client, static int mpu6050_remove(struct i2c_client *drv_client) { - g_mpu6050_data.drv_client = 0; + get_mpu6050_data()->drv_client = 0; dev_info(&drv_client->dev, "i2c driver removed\n"); return 0; @@ -155,10 +154,14 @@ static ssize_t data_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { size_t index = get_attribute_index(attr); + struct mpu6050_data_elements current_element; + bool has_element; + mpu6050_read_data(true); + has_element = get_active_element(get_mpu6050_data(), ¤t_element); - if (index < INDEX_COUNT && g_mpu6050_data.element_iter_current) - sprintf(buf, "%d\n", g_mpu6050_data.element_iter_current->data.data[index]); + if (index < INDEX_COUNT && has_element) + sprintf(buf, "%d\n", current_element.data[index]); else buf[0] = '\0'; return strlen(buf); @@ -255,9 +258,9 @@ static int __init mpu6050_init(void) } pr_info("mpu6050: sysfs data attributes created\n"); - init_mpu6050_data(&g_mpu6050_data, ELEMENTS_COUNT, mpu6050_read_data); + init_mpu6050_data(get_mpu6050_data(), ELEMENTS_COUNT, mpu6050_read_data); - ret = init_cdevs(cdevs, &g_mpu6050_data); + ret = init_cdevs(cdevs, get_mpu6050_data()); if (ret) { pr_err("mpu6050: failed to create cdevs: %d\n", ret); goto error3; @@ -267,7 +270,7 @@ static int __init mpu6050_init(void) return 0; error3: - free_mpu6050_data(&g_mpu6050_data); + free_mpu6050_data(get_mpu6050_data()); error2: free_sysfs(); error1: @@ -278,7 +281,7 @@ static int __init mpu6050_init(void) static void __exit mpu6050_exit(void) { free_cdevs(get_cdevs()); - free_mpu6050_data(&g_mpu6050_data); + free_mpu6050_data(get_mpu6050_data()); free_sysfs(); i2c_del_driver(&mpu6050_i2c_driver); From f43c2ac05d0c7e44df08231b55bb2053c754f1e8 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Sun, 26 Nov 2017 21:06:26 +0200 Subject: [PATCH 11/14] [Lesson 6] mpu6050: data access synchronization. Added synchronization for data reading/writing. --- mpu6050/mpu6050_data.c | 53 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/mpu6050/mpu6050_data.c b/mpu6050/mpu6050_data.c index 68e13a2..f4cc956 100644 --- a/mpu6050/mpu6050_data.c +++ b/mpu6050/mpu6050_data.c @@ -1,4 +1,5 @@ #include "mpu6050_data.h" +#include struct mpu6050_data_holder_internal { struct mpu6050_data_holder public_holder; @@ -6,9 +7,12 @@ struct mpu6050_data_holder_internal { struct mpu6050_data_list *element_iter_current; struct mpu6050_data_list *element_iter; struct mpu6050_data_list list; + spinlock_t lock; }; -static struct mpu6050_data_holder_internal g_mpu6050_data; +static struct mpu6050_data_holder_internal g_mpu6050_data = { + lock: __SPIN_LOCK_UNLOCKED(lock) +}; struct mpu6050_data_holder* get_mpu6050_data(void) { @@ -35,6 +39,8 @@ void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, pr_info("%s %s: element %p\n", THIS_MODULE->name, __FUNCTION__, element); + if (!element) + break; INIT_LIST_HEAD(&element->list); list_add(&element->list, &data->list.list); ++data->elements_count; @@ -51,11 +57,13 @@ void free_mpu6050_data(struct mpu6050_data_holder *data_) if (!data) return; + spin_lock(&data->lock); list_for_each_entry_safe(node, tmp, &data->list.list, list) { pr_info("%s %s: freeing node %p", THIS_MODULE->name, __FUNCTION__, node); list_del(&node->list); kfree(node); } + spin_unlock(&data->lock); } void add_mpu6050_element(struct mpu6050_data_holder *data_, @@ -66,6 +74,8 @@ void add_mpu6050_element(struct mpu6050_data_holder *data_, (struct mpu6050_data_holder_internal *)data_; if (!data || !element) return; + + spin_lock(&data->lock); if (!data->element_iter_current) { data->element_iter_current = list_first_entry(&data->list.list, struct mpu6050_data_list, list); @@ -102,8 +112,8 @@ void add_mpu6050_element(struct mpu6050_data_holder *data_, /* pr_err("%s %s: data holder is not initialized properly", THIS_MODULE->name, __FUNCTION__); */ - return; } + spin_unlock(&data->lock); } bool get_active_element(struct mpu6050_data_holder *data_, @@ -112,10 +122,16 @@ bool get_active_element(struct mpu6050_data_holder *data_, bool result = false; struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; - if (!data || !data->element_iter_current || !element) + if (!data || !element) return result; + + spin_lock(&data->lock); + if (!data->element_iter_current) + goto error1; *element = data->element_iter_current->data; result = true; +error1: + spin_unlock(&data->lock); return result; } @@ -128,12 +144,18 @@ bool get_first_element(struct mpu6050_data_holder *data_, bool result = false; if (!data || !data->element_iter_current || !element) return result; + + spin_lock(&data->lock); + if (!data->element_iter_current) + goto error1; first = list_first_entry(&data->list.list, struct mpu6050_data_list, list); if (!first) - return result; + goto error1; data->element_iter = first; *element = first->data; result = true; +error1: + spin_unlock(&data->lock); return result; } @@ -144,20 +166,26 @@ bool get_next_element(struct mpu6050_data_holder *data_, (struct mpu6050_data_holder_internal *)data_; struct mpu6050_data_list* next = NULL; bool result = false; - if (!data || !data->element_iter_current || - !element || !data->element_iter) + if (!data || !element) return result; + + spin_lock(&data->lock); + if (!data->element_iter_current || !data->element_iter) + goto error1; + if (data->element_iter == data->element_iter_current) next = data->element_iter; else next = list_next_entry(data->element_iter, list); if (!next) - return result; + goto error1; data->element_iter = next; *element = next->data; result = true; +error1: + spin_unlock(&data->lock); return result; } @@ -166,10 +194,15 @@ bool is_last_element(struct mpu6050_data_holder *data_) struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; bool result = true; - if (!data || !data->element_iter_current || !data->element_iter) + if (!data) return result; + + spin_lock(&data->lock); + if (!data->element_iter_current || !data->element_iter) + goto error1; if (data->element_iter != data->element_iter_current) result = false; - +error1: + spin_unlock(&data->lock); return result; -} \ No newline at end of file +} From f48bd188031644ba4ffd76ab9f7adef3c1353f41 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Mon, 27 Nov 2017 16:53:18 +0200 Subject: [PATCH 12/14] [Lesson 6] mpu6050: added timer to read data Timer is added to read data each 1000 msecs. --- mpu6050/mpu6050_cdev.c | 2 -- mpu6050/mpu6050_main.c | 63 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/mpu6050/mpu6050_cdev.c b/mpu6050/mpu6050_cdev.c index 1be4235..9f42cf7 100644 --- a/mpu6050/mpu6050_cdev.c +++ b/mpu6050/mpu6050_cdev.c @@ -125,8 +125,6 @@ static int open_cdev(struct inode *node, struct file *file) cdev->read_all = false; cdev->first_element = true; } - - read_data(get_cdevs()); } return 0; diff --git a/mpu6050/mpu6050_main.c b/mpu6050/mpu6050_main.c index 628d086..edfe4aa 100644 --- a/mpu6050/mpu6050_main.c +++ b/mpu6050/mpu6050_main.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "mpu6050-regs.h" #include "mpu6050_data.h" @@ -157,7 +159,6 @@ static ssize_t data_show(struct kobject *kobj, struct mpu6050_data_elements current_element; bool has_element; - mpu6050_read_data(true); has_element = get_active_element(get_mpu6050_data(), ¤t_element); if (index < INDEX_COUNT && has_element) @@ -218,8 +219,45 @@ static size_t get_attribute_index(struct kobj_attribute const *attribute) return index; } +static void read_timer(unsigned long data); + /* Module initialization */ static struct kobject *g_kobject = NULL; +static struct task_struct *g_reading_thread = NULL; +DECLARE_COMPLETION(g_read_comletion); +DEFINE_TIMER(g_read_timer, read_timer, 0, 0); +DEFINE_SPINLOCK(g_read_timer_lock); +#define TIMER_DELAY_MSEC 1000 + +void read_timer(unsigned long param) +{ + complete(&g_read_comletion); + pr_info("mpu6050 %s: timer fired:\n", __FUNCTION__); +} + +int reading_thread(void *data) +{ + u64 time; + int ret; + pr_info("mpu6050 %s started\n", __FUNCTION__); + + while (!kthread_should_stop()) + { + pr_info("mpu6050 %s waiting\n", __FUNCTION__); + wait_for_completion(&g_read_comletion); + if (!kthread_should_stop()) + mpu6050_read_data(false); + else + break; + time = get_jiffies_64() + msecs_to_jiffies(TIMER_DELAY_MSEC); + ret = mod_timer(&g_read_timer, time); + pr_info("mpu6050 %s: timer rescheduled: %d\n", __FUNCTION__, ret); + } + + g_reading_thread = NULL; + pr_info("mpu6050 %s finished\n", __FUNCTION__); + return 0; +} static void free_sysfs(void) { @@ -234,6 +272,7 @@ static void free_sysfs(void) static int __init mpu6050_init(void) { int ret; + u64 timer_time; struct cdevs_holder *cdevs = get_cdevs(); /* Create i2c driver */ @@ -266,9 +305,22 @@ static int __init mpu6050_init(void) goto error3; } + g_reading_thread = kthread_run(reading_thread, NULL, "mpu6050_read_th"); + if (!g_reading_thread) { + pr_err("mpu6050: failed to create reading thread\n"); + goto error4; + } + + init_timer_deferrable(&g_read_timer); + timer_time = get_jiffies_64() + msecs_to_jiffies(TIMER_DELAY_MSEC); + ret = mod_timer(&g_read_timer, timer_time); + pr_info("mpu6050: timer init: %d\n", ret); + pr_info("mpu6050: module loaded\n"); return 0; +error4: + free_cdevs(get_cdevs()); error3: free_mpu6050_data(get_mpu6050_data()); error2: @@ -280,6 +332,15 @@ static int __init mpu6050_init(void) static void __exit mpu6050_exit(void) { + int result; + if (g_reading_thread) + { + complete(&g_read_comletion); + kthread_stop(g_reading_thread); + } + result = del_timer_sync(&g_read_timer); + pr_info("mpu6050: timer deleted %d\n", result); + free_cdevs(get_cdevs()); free_mpu6050_data(get_mpu6050_data()); free_sysfs(); From 69d41028fca920afc635f026ab8171d9282052e8 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Tue, 12 Dec 2017 16:33:34 +0200 Subject: [PATCH 13/14] [Lesson 6] mpu6050: codestyle fixes. Code correction according to checkpatch.pl --- mpu6050/mpu6050_cdev.c | 96 ++++++++++++++++++++---------------------- mpu6050/mpu6050_cdev.h | 2 +- mpu6050/mpu6050_data.c | 68 ++++++++++++++++++------------ mpu6050/mpu6050_data.h | 2 +- mpu6050/mpu6050_main.c | 96 ++++++++++++++++++++++++------------------ 5 files changed, 142 insertions(+), 122 deletions(-) diff --git a/mpu6050/mpu6050_cdev.c b/mpu6050/mpu6050_cdev.c index 9f42cf7..11dfb2b 100644 --- a/mpu6050/mpu6050_cdev.c +++ b/mpu6050/mpu6050_cdev.c @@ -6,36 +6,24 @@ static ssize_t read_line(struct file *, char __user *, size_t, loff_t *); static ssize_t read_full(struct file *, char __user *, size_t, loff_t *); -struct file_operations g_fops_line = { - read: read_line, - open: open_cdev, - release: release_cdev, - owner: THIS_MODULE +struct file_operations const g_fops_line = { + .read = read_line, + .open = open_cdev, + .release = release_cdev, + .owner = THIS_MODULE }; -struct file_operations g_fops_full = { - read: read_full, - open: open_cdev, - release: release_cdev, - owner: THIS_MODULE +struct file_operations const g_fops_full = { + .read = read_full, + .open = open_cdev, + .release = release_cdev, + .owner = THIS_MODULE }; -static void read_data(struct cdevs_holder *holder) -{ - if (!holder || !holder->data || !holder->data->read_data) - { - pr_err("%s %s can't read data %p/%p/%p \n", THIS_MODULE->name, - __FUNCTION__, holder, holder ? holder->data : NULL, - holder && holder->data ? holder->data->read_data : NULL); - return; - } - - holder->data->read_data(true); -} - struct cdevs_holder *get_cdevs(void) { static struct cdevs_holder g_cdevs_holder; + return &g_cdevs_holder; } @@ -43,6 +31,7 @@ struct cdev_instance *get_cdev(dev_t dev_no) { struct cdevs_holder *cdevs = get_cdevs(); struct cdev_instance *result = NULL; + if (!cdevs) return result; if (cdevs->cdev_line.dev_no == dev_no) @@ -56,29 +45,29 @@ void free_cdevs(struct cdevs_holder *cdevs) { if (!cdevs) return; - if (cdevs->cdev_line.cdev) - { + if (cdevs->cdev_line.cdev) { unregister_chrdev_region(cdevs->cdev_line.dev_no, 1); cdev_del(cdevs->cdev_line.cdev); cdevs->cdev_line.cdev = NULL; } - if (cdevs->cdev_full.cdev) - { + if (cdevs->cdev_full.cdev) { unregister_chrdev_region(cdevs->cdev_full.dev_no, 1); cdev_del(cdevs->cdev_full.cdev); cdevs->cdev_full.cdev = NULL; } - pr_info("%s %s unintialized majors %d/%d \n", THIS_MODULE->name, - __FUNCTION__, cdevs->cdev_line.major, cdevs->cdev_full.major); + pr_info("%s %s unintialized majors %d/%d\n", THIS_MODULE->name, + __func__, cdevs->cdev_line.major, cdevs->cdev_full.major); } int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) { int result = -1; + if (!cdevs || !data) return result; - result = alloc_chrdev_region(&cdevs->cdev_line.dev_no, 0, 1, "mpu6050_line"); + result = alloc_chrdev_region( + &cdevs->cdev_line.dev_no, 0, 1, "mpu6050_line"); if (result < 0) goto error1; cdevs->cdev_line.major = MAJOR(cdevs->cdev_line.dev_no); @@ -91,7 +80,8 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) if (result < 0) goto error1; - result = alloc_chrdev_region(&cdevs->cdev_full.dev_no, 0, 1, "mpu6050_full"); + result = alloc_chrdev_region( + &cdevs->cdev_full.dev_no, 0, 1, "mpu6050_full"); if (result < 0) goto error1; cdevs->cdev_full.major = MAJOR(cdevs->cdev_full.dev_no); @@ -105,8 +95,8 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) goto error1; cdevs->data = data; - pr_info("%s %s intialized majors %d/%d \n", THIS_MODULE->name, __FUNCTION__, - cdevs->cdev_line.major, cdevs->cdev_full.major); + pr_info("%s %s intialized majors %d/%d\n", THIS_MODULE->name, + __func__, cdevs->cdev_line.major, cdevs->cdev_full.major); return 0; error1: @@ -117,11 +107,11 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) static int open_cdev(struct inode *node, struct file *file) { pr_info("%s %s node %p/%u\n", - THIS_MODULE->name, __FUNCTION__, node, node ? node->i_rdev : 0); + THIS_MODULE->name, __func__, node, node ? node->i_rdev : 0); if (node) { struct cdev_instance *cdev = get_cdev(node->i_rdev); - if (cdev) - { + + if (cdev) { cdev->read_all = false; cdev->first_element = true; } @@ -133,7 +123,7 @@ static int open_cdev(struct inode *node, struct file *file) static int release_cdev(struct inode *node, struct file *file) { pr_info("%s %s node %p\n", - THIS_MODULE->name, __FUNCTION__, node); + THIS_MODULE->name, __func__, node); return 0; } @@ -144,6 +134,7 @@ static ssize_t read_to_user_buffer(struct mpu6050_data_elements *element, char __user *buffer_to, size_t count) { ssize_t result = 0; + if (!element || !buffer_to || !count) return result; if (count >= size_line) { @@ -151,9 +142,12 @@ static ssize_t read_to_user_buffer(struct mpu6050_data_elements *element, size_t length = 1; int printed = snprintf(buffer, size_line, format_line, (unsigned long)element->extra_data[INDEX_TIMESTAMP], - element->data[INDEX_GYRO_X], element->data[INDEX_GYRO_Y], - element->data[INDEX_GYRO_Z], element->data[INDEX_ACCEL_X], - element->data[INDEX_ACCEL_Y], element->data[INDEX_ACCEL_Z]); + element->data[INDEX_GYRO_X], + element->data[INDEX_GYRO_Y], + element->data[INDEX_GYRO_Z], + element->data[INDEX_ACCEL_X], + element->data[INDEX_ACCEL_Y], + element->data[INDEX_ACCEL_Z]); length = printed + 1; length = length < size_line ? length : size_line; buffer[length - 1] = '\0'; @@ -162,21 +156,21 @@ static ssize_t read_to_user_buffer(struct mpu6050_data_elements *element, result = length; else result = 0; - } - else { + } else { /* TODO: implement dyn-buffer */ } return result; } - -static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +static ssize_t read_line( + struct file *file, char __user *buffer_to, size_t count, loff_t *off) { unsigned long result = 0; struct cdevs_holder *holder = get_cdevs(); struct cdev_instance *cdev; struct mpu6050_data_elements element; bool has_element; + if (!holder || !holder->cdev_line.cdev || !holder->data) return result; @@ -191,19 +185,21 @@ static ssize_t read_line(struct file *file, char __user *buffer_to, size_t count result = read_to_user_buffer(&element, buffer_to, count); cdev->read_all = true; - pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu \n", - THIS_MODULE->name, __FUNCTION__, + pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu\n", + THIS_MODULE->name, __func__, buffer_to, (long)count, (long)size_line, (long)result); return result; } -static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count, loff_t *off) +static ssize_t read_full( + struct file *file, char __user *buffer_to, size_t count, loff_t *off) { unsigned long result = 0; struct cdevs_holder *holder = get_cdevs(); struct cdev_instance *cdev; struct mpu6050_data_elements element; bool element_read = false; + if (!holder || !holder->cdev_full.cdev || !holder->data) return result; @@ -218,7 +214,7 @@ static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count element_read = get_next_element(holder->data, &element); pr_info("%s %s element read: %d\n", - THIS_MODULE->name, __FUNCTION__, element_read); + THIS_MODULE->name, __func__, element_read); if (element_read) cdev->first_element = false; @@ -230,8 +226,8 @@ static ssize_t read_full(struct file *file, char __user *buffer_to, size_t count if (is_last_element(holder->data)) cdev->read_all = true; - pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu, read_all: %s \n", - THIS_MODULE->name, __FUNCTION__, + pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu, read_all: %s\n", + THIS_MODULE->name, __func__, buffer_to, (long)count, (long)size_line, (long)result, cdev->read_all ? "yes" : "no"); return result; diff --git a/mpu6050/mpu6050_cdev.h b/mpu6050/mpu6050_cdev.h index 2574d51..8f1173b 100644 --- a/mpu6050/mpu6050_cdev.h +++ b/mpu6050/mpu6050_cdev.h @@ -1,6 +1,6 @@ #include #include -#include +#include #include "mpu6050_data.h" diff --git a/mpu6050/mpu6050_data.c b/mpu6050/mpu6050_data.c index f4cc956..278f7e2 100644 --- a/mpu6050/mpu6050_data.c +++ b/mpu6050/mpu6050_data.c @@ -11,12 +11,12 @@ struct mpu6050_data_holder_internal { }; static struct mpu6050_data_holder_internal g_mpu6050_data = { - lock: __SPIN_LOCK_UNLOCKED(lock) + .lock = __SPIN_LOCK_UNLOCKED(lock) }; -struct mpu6050_data_holder* get_mpu6050_data(void) +struct mpu6050_data_holder *get_mpu6050_data(void) { - return (struct mpu6050_data_holder*)&g_mpu6050_data; + return (struct mpu6050_data_holder *)&g_mpu6050_data; } void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, @@ -25,6 +25,7 @@ void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, size_t const element_size = sizeof(struct mpu6050_data_list); struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; + if (!data) return; data->elements_count = 0; @@ -35,10 +36,11 @@ void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, INIT_LIST_HEAD(&data->list.list); do { - struct mpu6050_data_list *element = kzalloc(element_size, GFP_KERNEL); + struct mpu6050_data_list *element = kzalloc( + element_size, GFP_KERNEL); pr_info("%s %s: element %p\n", - THIS_MODULE->name, __FUNCTION__, element); + THIS_MODULE->name, __func__, element); if (!element) break; INIT_LIST_HEAD(&element->list); @@ -54,12 +56,14 @@ void free_mpu6050_data(struct mpu6050_data_holder *data_) struct mpu6050_data_list *node, *tmp; struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; + if (!data) return; spin_lock(&data->lock); list_for_each_entry_safe(node, tmp, &data->list.list, list) { - pr_info("%s %s: freeing node %p", THIS_MODULE->name, __FUNCTION__, node); + pr_info("%s %s: freeing node %p", + THIS_MODULE->name, __func__, node); list_del(&node->list); kfree(node); } @@ -72,46 +76,45 @@ void add_mpu6050_element(struct mpu6050_data_holder *data_, int is_last = false; struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; + if (!data || !element) return; spin_lock(&data->lock); if (!data->element_iter_current) { - data->element_iter_current = - list_first_entry(&data->list.list, struct mpu6050_data_list, list); - } - else { + data->element_iter_current = list_first_entry( + &data->list.list, struct mpu6050_data_list, list); + } else { /* check if its the last element */ - is_last = list_is_last(&data->element_iter_current->list, &data->list.list); + is_last = list_is_last( + &data->element_iter_current->list, &data->list.list); if (is_last) { - struct mpu6050_data_list *first = - list_first_entry(&data->list.list, struct mpu6050_data_list, list); + struct mpu6050_data_list *first = list_first_entry( + &data->list.list, + struct mpu6050_data_list, list); if (first) { /* move first to the last and use it */ list_move_tail(&first->list, &data->list.list); data->element_iter_current = list_next_entry( data->element_iter_current, list); } - } - else { + } else { data->element_iter_current = list_next_entry( data->element_iter_current, list); } } if (data->element_iter_current) { - memcpy( data->element_iter_current->data.data, element->data, + memcpy(data->element_iter_current->data.data, element->data, sizeof(data->element_iter_current->data.data)); - memcpy( data->element_iter_current->data.extra_data, element->extra_data, + memcpy(data->element_iter_current->data.extra_data, + element->extra_data, sizeof(data->element_iter_current->data.extra_data)); - /* pr_info("%s %s: was_last: %d next %p", THIS_MODULE->name, __FUNCTION__, - is_last, data->element_iter_current); - */ - } - else { - /* pr_err("%s %s: data holder is not initialized properly", - THIS_MODULE->name, __FUNCTION__); - */ + /* pr_info("%s %s: was_last: %d next %p", THIS_MODULE->name, */ + /* __func__, is_last, data->element_iter_current); */ + } else { + /* pr_err("%s %s: data holder is not initialized properly", */ + /* THIS_MODULE->name, __func__); */ } spin_unlock(&data->lock); } @@ -122,6 +125,7 @@ bool get_active_element(struct mpu6050_data_holder *data_, bool result = false; struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; + if (!data || !element) return result; @@ -130,6 +134,7 @@ bool get_active_element(struct mpu6050_data_holder *data_, goto error1; *element = data->element_iter_current->data; result = true; + error1: spin_unlock(&data->lock); return result; @@ -140,20 +145,23 @@ bool get_first_element(struct mpu6050_data_holder *data_, { struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; - struct mpu6050_data_list* first = NULL; + struct mpu6050_data_list *first = NULL; bool result = false; + if (!data || !data->element_iter_current || !element) return result; spin_lock(&data->lock); if (!data->element_iter_current) goto error1; - first = list_first_entry(&data->list.list, struct mpu6050_data_list, list); + first = list_first_entry( + &data->list.list, struct mpu6050_data_list, list); if (!first) goto error1; data->element_iter = first; *element = first->data; result = true; + error1: spin_unlock(&data->lock); return result; @@ -164,8 +172,9 @@ bool get_next_element(struct mpu6050_data_holder *data_, { struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; - struct mpu6050_data_list* next = NULL; + struct mpu6050_data_list *next = NULL; bool result = false; + if (!data || !element) return result; @@ -184,6 +193,7 @@ bool get_next_element(struct mpu6050_data_holder *data_, data->element_iter = next; *element = next->data; result = true; + error1: spin_unlock(&data->lock); return result; @@ -194,6 +204,7 @@ bool is_last_element(struct mpu6050_data_holder *data_) struct mpu6050_data_holder_internal *data = (struct mpu6050_data_holder_internal *)data_; bool result = true; + if (!data) return result; @@ -202,6 +213,7 @@ bool is_last_element(struct mpu6050_data_holder *data_) goto error1; if (data->element_iter != data->element_iter_current) result = false; + error1: spin_unlock(&data->lock); return result; diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index cc4c28d..3d4c6e4 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -35,7 +35,7 @@ struct mpu6050_data_holder { int (*read_data)(bool debug); }; -struct mpu6050_data_holder* get_mpu6050_data(void); +struct mpu6050_data_holder *get_mpu6050_data(void); void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, int (*read_data)(bool debug)); void free_mpu6050_data(struct mpu6050_data_holder *data_); diff --git a/mpu6050/mpu6050_main.c b/mpu6050/mpu6050_main.c index edfe4aa..2944aaa 100644 --- a/mpu6050/mpu6050_main.c +++ b/mpu6050/mpu6050_main.c @@ -33,6 +33,7 @@ static int mpu6050_read_data(bool debug) return -ENODEV; if (has_element) { u64 diff = msecs - current_element.extra_data[INDEX_TIMESTAMP]; + if (diff < MSEC_PER_SEC) { if (debug) dev_info(&drv_client->dev, "data reading skipped: %llu\n", @@ -43,22 +44,29 @@ static int mpu6050_read_data(bool debug) /* accel */ element.data[INDEX_ACCEL_X] = - (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_XOUT_H)); + (s16)((u16)i2c_smbus_read_word_swapped( + drv_client, REG_ACCEL_XOUT_H)); element.data[INDEX_ACCEL_Y] = - (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_YOUT_H)); + (s16)((u16)i2c_smbus_read_word_swapped( + drv_client, REG_ACCEL_YOUT_H)); element.data[INDEX_ACCEL_Z] = - (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_ACCEL_ZOUT_H)); + (s16)((u16)i2c_smbus_read_word_swapped( + drv_client, REG_ACCEL_ZOUT_H)); /* gyro */ element.data[INDEX_GYRO_X] = - (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_XOUT_H)); + (s16)((u16)i2c_smbus_read_word_swapped( + drv_client, REG_GYRO_XOUT_H)); element.data[INDEX_GYRO_Y] = - (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_YOUT_H)); + (s16)((u16)i2c_smbus_read_word_swapped( + drv_client, REG_GYRO_YOUT_H)); element.data[INDEX_GYRO_Z] = - (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_GYRO_ZOUT_H)); + (s16)((u16)i2c_smbus_read_word_swapped( + drv_client, REG_GYRO_ZOUT_H)); /* Temperature in degrees C = * (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ - temp = (s16)((u16)i2c_smbus_read_word_swapped(drv_client, REG_TEMP_OUT_H)); + temp = (s16)((u16)i2c_smbus_read_word_swapped( + drv_client, REG_TEMP_OUT_H)); element.data[INDEX_TEMPERATURE] = (temp + 12420 + 170) / 340; /* Extra data */ @@ -169,24 +177,24 @@ static ssize_t data_show(struct kobject *kobj, } static const struct kobj_attribute g_kobj_attributes[INDEX_COUNT] = { - __ATTR(accel_x, S_IRUGO, data_show, NULL), - __ATTR(accel_y, S_IRUGO, data_show, NULL), - __ATTR(accel_z, S_IRUGO, data_show, NULL), - __ATTR(gyro_x, S_IRUGO, data_show, NULL), - __ATTR(gyro_y, S_IRUGO, data_show, NULL), - __ATTR(gyro_z, S_IRUGO, data_show, NULL), - __ATTR(temperature, S_IRUGO, data_show, NULL) + __ATTR(accel_x, 0444, data_show, NULL), + __ATTR(accel_y, 0444, data_show, NULL), + __ATTR(accel_z, 0444, data_show, NULL), + __ATTR(gyro_x, 0444, data_show, NULL), + __ATTR(gyro_y, 0444, data_show, NULL), + __ATTR(gyro_z, 0444, data_show, NULL), + __ATTR(temperature, 0444, data_show, NULL) }; static const struct attribute *g_attributes[INDEX_COUNT + 1] = { - &g_kobj_attributes[INDEX_ACCEL_X].attr, - &g_kobj_attributes[INDEX_ACCEL_Y].attr, - &g_kobj_attributes[INDEX_ACCEL_Z].attr, - &g_kobj_attributes[INDEX_GYRO_X].attr, - &g_kobj_attributes[INDEX_GYRO_Y].attr, - &g_kobj_attributes[INDEX_GYRO_Z].attr, - &g_kobj_attributes[INDEX_TEMPERATURE].attr, - NULL, + &g_kobj_attributes[INDEX_ACCEL_X].attr, + &g_kobj_attributes[INDEX_ACCEL_Y].attr, + &g_kobj_attributes[INDEX_ACCEL_Z].attr, + &g_kobj_attributes[INDEX_GYRO_X].attr, + &g_kobj_attributes[INDEX_GYRO_Y].attr, + &g_kobj_attributes[INDEX_GYRO_Z].attr, + &g_kobj_attributes[INDEX_TEMPERATURE].attr, + NULL, }; /* Gets a number of attributes + 1 (null-terminated) */ @@ -196,8 +204,10 @@ static size_t const attributes_count(void) size_t const count = INDEX_COUNT + 1; #else size_t const full_size = sizeof(g_attributes); - static size_t const count = full_size ? full_size / sizeof(g_attributes[0]) : 0; + static size_t const count = + full_size ? full_size / sizeof(g_attributes[0]) : 0; #endif + return count; } @@ -206,13 +216,13 @@ static size_t get_attribute_index(struct kobj_attribute const *attribute) { size_t const count = attributes_count(); size_t index = 0; - if (count) - { + + if (count) { index = (size_t)(attribute - &g_kobj_attributes[0]); - if (index >= count) - { - pr_err("%s wrong computing of data index, corrected: %ld -> %ld\n", - THIS_MODULE->name, (long)index, (long)(count - 1)); + if (index >= count) { + pr_err("%s wrong data index, corrected: %ld -> %ld\n", + THIS_MODULE->name, (long)index, + (long)(count - 1)); index = count - 1; } } @@ -222,8 +232,8 @@ static size_t get_attribute_index(struct kobj_attribute const *attribute) static void read_timer(unsigned long data); /* Module initialization */ -static struct kobject *g_kobject = NULL; -static struct task_struct *g_reading_thread = NULL; +static struct kobject *g_kobject; +static struct task_struct *g_reading_thread; DECLARE_COMPLETION(g_read_comletion); DEFINE_TIMER(g_read_timer, read_timer, 0, 0); DEFINE_SPINLOCK(g_read_timer_lock); @@ -232,18 +242,18 @@ DEFINE_SPINLOCK(g_read_timer_lock); void read_timer(unsigned long param) { complete(&g_read_comletion); - pr_info("mpu6050 %s: timer fired:\n", __FUNCTION__); + pr_info("mpu6050 %s: timer fired:\n", __func__); } int reading_thread(void *data) { u64 time; int ret; - pr_info("mpu6050 %s started\n", __FUNCTION__); - while (!kthread_should_stop()) - { - pr_info("mpu6050 %s waiting\n", __FUNCTION__); + pr_info("mpu6050 %s started\n", __func__); + + while (!kthread_should_stop()) { + pr_info("mpu6050 %s waiting\n", __func__); wait_for_completion(&g_read_comletion); if (!kthread_should_stop()) mpu6050_read_data(false); @@ -251,11 +261,11 @@ int reading_thread(void *data) break; time = get_jiffies_64() + msecs_to_jiffies(TIMER_DELAY_MSEC); ret = mod_timer(&g_read_timer, time); - pr_info("mpu6050 %s: timer rescheduled: %d\n", __FUNCTION__, ret); + pr_info("mpu6050 %s: timer rescheduled: %d\n", __func__, ret); } g_reading_thread = NULL; - pr_info("mpu6050 %s finished\n", __FUNCTION__); + pr_info("mpu6050 %s finished\n", __func__); return 0; } @@ -292,12 +302,14 @@ static int __init mpu6050_init(void) ret = sysfs_create_files(g_kobject, g_attributes); if (ret) { - pr_err("mpu6050: failed to create sysfs data attributes: %d\n", ret); + pr_err("mpu6050: failed to create sysfs data attributes: %d\n", + ret); goto error2; } pr_info("mpu6050: sysfs data attributes created\n"); - init_mpu6050_data(get_mpu6050_data(), ELEMENTS_COUNT, mpu6050_read_data); + init_mpu6050_data( + get_mpu6050_data(), ELEMENTS_COUNT, mpu6050_read_data); ret = init_cdevs(cdevs, get_mpu6050_data()); if (ret) { @@ -333,8 +345,8 @@ static int __init mpu6050_init(void) static void __exit mpu6050_exit(void) { int result; - if (g_reading_thread) - { + + if (g_reading_thread) { complete(&g_read_comletion); kthread_stop(g_reading_thread); } From 9661f28f9a53a3d0567e5ad1fdfecbe288736cf3 Mon Sep 17 00:00:00 2001 From: Sergii Romantsov Date: Mon, 1 Jan 2018 17:06:41 +0200 Subject: [PATCH 14/14] [Lesson 6] mpu6050: review fixes Cleaned code: avoided unused functions. Added support to read to user buffer with requested size. Corrected misprints for variable types. Divided indexes for different data. Improved module unitialization. --- mpu6050/mpu6050_cdev.c | 56 ++++++++++++++++++++---------------------- mpu6050/mpu6050_cdev.h | 2 +- mpu6050/mpu6050_data.c | 7 +++--- mpu6050/mpu6050_data.h | 15 +++++------ mpu6050/mpu6050_main.c | 40 +++++++++++------------------- 5 files changed, 53 insertions(+), 67 deletions(-) diff --git a/mpu6050/mpu6050_cdev.c b/mpu6050/mpu6050_cdev.c index 11dfb2b..b25e7a5 100644 --- a/mpu6050/mpu6050_cdev.c +++ b/mpu6050/mpu6050_cdev.c @@ -5,6 +5,7 @@ static int release_cdev(struct inode *, struct file *); static ssize_t read_line(struct file *, char __user *, size_t, loff_t *); static ssize_t read_full(struct file *, char __user *, size_t, loff_t *); +static struct cdevs_holder g_cdevs_holder; struct file_operations const g_fops_line = { .read = read_line, @@ -22,8 +23,6 @@ struct file_operations const g_fops_full = { struct cdevs_holder *get_cdevs(void) { - static struct cdevs_holder g_cdevs_holder; - return &g_cdevs_holder; } @@ -85,7 +84,7 @@ int init_cdevs(struct cdevs_holder *cdevs, struct mpu6050_data_holder *data) if (result < 0) goto error1; cdevs->cdev_full.major = MAJOR(cdevs->cdev_full.dev_no); - cdevs->cdev_line.read_all = false; + cdevs->cdev_full.read_all = false; cdevs->cdev_full.cdev = cdev_alloc(); cdevs->cdev_full.cdev->ops = &g_fops_full; cdevs->cdev_full.cdev->owner = THIS_MODULE; @@ -127,38 +126,37 @@ static int release_cdev(struct inode *node, struct file *file) return 0; } -static char const format_line[] = "%lu: gyro=%d:%d:%d acc=%d:%d:%d\n"; -static size_t const size_line = sizeof(format_line) + 21 + 6 * 11; +static char const g_format_line[] = "%lu: gyro=%d:%d:%d acc=%d:%d:%d\n"; +static size_t const g_size_line = sizeof(g_format_line) + 21 + 6 * 11; static ssize_t read_to_user_buffer(struct mpu6050_data_elements *element, char __user *buffer_to, size_t count) { ssize_t result = 0; + char buffer[g_size_line]; + size_t length = 0; + int printed = 0; if (!element || !buffer_to || !count) return result; - if (count >= size_line) { - char buffer[size_line]; - size_t length = 1; - int printed = snprintf(buffer, size_line, format_line, - (unsigned long)element->extra_data[INDEX_TIMESTAMP], - element->data[INDEX_GYRO_X], - element->data[INDEX_GYRO_Y], - element->data[INDEX_GYRO_Z], - element->data[INDEX_ACCEL_X], - element->data[INDEX_ACCEL_Y], - element->data[INDEX_ACCEL_Z]); - length = printed + 1; - length = length < size_line ? length : size_line; - buffer[length - 1] = '\0'; - result = copy_to_user(buffer_to, buffer, length); - if (!result) - result = length; - else - result = 0; - } else { - /* TODO: implement dyn-buffer */ - } + + printed = snprintf(buffer, g_size_line, g_format_line, + (unsigned long)element->extra_data[EXTRA_INDEX_TIMESTAMP], + element->data[INDEX_GYRO_X], + element->data[INDEX_GYRO_Y], + element->data[INDEX_GYRO_Z], + element->data[INDEX_ACCEL_X], + element->data[INDEX_ACCEL_Y], + element->data[INDEX_ACCEL_Z]); + length = printed > 0 && printed < g_size_line ? printed + 1 : 1; + length = length < count ? length : count; + buffer[length - 1] = '\0'; + result = copy_to_user(buffer_to, buffer, length); + if (!result) + result = length; + else + result = 0; + return result; } @@ -187,7 +185,7 @@ static ssize_t read_line( cdev->read_all = true; pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu\n", THIS_MODULE->name, __func__, - buffer_to, (long)count, (long)size_line, (long)result); + buffer_to, (long)count, (long)g_size_line, (long)result); return result; } @@ -228,7 +226,7 @@ static ssize_t read_full( pr_info("%s %s read to %p/%lu from %lu buffer bytes -> %lu, read_all: %s\n", THIS_MODULE->name, __func__, - buffer_to, (long)count, (long)size_line, (long)result, + buffer_to, (long)count, (long)g_size_line, (long)result, cdev->read_all ? "yes" : "no"); return result; } diff --git a/mpu6050/mpu6050_cdev.h b/mpu6050/mpu6050_cdev.h index 8f1173b..fa8780f 100644 --- a/mpu6050/mpu6050_cdev.h +++ b/mpu6050/mpu6050_cdev.h @@ -8,7 +8,7 @@ struct cdev_instance { unsigned int major; dev_t dev_no; - dev_t read_all; + bool read_all; struct cdev *cdev; bool first_element; }; diff --git a/mpu6050/mpu6050_data.c b/mpu6050/mpu6050_data.c index 278f7e2..b76598b 100644 --- a/mpu6050/mpu6050_data.c +++ b/mpu6050/mpu6050_data.c @@ -16,11 +16,11 @@ static struct mpu6050_data_holder_internal g_mpu6050_data = { struct mpu6050_data_holder *get_mpu6050_data(void) { - return (struct mpu6050_data_holder *)&g_mpu6050_data; + return &g_mpu6050_data.public_holder; } -void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, - int (*read_data)(bool debug)) +void init_mpu6050_data(struct mpu6050_data_holder *data_, + size_t elements_count) { size_t const element_size = sizeof(struct mpu6050_data_list); struct mpu6050_data_holder_internal *data = @@ -31,7 +31,6 @@ void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, data->elements_count = 0; data->element_iter_current = NULL; data->element_iter = NULL; - data->public_holder.read_data = read_data; INIT_LIST_HEAD(&data->list.list); diff --git a/mpu6050/mpu6050_data.h b/mpu6050/mpu6050_data.h index 3d4c6e4..da881ce 100644 --- a/mpu6050/mpu6050_data.h +++ b/mpu6050/mpu6050_data.h @@ -15,14 +15,16 @@ enum mpu6050_data_index { INDEX_GYRO_Z, INDEX_TEMPERATURE, INDEX_COUNT, - /* Another level of data*/ - INDEX_TIMESTAMP = 0, - INDEX_EXTRA_COUNT +}; + +enum mpu6050_extra_data_index { + EXTRA_INDEX_TIMESTAMP = 0, + EXTRA_INDEX_COUNT }; struct mpu6050_data_elements { int data[INDEX_COUNT]; - u64 extra_data[INDEX_EXTRA_COUNT]; + u64 extra_data[EXTRA_INDEX_COUNT]; }; struct mpu6050_data_list { @@ -32,12 +34,11 @@ struct mpu6050_data_list { struct mpu6050_data_holder { struct i2c_client *drv_client; - int (*read_data)(bool debug); }; struct mpu6050_data_holder *get_mpu6050_data(void); -void init_mpu6050_data(struct mpu6050_data_holder *data_, size_t elements_count, - int (*read_data)(bool debug)); +void init_mpu6050_data(struct mpu6050_data_holder *data_, + size_t elements_count); void free_mpu6050_data(struct mpu6050_data_holder *data_); void add_mpu6050_element(struct mpu6050_data_holder *data_, struct mpu6050_data_elements *element); diff --git a/mpu6050/mpu6050_main.c b/mpu6050/mpu6050_main.c index 2944aaa..8abcd2e 100644 --- a/mpu6050/mpu6050_main.c +++ b/mpu6050/mpu6050_main.c @@ -19,7 +19,7 @@ /* functionality */ static size_t get_attribute_index(struct kobj_attribute const *attribute); -static int mpu6050_read_data(bool debug) +static int mpu6050_read_data(bool print_out) { int temp; const struct i2c_client *drv_client = get_mpu6050_data()->drv_client; @@ -32,10 +32,11 @@ static int mpu6050_read_data(bool debug) if (drv_client == 0) return -ENODEV; if (has_element) { - u64 diff = msecs - current_element.extra_data[INDEX_TIMESTAMP]; + u64 diff = msecs - + current_element.extra_data[EXTRA_INDEX_TIMESTAMP]; if (diff < MSEC_PER_SEC) { - if (debug) + if (print_out) dev_info(&drv_client->dev, "data reading skipped: %llu\n", diff); return 0; @@ -70,11 +71,11 @@ static int mpu6050_read_data(bool debug) element.data[INDEX_TEMPERATURE] = (temp + 12420 + 170) / 340; /* Extra data */ - element.extra_data[INDEX_TIMESTAMP] = msecs; + element.extra_data[EXTRA_INDEX_TIMESTAMP] = msecs; add_mpu6050_element(get_mpu6050_data(), &element); - if (debug) { + if (print_out) { dev_info(&drv_client->dev, "sensor data read:\n"); dev_info(&drv_client->dev, "ACCEL[X,Y,Z] = [%d, %d, %d]\n", element.data[INDEX_ACCEL_X], @@ -87,7 +88,7 @@ static int mpu6050_read_data(bool debug) dev_info(&drv_client->dev, "TEMP = %d\n", element.data[INDEX_TEMPERATURE]); dev_info(&drv_client->dev, "TIMESTAMP = %llu\n", - element.extra_data[INDEX_TIMESTAMP]); + element.extra_data[EXTRA_INDEX_TIMESTAMP]); } return 0; } @@ -197,24 +198,10 @@ static const struct attribute *g_attributes[INDEX_COUNT + 1] = { NULL, }; -/* Gets a number of attributes + 1 (null-terminated) */ -static size_t const attributes_count(void) -{ - #if 1 - size_t const count = INDEX_COUNT + 1; - #else - size_t const full_size = sizeof(g_attributes); - static size_t const count = - full_size ? full_size / sizeof(g_attributes[0]) : 0; - #endif - - return count; -} - /* Gets index of attribute in common array of data */ static size_t get_attribute_index(struct kobj_attribute const *attribute) { - size_t const count = attributes_count(); + size_t const count = INDEX_COUNT + 1; size_t index = 0; if (count) { @@ -309,7 +296,7 @@ static int __init mpu6050_init(void) pr_info("mpu6050: sysfs data attributes created\n"); init_mpu6050_data( - get_mpu6050_data(), ELEMENTS_COUNT, mpu6050_read_data); + get_mpu6050_data(), ELEMENTS_COUNT); ret = init_cdevs(cdevs, get_mpu6050_data()); if (ret) { @@ -318,7 +305,7 @@ static int __init mpu6050_init(void) } g_reading_thread = kthread_run(reading_thread, NULL, "mpu6050_read_th"); - if (!g_reading_thread) { + if (IS_ERR(g_reading_thread)) { pr_err("mpu6050: failed to create reading thread\n"); goto error4; } @@ -346,12 +333,13 @@ static void __exit mpu6050_exit(void) { int result; + result = del_timer_sync(&g_read_timer); + pr_info("mpu6050: timer deleted %d\n", result); + if (g_reading_thread) { - complete(&g_read_comletion); kthread_stop(g_reading_thread); + complete(&g_read_comletion); } - result = del_timer_sync(&g_read_timer); - pr_info("mpu6050: timer deleted %d\n", result); free_cdevs(get_cdevs()); free_mpu6050_data(get_mpu6050_data());