From 650a1eef289d5b8fa89bb702bb5df796e88e3360 Mon Sep 17 00:00:00 2001 From: Aleksandr Bulyshchenko Date: Sat, 14 Dec 2019 03:24:48 +0200 Subject: [PATCH 1/6] 10-ConcurrencyAndSynchronization: Homework task Signed-off-by: Aleksandr Bulyshchenko --- 10-ConcurrencyAndSynchronization/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 10-ConcurrencyAndSynchronization/README.md diff --git a/10-ConcurrencyAndSynchronization/README.md b/10-ConcurrencyAndSynchronization/README.md new file mode 100644 index 0000000..ec249ae --- /dev/null +++ b/10-ConcurrencyAndSynchronization/README.md @@ -0,0 +1,21 @@ +# Concurrency and synchronization + +## Homework + +**(based on MPU6050 driver)** + +_NB_: +Communication with peripheral device on i2c is quite slow asynchronous process +which responsiveness depends on the slave latency and bus utilisation. +Thus generally it's better to be done in background process. + +1. Move interaction with MPU6050 into separate thread. + +2. Protect static data of the driver (`g_mpu6050_data` in the master) +for the case of concurrent access. + +3. Limit interaction with MPU6050 in case of frequent requests: +Define validity interval - if the latest read data is older than this threshold +then new data reading is performed, otherwise previously read data is returned. +(This threshold should be configurable parameter with reasonable default.) + From cb1cb44a87a562784ded5b1cd00284fb2be1f1b4 Mon Sep 17 00:00:00 2001 From: Dmitry Domnin Date: Tue, 17 Dec 2019 22:47:29 +0200 Subject: [PATCH 2/6] 10-ConcurrencyAndSynchronization: Add mpu6050 files from hash 068d343 Signed-off-by: Dmitry Domnin --- 10-ConcurrencyAndSynchronization/Makefile | 32 ++ .../mpu6050-regs.h | 33 ++ 10-ConcurrencyAndSynchronization/mpu6050.c | 296 ++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 10-ConcurrencyAndSynchronization/Makefile create mode 100644 10-ConcurrencyAndSynchronization/mpu6050-regs.h create mode 100644 10-ConcurrencyAndSynchronization/mpu6050.c diff --git a/10-ConcurrencyAndSynchronization/Makefile b/10-ConcurrencyAndSynchronization/Makefile new file mode 100644 index 0000000..c2abd90 --- /dev/null +++ b/10-ConcurrencyAndSynchronization/Makefile @@ -0,0 +1,32 @@ +# +# mpu6050 accelerometer & gyroscope +# +ifneq ($(KERNELRELEASE),) + +obj-m := mpu6050.o + +else + + +ifeq ($(KERNELDIR),) +ifeq ($(BBB_KERNEL),) + $(error Path to kernel tree - KERNELDIR or BBB_KERNEL variable is not defined!) +endif +endif + +KERNELDIR ?= $(BBB_KERNEL) + +export ARCH = arm +export CROSS_COMPILE ?= arm-linux-gnueabihf- + + +.PHONY: all clean + +all: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) clean + + +endif diff --git a/10-ConcurrencyAndSynchronization/mpu6050-regs.h b/10-ConcurrencyAndSynchronization/mpu6050-regs.h new file mode 100644 index 0000000..9d51680 --- /dev/null +++ b/10-ConcurrencyAndSynchronization/mpu6050-regs.h @@ -0,0 +1,33 @@ +#ifndef _MPU6050_REGS_H +#define _MPU6050_REGS_H + +/* Registed addresses */ +#define REG_CONFIG 0x1A +#define REG_GYRO_CONFIG 0x1B +#define REG_ACCEL_CONFIG 0x1C +#define REG_FIFO_EN 0x23 +#define REG_INT_PIN_CFG 0x37 +#define REG_INT_ENABLE 0x38 +#define REG_ACCEL_XOUT_H 0x3B +#define REG_ACCEL_XOUT_L 0x3C +#define REG_ACCEL_YOUT_H 0x3D +#define REG_ACCEL_YOUT_L 0x3E +#define REG_ACCEL_ZOUT_H 0x3F +#define REG_ACCEL_ZOUT_L 0x40 +#define REG_TEMP_OUT_H 0x41 +#define REG_TEMP_OUT_L 0x42 +#define REG_GYRO_XOUT_H 0x43 +#define REG_GYRO_XOUT_L 0x44 +#define REG_GYRO_YOUT_H 0x45 +#define REG_GYRO_YOUT_L 0x46 +#define REG_GYRO_ZOUT_H 0x47 +#define REG_GYRO_ZOUT_L 0x48 +#define REG_USER_CTRL 0x6A +#define REG_PWR_MGMT_1 0x6B +#define REG_PWR_MGMT_2 0x6C +#define REG_WHO_AM_I 0x75 + +/* Register values */ +#define MPU6050_WHO_AM_I 0x68 + +#endif /* _MPU6050_REGS_H */ diff --git a/10-ConcurrencyAndSynchronization/mpu6050.c b/10-ConcurrencyAndSynchronization/mpu6050.c new file mode 100644 index 0000000..b6ef64b --- /dev/null +++ b/10-ConcurrencyAndSynchronization/mpu6050.c @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#include + +#include "mpu6050-regs.h" + + +struct mpu6050_data { + struct i2c_client *drv_client; + int accel_values[3]; + int gyro_values[3]; + int temperature; +}; + +static struct mpu6050_data g_mpu6050_data; + +static int mpu6050_read_data(void) +{ + int temp; + 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)); + /* 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)); + /* 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; + + 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]); + 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]); + dev_info(&drv_client->dev, "TEMP = %d\n", + g_mpu6050_data.temperature); + + return 0; +} + +static int mpu6050_probe(struct i2c_client *drv_client, + const struct i2c_device_id *id) +{ + int ret; + + dev_info(&drv_client->dev, + "i2c client address is 0x%X\n", drv_client->addr); + + /* Read who_am_i register */ + ret = i2c_smbus_read_byte_data(drv_client, REG_WHO_AM_I); + if (IS_ERR_VALUE(ret)) { + dev_err(&drv_client->dev, + "i2c_smbus_read_byte_data() failed with error: %d\n", + ret); + return ret; + } + if (ret != MPU6050_WHO_AM_I) { + dev_err(&drv_client->dev, + "wrong i2c device found: expected 0x%X, found 0x%X\n", + MPU6050_WHO_AM_I, ret); + return -1; + } + dev_info(&drv_client->dev, + "i2c mpu6050 device found, WHO_AM_I register value = 0x%X\n", + ret); + + /* Setup the device */ + /* No error handling here! */ + i2c_smbus_write_byte_data(drv_client, REG_CONFIG, 0); + i2c_smbus_write_byte_data(drv_client, REG_GYRO_CONFIG, 0); + i2c_smbus_write_byte_data(drv_client, REG_ACCEL_CONFIG, 0); + i2c_smbus_write_byte_data(drv_client, REG_FIFO_EN, 0); + i2c_smbus_write_byte_data(drv_client, REG_INT_PIN_CFG, 0); + i2c_smbus_write_byte_data(drv_client, REG_INT_ENABLE, 0); + i2c_smbus_write_byte_data(drv_client, REG_USER_CTRL, 0); + 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; + + dev_info(&drv_client->dev, "i2c driver probed\n"); + return 0; +} + +static int mpu6050_remove(struct i2c_client *drv_client) +{ + g_mpu6050_data.drv_client = 0; + + dev_info(&drv_client->dev, "i2c driver removed\n"); + return 0; +} + +static const struct i2c_device_id mpu6050_idtable[] = { + { "mpu6050", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpu6050_idtable); + +static struct i2c_driver mpu6050_i2c_driver = { + .driver = { + .name = "gl_mpu6050", + }, + + .probe = mpu6050_probe, + .remove = mpu6050_remove, + .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) +{ + mpu6050_read_data(); + + sprintf(buf, "%d\n", g_mpu6050_data.accel_values[1]); + return strlen(buf); +} + +static ssize_t accel_z_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + mpu6050_read_data(); + + sprintf(buf, "%d\n", g_mpu6050_data.accel_values[2]); + return strlen(buf); +} + +static ssize_t gyro_x_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + mpu6050_read_data(); + + sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[0]); + return strlen(buf); +} + +static ssize_t gyro_y_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + mpu6050_read_data(); + + sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[1]); + return strlen(buf); +} + +static ssize_t gyro_z_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + mpu6050_read_data(); + + 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) +{ + mpu6050_read_data(); + + sprintf(buf, "%d\n", g_mpu6050_data.temperature); + return strlen(buf); +} + +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; + + /* Create i2c driver */ + ret = i2c_add_driver(&mpu6050_i2c_driver); + if (ret) { + pr_err("mpu6050: failed to add new i2c driver: %d\n", ret); + return ret; + } + 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; + } + 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); + 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; + } + /* 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: module loaded\n"); + return 0; +} + +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"); + } + + i2c_del_driver(&mpu6050_i2c_driver); + pr_info("mpu6050: i2c driver deleted\n"); + + pr_info("mpu6050: module exited\n"); +} + +module_init(mpu6050_init); +module_exit(mpu6050_exit); + +MODULE_AUTHOR("Andriy.Khulap "); +MODULE_DESCRIPTION("mpu6050 I2C acc&gyro"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); From ab76794c35686483f93787e8a1498ce4c58cae8e Mon Sep 17 00:00:00 2001 From: Dmitry Domnin Date: Tue, 31 Dec 2019 19:55:00 +0200 Subject: [PATCH 3/6] 10-ConcurrencyAndSynchronization: Makefile refactoring Signed-off-by: Dmitry Domnin --- 10-ConcurrencyAndSynchronization/Makefile | 39 ++++++++++++++--------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/10-ConcurrencyAndSynchronization/Makefile b/10-ConcurrencyAndSynchronization/Makefile index c2abd90..3864102 100644 --- a/10-ConcurrencyAndSynchronization/Makefile +++ b/10-ConcurrencyAndSynchronization/Makefile @@ -1,32 +1,41 @@ -# -# mpu6050 accelerometer & gyroscope -# +NAME = mpu6050 + ifneq ($(KERNELRELEASE),) -obj-m := mpu6050.o +obj-m := $(NAME).o else - -ifeq ($(KERNELDIR),) -ifeq ($(BBB_KERNEL),) - $(error Path to kernel tree - KERNELDIR or BBB_KERNEL variable is not defined!) -endif -endif - -KERNELDIR ?= $(BBB_KERNEL) +NAMELINUX ?= root@192.168.0.130 +MODULEDIR ?= /lib/modules/4.19.83-sunxi/kernel/drivers/iio/accel +KERNELDIR ?= /home/dmitry/GL/orange-pi-4.19.83 +DTSDIR ?= $(PWD)/dtsi export ARCH = arm export CROSS_COMPILE ?= arm-linux-gnueabihf- - .PHONY: all clean -all: +all: $(NAME).c $(MAKE) -C $(KERNELDIR) M=$(CURDIR) modules +dtbo: + $(KERNELDIR)/scripts/dtc/dtc -I dts -O dtb \ + -o $(DTSDIR)/$(NAME).dtbo \ + $(DTSDIR)/$(NAME).dtsi + +copymod: $(NAME).ko + scp $< $(NAMELINUX):$(MODULEDIR) + +copysshid: + ssh-copy-id -i ~/.ssh/id_rsa.pub $(NAMELINUX) + +copydtbo: + scp $(PWD)/dtsi/$(NAME).dtbo \ + $(NAMELINUX):/boot/overlay-user + clean: $(MAKE) -C $(KERNELDIR) M=$(CURDIR) clean - + -rm $(DTSDIR)/$(NAME).dtbo endif From 5a2f9b3724a411c5db5bc2838c1c68325465f904 Mon Sep 17 00:00:00 2001 From: Dmitry Domnin Date: Tue, 31 Dec 2019 19:58:11 +0200 Subject: [PATCH 4/6] 10-ConcurrencyAndSynchronization: Add overlay Signed-off-by: Dmitry Domnin --- .../dtsi/mpu6050.dtsi | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 10-ConcurrencyAndSynchronization/dtsi/mpu6050.dtsi diff --git a/10-ConcurrencyAndSynchronization/dtsi/mpu6050.dtsi b/10-ConcurrencyAndSynchronization/dtsi/mpu6050.dtsi new file mode 100644 index 0000000..571772f --- /dev/null +++ b/10-ConcurrencyAndSynchronization/dtsi/mpu6050.dtsi @@ -0,0 +1,22 @@ +/dts-v1/; +/plugin/; + +/ { + compatible = "allwinner,sun8i-h3"; + + fragment@0 { + target = <&i2c0>; + __overlay__ { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + mpu: mpu6050@68 { + compatible = "gl, mpu6050"; + reg = <0x68>; + delay_ms = <10000>; + status = "okay"; + }; + }; + }; +}; From 9c64281531c9c4d1422e028d2773256878798392 Mon Sep 17 00:00:00 2001 From: Dmitry Domnin Date: Tue, 31 Dec 2019 20:02:01 +0200 Subject: [PATCH 5/6] 10-ConcurrencyAndSynchronization: Module refactoring Signed-off-by: Dmitry Domnin --- 10-ConcurrencyAndSynchronization/mpu6050.c | 425 +++++++++++++-------- 1 file changed, 259 insertions(+), 166 deletions(-) diff --git a/10-ConcurrencyAndSynchronization/mpu6050.c b/10-ConcurrencyAndSynchronization/mpu6050.c index b6ef64b..6d5405a 100644 --- a/10-ConcurrencyAndSynchronization/mpu6050.c +++ b/10-ConcurrencyAndSynchronization/mpu6050.c @@ -4,27 +4,50 @@ #include #include #include - +#include +#include +#include +#include +#include #include "mpu6050-regs.h" +struct temp_t { + int t_int; + int t_frac; +}; struct mpu6050_data { struct i2c_client *drv_client; int accel_values[3]; int gyro_values[3]; - int temperature; + struct temp_t temperature; + struct mutex mutex_mpu; + struct completion read_complete; + struct task_struct *reading_thread; + unsigned int delay_ms; + struct timer_list delay_timer; + bool flag_read; }; static struct mpu6050_data g_mpu6050_data; +static void delay_timer_callback(struct timer_list *t) +{ + dev_info(&g_mpu6050_data.drv_client->dev, + "delay timer callback, data can be updated\n"); + g_mpu6050_data.flag_read = false; +} + static int mpu6050_read_data(void) { - int temp; + int temp = 0; struct i2c_client *drv_client = g_mpu6050_data.drv_client; if (drv_client == 0) return -ENODEV; + dev_info(&drv_client->dev, "sensor data read:\n"); + /* 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)); @@ -33,13 +56,11 @@ static int mpu6050_read_data(void) 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)); - /* Temperature in degrees C = - * (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 - */ + /* temp */ 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.temperature.t_int = (temp * 1000 / 340 + 35000) / 1000; + g_mpu6050_data.temperature.t_frac = (temp * 1000 / 340 + 35000) % 1000; - 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], @@ -48,249 +69,321 @@ static int mpu6050_read_data(void) g_mpu6050_data.gyro_values[0], g_mpu6050_data.gyro_values[1], g_mpu6050_data.gyro_values[2]); - dev_info(&drv_client->dev, "TEMP = %d\n", - g_mpu6050_data.temperature); + dev_info(&drv_client->dev, "TEMP = %02d.%03d\n", + g_mpu6050_data.temperature.t_int, + g_mpu6050_data.temperature.t_frac); return 0; } -static int mpu6050_probe(struct i2c_client *drv_client, - const struct i2c_device_id *id) +static int mpu6050_read_thread(void *data) { - int ret; - - dev_info(&drv_client->dev, - "i2c client address is 0x%X\n", drv_client->addr); - - /* Read who_am_i register */ - ret = i2c_smbus_read_byte_data(drv_client, REG_WHO_AM_I); - if (IS_ERR_VALUE(ret)) { - dev_err(&drv_client->dev, - "i2c_smbus_read_byte_data() failed with error: %d\n", - ret); - return ret; - } - if (ret != MPU6050_WHO_AM_I) { - dev_err(&drv_client->dev, - "wrong i2c device found: expected 0x%X, found 0x%X\n", - MPU6050_WHO_AM_I, ret); - return -1; - } - dev_info(&drv_client->dev, - "i2c mpu6050 device found, WHO_AM_I register value = 0x%X\n", - ret); + struct mpu6050_data *pdata = data; - /* Setup the device */ - /* No error handling here! */ - i2c_smbus_write_byte_data(drv_client, REG_CONFIG, 0); - i2c_smbus_write_byte_data(drv_client, REG_GYRO_CONFIG, 0); - i2c_smbus_write_byte_data(drv_client, REG_ACCEL_CONFIG, 0); - i2c_smbus_write_byte_data(drv_client, REG_FIFO_EN, 0); - i2c_smbus_write_byte_data(drv_client, REG_INT_PIN_CFG, 0); - i2c_smbus_write_byte_data(drv_client, REG_INT_ENABLE, 0); - i2c_smbus_write_byte_data(drv_client, REG_USER_CTRL, 0); - i2c_smbus_write_byte_data(drv_client, REG_PWR_MGMT_1, 0); - i2c_smbus_write_byte_data(drv_client, REG_PWR_MGMT_2, 0); + while (!kthread_should_stop()) { + dev_info(&pdata->drv_client->dev, "mpu6050 read thread\n"); - g_mpu6050_data.drv_client = drv_client; + mutex_lock(&pdata->mutex_mpu); + mpu6050_read_data(); + mutex_unlock(&pdata->mutex_mpu); - dev_info(&drv_client->dev, "i2c driver probed\n"); - return 0; -} + complete(&pdata->read_complete); + mod_timer(&pdata->delay_timer, get_jiffies_64() + + msecs_to_jiffies(pdata->delay_ms)); + pdata->flag_read = true; -static int mpu6050_remove(struct i2c_client *drv_client) -{ - g_mpu6050_data.drv_client = 0; - - dev_info(&drv_client->dev, "i2c driver removed\n"); + kthread_park(pdata->reading_thread); + if (kthread_should_park()) + kthread_parkme(); + } return 0; } -static const struct i2c_device_id mpu6050_idtable[] = { - { "mpu6050", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mpu6050_idtable); - -static struct i2c_driver mpu6050_i2c_driver = { - .driver = { - .name = "gl_mpu6050", - }, - - .probe = mpu6050_probe, - .remove = mpu6050_remove, - .id_table = mpu6050_idtable, -}; - static ssize_t accel_x_show(struct class *class, - struct class_attribute *attr, char *buf) + struct class_attribute *attr, char *buf) { - mpu6050_read_data(); - + if (!g_mpu6050_data.flag_read) { + kthread_unpark(g_mpu6050_data.reading_thread); + wake_up_process(g_mpu6050_data.reading_thread); + wait_for_completion(&g_mpu6050_data.read_complete); + } + mutex_lock(&g_mpu6050_data.mutex_mpu); sprintf(buf, "%d\n", g_mpu6050_data.accel_values[0]); + mutex_unlock(&g_mpu6050_data.mutex_mpu); + return strlen(buf); } static ssize_t accel_y_show(struct class *class, - struct class_attribute *attr, char *buf) + struct class_attribute *attr, char *buf) { - mpu6050_read_data(); + if (!g_mpu6050_data.flag_read) { + kthread_unpark(g_mpu6050_data.reading_thread); + wake_up_process(g_mpu6050_data.reading_thread); + wait_for_completion(&g_mpu6050_data.read_complete); + } + mutex_lock(&g_mpu6050_data.mutex_mpu); sprintf(buf, "%d\n", g_mpu6050_data.accel_values[1]); + mutex_unlock(&g_mpu6050_data.mutex_mpu); + return strlen(buf); } static ssize_t accel_z_show(struct class *class, - struct class_attribute *attr, char *buf) + struct class_attribute *attr, char *buf) { - mpu6050_read_data(); + if (!g_mpu6050_data.flag_read) { + kthread_unpark(g_mpu6050_data.reading_thread); + wake_up_process(g_mpu6050_data.reading_thread); + wait_for_completion(&g_mpu6050_data.read_complete); + } + mutex_lock(&g_mpu6050_data.mutex_mpu); sprintf(buf, "%d\n", g_mpu6050_data.accel_values[2]); + mutex_unlock(&g_mpu6050_data.mutex_mpu); + return strlen(buf); } static ssize_t gyro_x_show(struct class *class, - struct class_attribute *attr, char *buf) + struct class_attribute *attr, char *buf) { - mpu6050_read_data(); + if (!g_mpu6050_data.flag_read) { + kthread_unpark(g_mpu6050_data.reading_thread); + wake_up_process(g_mpu6050_data.reading_thread); + wait_for_completion(&g_mpu6050_data.read_complete); + } + mutex_lock(&g_mpu6050_data.mutex_mpu); sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[0]); + mutex_unlock(&g_mpu6050_data.mutex_mpu); + return strlen(buf); } static ssize_t gyro_y_show(struct class *class, - struct class_attribute *attr, char *buf) + struct class_attribute *attr, char *buf) { - mpu6050_read_data(); + if (!g_mpu6050_data.flag_read) { + kthread_unpark(g_mpu6050_data.reading_thread); + wake_up_process(g_mpu6050_data.reading_thread); + wait_for_completion(&g_mpu6050_data.read_complete); + } + mutex_lock(&g_mpu6050_data.mutex_mpu); sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[1]); + mutex_unlock(&g_mpu6050_data.mutex_mpu); + return strlen(buf); } static ssize_t gyro_z_show(struct class *class, - struct class_attribute *attr, char *buf) + struct class_attribute *attr, char *buf) { - mpu6050_read_data(); + if (!g_mpu6050_data.flag_read) { + kthread_unpark(g_mpu6050_data.reading_thread); + wake_up_process(g_mpu6050_data.reading_thread); + wait_for_completion(&g_mpu6050_data.read_complete); + } + mutex_lock(&g_mpu6050_data.mutex_mpu); sprintf(buf, "%d\n", g_mpu6050_data.gyro_values[2]); + mutex_unlock(&g_mpu6050_data.mutex_mpu); + return strlen(buf); } static ssize_t temp_show(struct class *class, - struct class_attribute *attr, char *buf) + struct class_attribute *attr, char *buf) { - mpu6050_read_data(); + if (!g_mpu6050_data.flag_read) { + kthread_unpark(g_mpu6050_data.reading_thread); + wake_up_process(g_mpu6050_data.reading_thread); + wait_for_completion(&g_mpu6050_data.read_complete); + } + + mutex_lock(&g_mpu6050_data.mutex_mpu); + sprintf(buf, "%02d.%03d\n", g_mpu6050_data.temperature.t_int, + g_mpu6050_data.temperature.t_frac); + mutex_unlock(&g_mpu6050_data.mutex_mpu); - sprintf(buf, "%d\n", g_mpu6050_data.temperature); return strlen(buf); } -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 ssize_t delay_ms_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + sprintf(buf, "%d\n", g_mpu6050_data.delay_ms); -static struct class *attr_class; + return strlen(buf); +} -static int mpu6050_init(void) +static ssize_t delay_ms_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + ssize_t result = 0; + unsigned int delay = 0; + + result = sscanf(buf, "%d", &delay); + if (result != 1) + return -EINVAL; + + g_mpu6050_data.delay_ms = delay; + dev_info(&g_mpu6050_data.drv_client->dev, + "set new delay value: %d mS\n", delay); + + return count; +} + +CLASS_ATTR_RO(accel_x); +CLASS_ATTR_RO(accel_y); +CLASS_ATTR_RO(accel_z); +CLASS_ATTR_RO(gyro_x); +CLASS_ATTR_RO(gyro_y); +CLASS_ATTR_RO(gyro_z); +CLASS_ATTR_RO(temp); +CLASS_ATTR_RW(delay_ms); + +static struct attribute *mpu_class_attrs[] = { + &class_attr_accel_x.attr, + &class_attr_accel_y.attr, + &class_attr_accel_z.attr, + &class_attr_gyro_x.attr, + &class_attr_gyro_y.attr, + &class_attr_gyro_z.attr, + &class_attr_temp.attr, + &class_attr_delay_ms.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(mpu_class); + +/* Device model classes */ +struct class mpu_rc_class = { + .name = "mpu6050", + .owner = THIS_MODULE, + .class_groups = mpu_class_groups, +}; + +static int mpu6050_probe(struct i2c_client *drv_client, + const struct i2c_device_id *id) { int ret; - /* Create i2c driver */ - ret = i2c_add_driver(&mpu6050_i2c_driver); - if (ret) { - pr_err("mpu6050: failed to add new i2c driver: %d\n", ret); - return ret; - } - pr_info("mpu6050: i2c driver created\n"); + g_mpu6050_data.drv_client = drv_client; - /* 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; - } - pr_info("mpu6050: sysfs class created\n"); + mutex_init(&g_mpu6050_data.mutex_mpu); - /* 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); + init_completion(&g_mpu6050_data.read_complete); + + g_mpu6050_data.reading_thread = kthread_create(mpu6050_read_thread, + &g_mpu6050_data, "reading_mpu6050"); + + dev_info(&drv_client->dev, + "i2c client address is 0x%X\n", drv_client->addr); + + /* Read who_am_i register */ + ret = i2c_smbus_read_byte_data(drv_client, REG_WHO_AM_I); + if (IS_ERR_VALUE(ret)) { + dev_err(&drv_client->dev, + "i2c_smbus_read_byte_data() failed with error: %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; + if (ret != MPU6050_WHO_AM_I) { + dev_err(&drv_client->dev, + "wrong i2c device found: expected 0x%X, found 0x%X\n", + MPU6050_WHO_AM_I, ret); + return -1; } - /* Create gyro_x */ - ret = class_create_file(attr_class, &class_attr_gyro_x); - if (ret) { - pr_err("mpu6050: failed to create sysfs class attribute gyro_x: %d\n", ret); - return ret; + dev_info(&drv_client->dev, + "i2c mpu6050 device found, WHO_AM_I register value = 0x%X\n", + ret); + + /* Setup the device */ + // reset + if (IS_ERR_VALUE(i2c_smbus_write_byte_data(drv_client, + REG_PWR_MGMT_1, 0x80))) { + dev_err(&drv_client->dev, + "i2c_smbus_write_byte_data() failed\n"); + return -EIO; } - /* 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; + msleep(100); + // start + if (IS_ERR_VALUE(i2c_smbus_write_byte_data(drv_client, + REG_PWR_MGMT_1, 0x00))) { + dev_err(&drv_client->dev, + "i2c_smbus_write_byte_data() failed\n"); + return -EIO; } - /* 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; + + if (of_find_property(drv_client->dev.of_node, "delay_ms", NULL)) { + of_property_read_u32(drv_client->dev.of_node, "delay_ms", + &g_mpu6050_data.delay_ms); + dev_info(&drv_client->dev, "dtsi->delay_ms = %d\n", + g_mpu6050_data.delay_ms); + } else { + g_mpu6050_data.delay_ms = 10000; } - /* 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); + + timer_setup(&g_mpu6050_data.delay_timer, delay_timer_callback, 0); + + ret = class_register(&mpu_rc_class); + if (ret < 0) { + dev_err(&drv_client->dev, "failed to create sysfs class: %d\n", + ret); return ret; } + dev_info(&drv_client->dev, "sysfs class created\n"); - pr_info("mpu6050: sysfs class attributes created\n"); - - pr_info("mpu6050: module loaded\n"); + dev_info(&drv_client->dev, "i2c driver probed\n"); return 0; } -static void mpu6050_exit(void) +static int mpu6050_remove(struct i2c_client *drv_client) { - 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"); - } + g_mpu6050_data.drv_client = 0; + + class_unregister(&mpu_rc_class); + dev_info(&drv_client->dev, "sysfs class destroyed\n"); - i2c_del_driver(&mpu6050_i2c_driver); - pr_info("mpu6050: i2c driver deleted\n"); + del_timer(&g_mpu6050_data.delay_timer); + kthread_stop(g_mpu6050_data.reading_thread); + mutex_destroy(&g_mpu6050_data.mutex_mpu); - pr_info("mpu6050: module exited\n"); + dev_info(&drv_client->dev, "i2c driver removed\n"); + return 0; } -module_init(mpu6050_init); -module_exit(mpu6050_exit); +static const struct of_device_id mpu6050_ids[] = { + { .compatible = "gl, mpu6050", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mpu6050_ids); + +static const struct i2c_device_id mpu6050_idtable[] = { + { "mpu6050", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpu6050_idtable); + +static struct i2c_driver mpu6050_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "mpu6050", + .of_match_table = of_match_ptr(mpu6050_ids), + }, + .probe = mpu6050_probe, + .remove = mpu6050_remove, + .id_table = mpu6050_idtable, +}; + +module_i2c_driver(mpu6050_i2c_driver); MODULE_AUTHOR("Andriy.Khulap "); +MODULE_AUTHOR("Dmitry Domnin"); MODULE_DESCRIPTION("mpu6050 I2C acc&gyro"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); From f1a3be48093ac10ca45deaddb48095018288b307 Mon Sep 17 00:00:00 2001 From: Dmitry Domnin Date: Sun, 5 Jan 2020 20:46:29 +0200 Subject: [PATCH 6/6] 10-ConcurrencyAndSynchronization: Add results Signed-off-by: Dmitry Domnin --- .../result/dmesg.txt | 24 +++++++ .../result/load-uboot.txt | 68 +++++++++++++++++++ .../result/read_attrs.txt | 29 ++++++++ 3 files changed, 121 insertions(+) create mode 100644 10-ConcurrencyAndSynchronization/result/dmesg.txt create mode 100644 10-ConcurrencyAndSynchronization/result/load-uboot.txt create mode 100644 10-ConcurrencyAndSynchronization/result/read_attrs.txt diff --git a/10-ConcurrencyAndSynchronization/result/dmesg.txt b/10-ConcurrencyAndSynchronization/result/dmesg.txt new file mode 100644 index 0000000..b5cabd0 --- /dev/null +++ b/10-ConcurrencyAndSynchronization/result/dmesg.txt @@ -0,0 +1,24 @@ +root@orangepione:~# dmesg | grep mpu6050 +[ 6.104628] mpu6050 0-0068: i2c client address is 0x68 +[ 6.107458] mpu6050 0-0068: i2c mpu6050 device found, WHO_AM_I register value = 0x68 +[ 6.213196] mpu6050 0-0068: dtsi->delay_ms = 10000 +[ 6.213323] mpu6050 0-0068: sysfs class created +[ 6.213330] mpu6050 0-0068: i2c driver probed +[ 392.439916] mpu6050 0-0068: mpu6050 read thread +[ 392.439934] mpu6050 0-0068: sensor data read: +[ 392.443708] mpu6050 0-0068: ACCEL[X,Y,Z] = [-4952, -10656, 13284] +[ 392.443726] mpu6050 0-0068: GYRO[X,Y,Z] = [554, -46, 24] +[ 392.443738] mpu6050 0-0068: TEMP = 24.506 +[ 402.694992] mpu6050 0-0068: delay timer callback, data can be updated +[ 403.721889] mpu6050 0-0068: mpu6050 read thread +[ 403.721906] mpu6050 0-0068: sensor data read: +[ 403.725741] mpu6050 0-0068: ACCEL[X,Y,Z] = [-4948, -11004, 13152] +[ 403.725761] mpu6050 0-0068: GYRO[X,Y,Z] = [401, -415, -128] +[ 403.725775] mpu6050 0-0068: TEMP = 24.553 +[ 413.959345] mpu6050 0-0068: delay timer callback, data can be updated +[ 415.922511] mpu6050 0-0068: mpu6050 read thread +[ 415.922528] mpu6050 0-0068: sensor data read: +[ 415.926684] mpu6050 0-0068: ACCEL[X,Y,Z] = [-5032, -11100, 13196] +[ 415.926704] mpu6050 0-0068: GYRO[X,Y,Z] = [624, -408, -66] +[ 415.926718] mpu6050 0-0068: TEMP = 24.412 +[ 425.991767] mpu6050 0-0068: delay timer callback, data can be updated diff --git a/10-ConcurrencyAndSynchronization/result/load-uboot.txt b/10-ConcurrencyAndSynchronization/result/load-uboot.txt new file mode 100644 index 0000000..a385737 --- /dev/null +++ b/10-ConcurrencyAndSynchronization/result/load-uboot.txt @@ -0,0 +1,68 @@ +U-Boot SPL 2019.04-armbian (Dec 01 2019 - 12:47:04 +0200) +DRAM: 512 MiB +Trying to boot from MMC1 + + +U-Boot 2019.04-armbian (Dec 01 2019 - 12:47:04 +0200) Allwinner Technology + +CPU: Allwinner H3 (SUN8I 1680) +Model: Xunlong Orange Pi One +DRAM: 512 MiB +MMC: mmc@1c0f000: 0 +Loading Environment from EXT4... ** File not found /boot/boot.env ** + +** Unable to read "/boot/boot.env" from mmc0:1 ** +In: serial +Out: serial +Err: serial +Net: phy interface0 +eth0: ethernet@1c30000 +** Reading file would overwrite reserved memory ** +There is no valid bmp file at the given address +starting USB... +USB0: USB EHCI 1.00 +USB1: USB OHCI 1.0 +USB2: USB EHCI 1.00 +USB3: USB OHCI 1.0 +scanning bus 0 for devices... 1 USB Device(s) found +scanning bus 1 for devices... 1 USB Device(s) found +scanning bus 2 for devices... 1 USB Device(s) found +scanning bus 3 for devices... 1 USB Device(s) found + scanning usb for storage devices... 0 Storage Device(s) found +Autoboot in 1 seconds, press to stop +switch to partitions #0, OK +mmc0 is current device +Scanning mmc 0:1... +Found U-Boot script /boot/boot.scr +3798 bytes read in 2 ms (1.8 MiB/s) +## Executing script at 43100000 +U-boot loaded from SD +Boot script loaded from mmc +251 bytes read in 2 ms (122.1 KiB/s) +6842649 bytes read in 331 ms (19.7 MiB/s) +7430672 bytes read in 360 ms (19.7 MiB/s) +Found mainline kernel configuration +29878 bytes read in 10 ms (2.8 MiB/s) +374 bytes read in 7 ms (51.8 KiB/s) +Applying kernel provided DT overlay sun8i-h3-i2c0.dtbo +274 bytes read in 3 ms (88.9 KiB/s) +Applying user provided DT overlay fake_module.dtbo +434 bytes read in 1 ms (423.8 KiB/s) +Applying user provided DT overlay mpu6050.dtbo +4155 bytes read in 4 ms (1013.7 KiB/s) +Applying kernel provided DT fixup script (sun8i-h3-fixup.scr) +## Executing script at 44000000 +## Loading init Ramdisk from Legacy Image at 43300000 ... + Image Name: uInitrd + Image Type: ARM Linux RAMDisk Image (gzip compressed) + Data Size: 6842585 Bytes = 6.5 MiB + Load Address: 00000000 + Entry Point: 00000000 + Verifying Checksum ... OK +## Flattened Device Tree blob at 43000000 + Booting using the fdt blob at 0x43000000 +EHCI failed to shut down host controller. + Loading Ramdisk to 49979000, end 49fff8d9 ... OK + Loading Device Tree to 49909000, end 49978fff ... OK + +Starting kernel ... diff --git a/10-ConcurrencyAndSynchronization/result/read_attrs.txt b/10-ConcurrencyAndSynchronization/result/read_attrs.txt new file mode 100644 index 0000000..f440262 --- /dev/null +++ b/10-ConcurrencyAndSynchronization/result/read_attrs.txt @@ -0,0 +1,29 @@ +root@orangepione:~# cd /sys/class/mpu6050/ +root@orangepione:/sys/class/mpu6050# ls +accel_x accel_y accel_z delay_ms gyro_x gyro_y gyro_z temp +root@orangepione:/sys/class/mpu6050# cat accel_x +-4952 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4952 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4952 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4952 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4952 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4952 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4948 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4948 +root@orangepione:/sys/class/mpu6050# cat accel_x +-4948 +root@orangepione:/sys/class/mpu6050# cat temp +24.553 +root@orangepione:/sys/class/mpu6050# cat temp +24.412 +root@orangepione:/sys/class/mpu6050# cat temp +24.412 +root@orangepione:/sys/class/mpu6050# cat temp +24.412