From e17e666d0d6e7dc9f0bc5c6d3d12351709580acc Mon Sep 17 00:00:00 2001 From: ruvi Date: Tue, 24 Oct 2017 12:47:25 -0400 Subject: [PATCH 1/9] Homework for Lesson-04: kmalloc memory allocation added --- lesson-04-memory-management/mm/.gitignore | 2 + lesson-04-memory-management/mm/Makefile | 1 + lesson-04-memory-management/mm/xxx_kmalloc.c | 51 ++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 lesson-04-memory-management/mm/.gitignore create mode 100644 lesson-04-memory-management/mm/xxx_kmalloc.c diff --git a/lesson-04-memory-management/mm/.gitignore b/lesson-04-memory-management/mm/.gitignore new file mode 100644 index 0000000..38a07a9 --- /dev/null +++ b/lesson-04-memory-management/mm/.gitignore @@ -0,0 +1,2 @@ +*.c~ +/Makefile~ diff --git a/lesson-04-memory-management/mm/Makefile b/lesson-04-memory-management/mm/Makefile index 84465a9..0207ff3 100644 --- a/lesson-04-memory-management/mm/Makefile +++ b/lesson-04-memory-management/mm/Makefile @@ -5,6 +5,7 @@ ifneq ($(KERNELRELEASE),) obj-m += xxx.o +obj-m += xxx_kmalloc.o else diff --git a/lesson-04-memory-management/mm/xxx_kmalloc.c b/lesson-04-memory-management/mm/xxx_kmalloc.c new file mode 100644 index 0000000..7ecdccf --- /dev/null +++ b/lesson-04-memory-management/mm/xxx_kmalloc.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include + +#define LEN_MSG 160 +//static char buf_msg[ LEN_MSG + 1 ] = "Hello from module!\n"; +static char* buf_msg; + + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + strcpy( buf, buf_msg ); + printk( "read %ld\n", (long)strlen( buf ) ); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + printk( "write %ld\n", (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +int __init x_init(void) { + int res; + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + kfree(buf_msg); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); From a010b144a85cad4e13032983780010847586e5a9 Mon Sep 17 00:00:00 2001 From: ruvi Date: Tue, 24 Oct 2017 12:50:25 -0400 Subject: [PATCH 2/9] Homework for Lesson-05 --- lesson-05-time-management/.gitignore | 2 + lesson-05-time-management/Makefile | 22 ++++ lesson-05-time-management/xxx_1.c | 72 +++++++++++++ lesson-05-time-management/xxx_2.c | 60 +++++++++++ lesson-05-time-management/xxx_hrtimer.c | 132 ++++++++++++++++++++++++ lesson-05-time-management/xxx_timer.c | 105 +++++++++++++++++++ 6 files changed, 393 insertions(+) create mode 100644 lesson-05-time-management/.gitignore create mode 100644 lesson-05-time-management/Makefile create mode 100644 lesson-05-time-management/xxx_1.c create mode 100644 lesson-05-time-management/xxx_2.c create mode 100644 lesson-05-time-management/xxx_hrtimer.c create mode 100644 lesson-05-time-management/xxx_timer.c diff --git a/lesson-05-time-management/.gitignore b/lesson-05-time-management/.gitignore new file mode 100644 index 0000000..1e1e680 --- /dev/null +++ b/lesson-05-time-management/.gitignore @@ -0,0 +1,2 @@ +/Makefile~ +*.c~ diff --git a/lesson-05-time-management/Makefile b/lesson-05-time-management/Makefile new file mode 100644 index 0000000..6b603d1 --- /dev/null +++ b/lesson-05-time-management/Makefile @@ -0,0 +1,22 @@ +# +# Memory management example +# + +ifneq ($(KERNELRELEASE),) + +obj-m += xxx_1.o +obj-m += xxx_2.o +obj-m += xxx_timer.o +obj-m += xxx_hrtimer.o + +else + +KERNELDIR := $(BUILD_KERNEL) + +.PHONY: all clean +all: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) modules +clean: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) clean + +endif diff --git a/lesson-05-time-management/xxx_1.c b/lesson-05-time-management/xxx_1.c new file mode 100644 index 0000000..8fb3611 --- /dev/null +++ b/lesson-05-time-management/xxx_1.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +//static char buf_msg[ LEN_MSG + 1 ] = "Hello from module!\n"; +static char* buf_msg; +static struct timeval* tv; +static unsigned char first_read; + + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + struct timeval tv_cur; + strcpy( buf, buf_msg ); + do_gettimeofday(&tv_cur); + if (first_read == 1) + { + printk( "No previous read operations\n"); + first_read = 0; + } + else + { + printk( "previous read was done %ld s ago\n", tv_cur.tv_sec-tv->tv_sec); + } + printk( "read %ld\n", (long)strlen( buf ) ); + *tv = tv_cur; + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + printk( "write %ld\n", (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +int __init x_init(void) { + int res; + tv = kmalloc(sizeof(*tv), GFP_KERNEL); + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + first_read = 1; + tv->tv_sec = 0; + tv->tv_usec = 0; + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + kfree(buf_msg); + kfree(tv); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); diff --git a/lesson-05-time-management/xxx_2.c b/lesson-05-time-management/xxx_2.c new file mode 100644 index 0000000..d41e9f1 --- /dev/null +++ b/lesson-05-time-management/xxx_2.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +//static char buf_msg[ LEN_MSG + 1 ] = "Hello from module!\n"; +static char* buf_msg; +static struct timeval* tv; + + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + strcpy( buf, buf_msg ); + printk( "previous absolute read time: %ld s %ld mks\n", tv->tv_sec, tv->tv_usec ); + printk( "read %ld\n", (long)strlen( buf ) ); + do_gettimeofday(tv); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + printk( "write %ld\n", (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +int __init x_init(void) { + int res; + tv = kmalloc(sizeof(*tv),GFP_KERNEL); + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + tv->tv_sec = 0; + tv->tv_usec = 0; + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + kfree(buf_msg); + kfree(tv); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); diff --git a/lesson-05-time-management/xxx_hrtimer.c b/lesson-05-time-management/xxx_hrtimer.c new file mode 100644 index 0000000..110cab3 --- /dev/null +++ b/lesson-05-time-management/xxx_hrtimer.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +//static char buf_msg[ LEN_MSG + 1 ] = "Hello from module!\n"; +static unsigned int fib_n; +static char* buf_msg; +static struct timeval* tv; +static ktime_t tout; +static struct kt_data +{ + struct hrtimer timer; + ktime_t period; +// int numb; +} *data; + + +unsigned long long int fib(unsigned int n) +{ + unsigned long long int first = 0, second = 1, next = 0; + unsigned int i; + for ( i = 0 ; i <= n ; i++ ) + { + if ( i <= 1 ) + next = i; + else + { + next = first + second; + first = second; + second = next; + } + } + return next; +} + + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + strcpy( buf, buf_msg ); +// printk( "previous absolute read time: %ld s %ld mks\n", tv->tv_sec, tv->tv_usec ); + printk( "read %ld\n", (long)strlen( buf ) ); +// do_gettimeofday(tv); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { +// printk( "write %ld\n", (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static int ktfun( struct hrtimer *var ) +{ +#else +static enum hrtimer_restart ktfun( struct hrtimer *var ) +{ +#endif + char buf[100]; + ktime_t now = var->base->get_time(); // текущее время в типе ktime_t +// printk( KERN_INFO "timer run #%d at jiffies=%ld\n", data->numb, jiffies ); + + sprintf(buf,"%u %llu\n",fib_n,fib(fib_n)); +// printk( KERN_INFO "%s\n", buf ); + xxx_store(x_class, &class_attr_xxx,buf,strlen(buf)+1); + if (fib_n < 93) fib_n++; + else fib_n = 0; + hrtimer_forward( var, now, tout ); +// return data->numb-- > 0 ? HRTIMER_RESTART : HRTIMER_NORESTART; + return HRTIMER_RESTART; +} + + +int __init x_init(void) +{ + fib_n = 0; + + enum hrtimer_mode mode; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) + mode = HRTIMER_REL; + #else + mode = HRTIMER_MODE_REL; + #endif + tout = ktime_set( 1, 0 ); /* 1 sec. + 0 nsec. */ + data = kmalloc( sizeof(*data), GFP_KERNEL ); + data->period = tout; + hrtimer_init( &data->timer, CLOCK_REALTIME, mode ); + data->timer.function = ktfun; +// data->numb = 93; + printk( KERN_INFO "timer start at jiffies=%ld\n", jiffies ); + hrtimer_start( &data->timer, data->period, mode ); + + int res; + tv = kmalloc(sizeof(*tv),GFP_KERNEL); + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + tv->tv_sec = 0; + tv->tv_usec = 0; + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + hrtimer_cancel( &data->timer ); + kfree( data ); + kfree(buf_msg); + kfree(tv); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); diff --git a/lesson-05-time-management/xxx_timer.c b/lesson-05-time-management/xxx_timer.c new file mode 100644 index 0000000..8687146 --- /dev/null +++ b/lesson-05-time-management/xxx_timer.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +#define DELAY HZ +static unsigned int fib_n; +static char* buf_msg; +static struct timer_list irq_timer; +//static unsigned long time_interval; + +unsigned long long int fib(unsigned int n) +{ + unsigned long long int first = 0, second = 1, next = 0; + unsigned int i; + for ( i = 0 ; i <= n ; i++ ) + { + if ( i <= 1 ) + next = i; + else + { + next = first + second; + first = second; + second = next; + } + } + return next; +} + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + + strcpy( buf, buf_msg ); +// printk("timeout: %ld", time_interval); + printk( "read %ld\n", (long)strlen( buf ) ); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { +// printk( "write %ld\n", (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +void IRQ_TimerHandler(unsigned long data) +{ + char buf[100]; +// time_interval++; + sprintf(buf,"%u %llu\n",fib_n,fib(fib_n)); +// printk( KERN_INFO "%s\n", buf ); + xxx_store(x_class, &class_attr_xxx,buf,strlen(buf)+1); + if (fib_n < 93) fib_n++; + else fib_n = 0; + irq_timer.expires = get_jiffies_64() + DELAY; + add_timer(&irq_timer); +} + + +int __init x_init(void) { + int res; + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + + fib_n = 0; + +// time_interval = 0; +//init timer + init_timer(&irq_timer); +//inicialize struct + irq_timer.expires = get_jiffies_64() + DELAY; + irq_timer.data = 0; + irq_timer.function = IRQ_TimerHandler; +//add timer + add_timer(&irq_timer); + + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + del_timer(&irq_timer); + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + kfree(buf_msg); + + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); From dee62dbb6e27fe73e488a01b994929873c84a6a8 Mon Sep 17 00:00:00 2001 From: ruvi Date: Sat, 25 Nov 2017 21:27:53 +0200 Subject: [PATCH 3/9] three ways of dynamic memory allocation added: kmalloc, vmalloc, kmem_cache --- lesson-04-memory-management/mm/.gitignore | 2 + lesson-04-memory-management/mm/Makefile | 1 + lesson-04-memory-management/mm/xxx_dynamic.c | 153 +++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 lesson-04-memory-management/mm/.gitignore create mode 100644 lesson-04-memory-management/mm/xxx_dynamic.c diff --git a/lesson-04-memory-management/mm/.gitignore b/lesson-04-memory-management/mm/.gitignore new file mode 100644 index 0000000..1e1e680 --- /dev/null +++ b/lesson-04-memory-management/mm/.gitignore @@ -0,0 +1,2 @@ +/Makefile~ +*.c~ diff --git a/lesson-04-memory-management/mm/Makefile b/lesson-04-memory-management/mm/Makefile index 84465a9..a50bc59 100644 --- a/lesson-04-memory-management/mm/Makefile +++ b/lesson-04-memory-management/mm/Makefile @@ -5,6 +5,7 @@ ifneq ($(KERNELRELEASE),) obj-m += xxx.o +obj-m += xxx_dynamic.o else diff --git a/lesson-04-memory-management/mm/xxx_dynamic.c b/lesson-04-memory-management/mm/xxx_dynamic.c new file mode 100644 index 0000000..359c202 --- /dev/null +++ b/lesson-04-memory-management/mm/xxx_dynamic.c @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUF_SIZE_MAX 256 + +static enum { ALLOC_TYPE_KMALLOC = 0, ALLOC_TYPE_KVMALLOC, ALLOC_TYPE_KCACHE} alloc_type = ALLOC_TYPE_KMALLOC; +static struct kmem_cache* pmem_cache = 0; +static size_t buf_size = 0; +struct KCACHE_STRUCT { + char buf[BUF_SIZE_MAX]; +}; + +static char* buf_msg = 0; + +const char* get_alloc_type(int type) +{ + switch (type) { + case ALLOC_TYPE_KMALLOC: + return "ALLOC_TYPE_KMALLOC"; + case ALLOC_TYPE_KVMALLOC: + return "ALLOC_TYPE_KVMALLOC"; + case ALLOC_TYPE_KCACHE: + return "ALLOC_TYPE_KCACHE"; + } + return "ALLOC_TYPE_UNKNOWN"; +} + +static void* mem_alloc(int type, size_t size) +{ + void* buf = 0; + switch (type) { + case ALLOC_TYPE_KMALLOC: + buf = kmalloc(size, GFP_KERNEL); + break; + case ALLOC_TYPE_KVMALLOC: + buf = vmalloc(size); + break; + case ALLOC_TYPE_KCACHE: + buf = kmem_cache_alloc( pmem_cache, GFP_KERNEL ); + break; + } + + if (buf) buf_size = size; + + printk(KERN_INFO "[%s:%s] {%s} %zu bytes --> %p\n", THIS_MODULE->name, __FUNCTION__, get_alloc_type(type), size, buf); + return buf; +} + +static void mem_free(int type, void* buf) +{ + if (!buf) { + printk(KERN_INFO "[%s:%s] Wrong parameter %p\n", THIS_MODULE->name, __FUNCTION__, buf); + return; + } + switch (type) { + case ALLOC_TYPE_KMALLOC: + kfree(buf); + break; + case ALLOC_TYPE_KVMALLOC: + vfree(buf); + break; + case ALLOC_TYPE_KCACHE: + kmem_cache_free( pmem_cache, buf ); + break; + } + printk(KERN_INFO "[%s:%s] {%s} bytes --> %p\n", THIS_MODULE->name, __FUNCTION__, get_alloc_type(type), buf); +} + +void init_kcache(void) +{ + if (alloc_type != ALLOC_TYPE_KCACHE) return; + pmem_cache = KMEM_CACHE(KCACHE_STRUCT, 0); + printk(KERN_INFO "[%s:%s] pmem_cache: %p\n", THIS_MODULE->name, __FUNCTION__, pmem_cache); +} + +void deinit_kcache(void) +{ + if (alloc_type != ALLOC_TYPE_KCACHE) { + mem_free(alloc_type, buf_msg); + return; + } + kmem_cache_destroy( pmem_cache ); +} + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + if (!buf_msg) { + printk(KERN_INFO "[%s:%s] Buffer is empty\n", THIS_MODULE->name, __FUNCTION__); + return 0; + } + strcpy( buf, buf_msg ); + printk(KERN_INFO "[%s:%s] read %ld\n", THIS_MODULE->name, __FUNCTION__, (long)strlen( buf ) ); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + if (buf_msg) { + mem_free(alloc_type, buf_msg); + buf_msg = 0; + } + + if (count >= BUF_SIZE_MAX) { + printk(KERN_INFO "[%s:%s] Buffer size exceeded! \n", THIS_MODULE->name, __FUNCTION__); + return 0; + } + buf_msg = mem_alloc(alloc_type, count + 1); + if (!buf_msg) { + printk(KERN_INFO "[%s:%s] Memory allocation error! \n", THIS_MODULE->name, __FUNCTION__); + return 0; + } + else { + printk(KERN_INFO "[%s:%s] write %ld\n", THIS_MODULE->name, __FUNCTION__, (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; + } +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +int __init x_init(void) { + int res; + printk(KERN_INFO "alloc_type is %s\n", get_alloc_type(alloc_type)); + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk(KERN_INFO "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + init_kcache(); + printk(KERN_INFO "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + deinit_kcache(); + printk(KERN_INFO "'xxx' module deinitialized\n" ); + return; +} + +module_param( alloc_type, int, 0444 ); +MODULE_PARM_DESC(alloc_type, "Type of memory allocation: 0 - kmalloc; 1 - vmalloc; 2 - cache"); + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); From dbfff23b5625d4e5712a0daebe610548d282d576 Mon Sep 17 00:00:00 2001 From: ruvi Date: Sun, 26 Nov 2017 15:43:58 +0200 Subject: [PATCH 4/9] Time management examples using timer and hrtimer added --- lesson-05-time-management/.gitignore | 2 + lesson-05-time-management/Makefile | 22 +++++ lesson-05-time-management/xxx_1.c | 71 ++++++++++++++ lesson-05-time-management/xxx_2.c | 59 ++++++++++++ lesson-05-time-management/xxx_hrtimer.c | 120 ++++++++++++++++++++++++ lesson-05-time-management/xxx_timer.c | 96 +++++++++++++++++++ 6 files changed, 370 insertions(+) create mode 100644 lesson-05-time-management/.gitignore create mode 100644 lesson-05-time-management/Makefile create mode 100644 lesson-05-time-management/xxx_1.c create mode 100644 lesson-05-time-management/xxx_2.c create mode 100644 lesson-05-time-management/xxx_hrtimer.c create mode 100644 lesson-05-time-management/xxx_timer.c diff --git a/lesson-05-time-management/.gitignore b/lesson-05-time-management/.gitignore new file mode 100644 index 0000000..1e1e680 --- /dev/null +++ b/lesson-05-time-management/.gitignore @@ -0,0 +1,2 @@ +/Makefile~ +*.c~ diff --git a/lesson-05-time-management/Makefile b/lesson-05-time-management/Makefile new file mode 100644 index 0000000..6b603d1 --- /dev/null +++ b/lesson-05-time-management/Makefile @@ -0,0 +1,22 @@ +# +# Memory management example +# + +ifneq ($(KERNELRELEASE),) + +obj-m += xxx_1.o +obj-m += xxx_2.o +obj-m += xxx_timer.o +obj-m += xxx_hrtimer.o + +else + +KERNELDIR := $(BUILD_KERNEL) + +.PHONY: all clean +all: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) modules +clean: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) clean + +endif diff --git a/lesson-05-time-management/xxx_1.c b/lesson-05-time-management/xxx_1.c new file mode 100644 index 0000000..d6837d4 --- /dev/null +++ b/lesson-05-time-management/xxx_1.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +static char* buf_msg; +static struct timeval* tv; +static unsigned char first_read; + + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + struct timeval tv_cur; + strcpy( buf, buf_msg ); + do_gettimeofday(&tv_cur); + if (first_read == 1) + { + printk( "No previous read operations\n"); + first_read = 0; + } + else + { + printk( "previous read was done %ld s ago\n", tv_cur.tv_sec-tv->tv_sec); + } + printk( "read %ld\n", (long)strlen( buf ) ); + *tv = tv_cur; + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + printk( "write %ld\n", (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +int __init x_init(void) { + int res; + tv = kmalloc(sizeof(*tv), GFP_KERNEL); + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + first_read = 1; + tv->tv_sec = 0; + tv->tv_usec = 0; + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + kfree(buf_msg); + kfree(tv); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); diff --git a/lesson-05-time-management/xxx_2.c b/lesson-05-time-management/xxx_2.c new file mode 100644 index 0000000..e056fac --- /dev/null +++ b/lesson-05-time-management/xxx_2.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +static char* buf_msg; +static struct timeval* tv; + + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + strcpy( buf, buf_msg ); + printk( "previous absolute read time: %ld s %ld mks\n", tv->tv_sec, tv->tv_usec ); + printk( "read %ld\n", (long)strlen( buf ) ); + do_gettimeofday(tv); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + printk( "write %ld\n", (long)count ); + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +int __init x_init(void) { + int res; + tv = kmalloc(sizeof(*tv),GFP_KERNEL); + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + tv->tv_sec = 0; + tv->tv_usec = 0; + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + kfree(buf_msg); + kfree(tv); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); diff --git a/lesson-05-time-management/xxx_hrtimer.c b/lesson-05-time-management/xxx_hrtimer.c new file mode 100644 index 0000000..bd52840 --- /dev/null +++ b/lesson-05-time-management/xxx_hrtimer.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +static unsigned int fib_n; +static char* buf_msg; +static struct timeval* tv; +static ktime_t tout; +static struct kt_data +{ + struct hrtimer timer; + ktime_t period; +} *data; + + +unsigned long long int fib(unsigned int n) +{ + unsigned long long int first = 0, second = 1, next = 0; + unsigned int i; + for ( i = 0 ; i <= n ; i++ ) + { + if ( i <= 1 ) + next = i; + else + { + next = first + second; + first = second; + second = next; + } + } + return next; +} + + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + strcpy( buf, buf_msg ); + printk( "read %ld\n", (long)strlen( buf ) ); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static int ktfun( struct hrtimer *var ) +{ +#else +static enum hrtimer_restart ktfun( struct hrtimer *var ) +{ +#endif + char buf[100]; + ktime_t now = var->base->get_time(); // текущее время в типе ktime_t + sprintf(buf,"%u %llu\n",fib_n,fib(fib_n)); + xxx_store(x_class, &class_attr_xxx,buf,strlen(buf)+1); + if (fib_n < 93) fib_n++; + else fib_n = 0; + hrtimer_forward( var, now, tout ); + return HRTIMER_RESTART; +} + + +int __init x_init(void) +{ + fib_n = 0; + enum hrtimer_mode mode; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) + mode = HRTIMER_REL; + #else + mode = HRTIMER_MODE_REL; + #endif + tout = ktime_set( 1, 0 ); /* 1 sec. + 0 nsec. */ + data = kmalloc( sizeof(*data), GFP_KERNEL ); + data->period = tout; + hrtimer_init( &data->timer, CLOCK_REALTIME, mode ); + data->timer.function = ktfun; + printk( KERN_INFO "timer start at jiffies=%ld\n", jiffies ); + hrtimer_start( &data->timer, data->period, mode ); + int res; + tv = kmalloc(sizeof(*tv),GFP_KERNEL); + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + tv->tv_sec = 0; + tv->tv_usec = 0; + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + hrtimer_cancel( &data->timer ); + kfree( data ); + kfree(buf_msg); + kfree(tv); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); diff --git a/lesson-05-time-management/xxx_timer.c b/lesson-05-time-management/xxx_timer.c new file mode 100644 index 0000000..1092573 --- /dev/null +++ b/lesson-05-time-management/xxx_timer.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LEN_MSG 160 +#define DELAY HZ +static unsigned int fib_n; +static char* buf_msg; +static struct timer_list irq_timer; + +unsigned long long int fib(unsigned int n) +{ + unsigned long long int first = 0, second = 1, next = 0; + unsigned int i; + for ( i = 0 ; i <= n ; i++ ) + { + if ( i <= 1 ) + next = i; + else + { + next = first + second; + first = second; + second = next; + } + } + return next; +} + +static ssize_t xxx_show( struct class *class, struct class_attribute *attr, char *buf ) { + strcpy( buf, buf_msg ); + printk( "read %ld\n", (long)strlen( buf ) ); + return strlen( buf ); +} + +static ssize_t xxx_store( struct class *class, struct class_attribute *attr, const char *buf, size_t count ) { + strncpy( buf_msg, buf, count ); + buf_msg[ count ] = '\0'; + return count; +} + +CLASS_ATTR_RW( xxx ); + +static struct class *x_class; + +void IRQ_TimerHandler(unsigned long data) +{ + char buf[100]; + sprintf(buf,"%u %llu\n",fib_n,fib(fib_n)); + xxx_store(x_class, &class_attr_xxx,buf,strlen(buf)+1); + if (fib_n < 93) fib_n++; + else fib_n = 0; + irq_timer.expires = get_jiffies_64() + DELAY; + add_timer(&irq_timer); +} + + +int __init x_init(void) { + int res; + buf_msg = kmalloc(LEN_MSG+1,GFP_KERNEL | __GFP_ZERO); + x_class = class_create( THIS_MODULE, "x-class" ); + if( IS_ERR( x_class ) ) printk( "bad class create\n" ); + res = class_create_file( x_class, &class_attr_xxx ); + + fib_n = 0; + +//init timer + init_timer(&irq_timer); +//initialize struct + irq_timer.expires = get_jiffies_64() + DELAY; + irq_timer.data = 0; + irq_timer.function = IRQ_TimerHandler; +//add timer + add_timer(&irq_timer); + printk( "'xxx' module initialized\n" ); + return res; +} + +void x_cleanup(void) { + del_timer(&irq_timer); + class_remove_file( x_class, &class_attr_xxx ); + class_destroy( x_class ); + kfree(buf_msg); + return; +} + +module_init( x_init ); +module_exit( x_cleanup ); + +MODULE_LICENSE( "GPL" ); From 72c49fec2dc81f4cfefba7e2432e580c5ab73a8a Mon Sep 17 00:00:00 2001 From: ruvi Date: Sun, 26 Nov 2017 22:49:50 +0200 Subject: [PATCH 5/9] Mpu6050 initial project added --- mpu6050/Makefile | 35 +++ mpu6050/am335x-bone-common.dtsi | 464 ++++++++++++++++++++++++++++++++ mpu6050/mpu6050-regs.h | 33 +++ mpu6050/mpu6050.c | 278 +++++++++++++++++++ 4 files changed, 810 insertions(+) create mode 100644 mpu6050/Makefile create mode 100644 mpu6050/am335x-bone-common.dtsi create mode 100644 mpu6050/mpu6050-regs.h create mode 100644 mpu6050/mpu6050.c diff --git a/mpu6050/Makefile b/mpu6050/Makefile new file mode 100644 index 0000000..7e1e885 --- /dev/null +++ b/mpu6050/Makefile @@ -0,0 +1,35 @@ +# +# 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 dtb + +all: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) clean + +dtb: + cp am335x-bone-common.dtsi $(KERNELDIR)/arch/arm/boot/dts/ + $(MAKE) -C $(KERNELDIR) M=$(CURDIR) dtbs + + +endif diff --git a/mpu6050/am335x-bone-common.dtsi b/mpu6050/am335x-bone-common.dtsi new file mode 100644 index 0000000..acb9914 --- /dev/null +++ b/mpu6050/am335x-bone-common.dtsi @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +/ { + cpus { + cpu@0 { + cpu0-supply = <&dcdc2_reg>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x10000000>; /* 256 MB */ + }; + + chosen { + stdout-path = &uart0; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&user_leds_s0>; + + compatible = "gpio-leds"; + + led2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + }; + + vmmcsd_fixed: fixedregulator0 { + compatible = "regulator-fixed"; + regulator-name = "vmmcsd_fixed"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; +}; + +&am33xx_pinmux { + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_IOPAD(0x854, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_IOPAD(0x858, PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_IOPAD(0x85c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_IOPAD(0x860, PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_a8.gpio1_24 */ + >; + }; + + i2c0_pins: pinmux_i2c0_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x988, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_sda.i2c0_sda */ + AM33XX_IOPAD(0x98c, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_scl.i2c0_scl */ + >; + }; + + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x978, PIN_INPUT_PULLUP | MUX_MODE3) /* uart1_ctsn.i2c2_sda */ + AM33XX_IOPAD(0x97c, PIN_INPUT_PULLUP | MUX_MODE3) /* uart1_rtsn.i2c2_scl */ + >; + }; + + uart0_pins: pinmux_uart0_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x970, PIN_INPUT_PULLUP | MUX_MODE0) /* uart0_rxd.uart0_rxd */ + AM33XX_IOPAD(0x974, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart0_txd.uart0_txd */ + >; + }; + + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Slave 1 */ + 0x108 (PIN_INPUT | MUX_MODE0) /* mii1_col.mii1_col */ + 0x10c (PIN_INPUT | MUX_MODE0) /* mii1_crs.mii1_crs */ + AM33XX_IOPAD(0x910, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxerr.mii1_rxerr */ + AM33XX_IOPAD(0x914, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */ + AM33XX_IOPAD(0x918, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxdv.mii1_rxdv */ + AM33XX_IOPAD(0x91c, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd3.mii1_txd3 */ + AM33XX_IOPAD(0x920, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd2.mii1_txd2 */ + AM33XX_IOPAD(0x924, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd1.mii1_txd1 */ + AM33XX_IOPAD(0x928, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd0.mii1_txd0 */ + AM33XX_IOPAD(0x92c, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_txclk.mii1_txclk */ + AM33XX_IOPAD(0x930, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxclk.mii1_rxclk */ + AM33XX_IOPAD(0x934, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd3.mii1_rxd3 */ + AM33XX_IOPAD(0x938, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd2.mii1_rxd2 */ + AM33XX_IOPAD(0x93c, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd1.mii1_rxd1 */ + AM33XX_IOPAD(0x940, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd0.mii1_rxd0 */ + >; + }; + + cpsw_sleep: cpsw_sleep { + pinctrl-single,pins = < + /* Slave 1 reset value */ + 0x108 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x10c (PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x910, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x914, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x918, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x91c, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x920, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x924, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x928, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x92c, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x930, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x934, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x938, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x93c, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x940, PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + davinci_mdio_default: davinci_mdio_default { + pinctrl-single,pins = < + /* MDIO */ + AM33XX_IOPAD(0x948, PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* mdio_data.mdio_data */ + AM33XX_IOPAD(0x94c, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mdio_clk.mdio_clk */ + >; + }; + + davinci_mdio_sleep: davinci_mdio_sleep { + pinctrl-single,pins = < + /* MDIO reset value */ + AM33XX_IOPAD(0x948, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x94c, PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + mmc1_pins: pinmux_mmc1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x960, PIN_INPUT | MUX_MODE7) /* GPIO0_6 */ + >; + }; + + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x880, PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + AM33XX_IOPAD(0x884, PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + AM33XX_IOPAD(0x800, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + AM33XX_IOPAD(0x804, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + AM33XX_IOPAD(0x808, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + AM33XX_IOPAD(0x80c, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + AM33XX_IOPAD(0x810, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad4.mmc1_dat4 */ + AM33XX_IOPAD(0x814, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad5.mmc1_dat5 */ + AM33XX_IOPAD(0x818, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad6.mmc1_dat6 */ + AM33XX_IOPAD(0x81c, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad7.mmc1_dat7 */ + >; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + + status = "okay"; +}; + +&usb { + status = "okay"; +}; + +&usb_ctrl_mod { + status = "okay"; +}; + +&usb0_phy { + status = "okay"; +}; + +&usb1_phy { + status = "okay"; +}; + +&usb0 { + status = "okay"; + dr_mode = "peripheral"; +}; + +&usb1 { + status = "okay"; + dr_mode = "host"; +}; + +&cppi41dma { + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + + status = "okay"; + clock-frequency = <400000>; + + tps: tps@24 { + reg = <0x24>; + }; + + baseboard_eeprom: baseboard_eeprom@50 { + compatible = "at,24c256"; + reg = <0x50>; + + #address-cells = <1>; + #size-cells = <1>; + baseboard_data: baseboard_data@0 { + reg = <0 0x100>; + }; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + status = "okay"; + clock-frequency = <100000>; + + gl_gyro: gl_gyro@68 { + compatible = "gl_mpu6050"; + reg = <0x68>; + status = "okay"; + }; + + cape_eeprom0: cape_eeprom0@54 { + compatible = "at,24c256"; + reg = <0x54>; + #address-cells = <1>; + #size-cells = <1>; + cape0_data: cape_data@0 { + reg = <0 0x100>; + }; + }; + + cape_eeprom1: cape_eeprom1@55 { + compatible = "at,24c256"; + reg = <0x55>; + #address-cells = <1>; + #size-cells = <1>; + cape1_data: cape_data@0 { + reg = <0 0x100>; + }; + }; + + cape_eeprom2: cape_eeprom2@56 { + compatible = "at,24c256"; + reg = <0x56>; + #address-cells = <1>; + #size-cells = <1>; + cape2_data: cape_data@0 { + reg = <0 0x100>; + }; + }; + + cape_eeprom3: cape_eeprom3@57 { + compatible = "at,24c256"; + reg = <0x57>; + #address-cells = <1>; + #size-cells = <1>; + cape3_data: cape_data@0 { + reg = <0 0x100>; + }; + }; +}; + + +/include/ "tps65217.dtsi" + +&tps { + /* + * Configure pmic to enter OFF-state instead of SLEEP-state ("RTC-only + * mode") at poweroff. Most BeagleBone versions do not support RTC-only + * mode and risk hardware damage if this mode is entered. + * + * For details, see linux-omap mailing list May 2015 thread + * [PATCH] ARM: dts: am335x-bone* enable pmic-shutdown-controller + * In particular, messages: + * http://www.spinics.net/lists/linux-omap/msg118585.html + * http://www.spinics.net/lists/linux-omap/msg118615.html + * + * You can override this later with + * &tps { /delete-property/ ti,pmic-shutdown-controller; } + * if you want to use RTC-only mode and made sure you are not affected + * by the hardware problems. (Tip: double-check by performing a current + * measurement after shutdown: it should be less than 1 mA.) + */ + + interrupts = <7>; /* NMI */ + interrupt-parent = <&intc>; + + ti,pmic-shutdown-controller; + + charger { + interrupts = , ; + interrupts-names = "AC", "USB"; + status = "okay"; + }; + + pwrbutton { + interrupts = ; + status = "okay"; + }; + + regulators { + dcdc1_reg: regulator@0 { + regulator-name = "vdds_dpr"; + regulator-always-on; + }; + + dcdc2_reg: regulator@1 { + /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */ + regulator-name = "vdd_mpu"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1351500>; + regulator-boot-on; + regulator-always-on; + }; + + dcdc3_reg: regulator@2 { + /* VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */ + regulator-name = "vdd_core"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1150000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1_reg: regulator@3 { + regulator-name = "vio,vrtc,vdds"; + regulator-always-on; + }; + + ldo2_reg: regulator@4 { + regulator-name = "vdd_3v3aux"; + regulator-always-on; + }; + + ldo3_reg: regulator@5 { + regulator-name = "vdd_1v8"; + regulator-always-on; + }; + + ldo4_reg: regulator@6 { + regulator-name = "vdd_3v3a"; + regulator-always-on; + }; + }; +}; + +&cpsw_emac0 { + phy_id = <&davinci_mdio>, <0>; + phy-mode = "mii"; +}; + +&mac { + slaves = <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cpsw_default>; + pinctrl-1 = <&cpsw_sleep>; + status = "okay"; +}; + +&davinci_mdio { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&davinci_mdio_default>; + pinctrl-1 = <&davinci_mdio_sleep>; + status = "okay"; +}; + +&mmc1 { + status = "okay"; + bus-width = <0x4>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins>; + cd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; +}; + +&aes { + status = "okay"; +}; + +&sham { + status = "okay"; +}; + +&rtc { + clocks = <&clk_32768_ck>, <&clkdiv32k_ick>; + clock-names = "ext-clk", "int-clk"; + system-power-controller; +}; + +&wkup_m3_ipc { + ti,scale-data-fw = "am335x-bone-scale-data.bin"; +}; + +&pruss_soc_bus { + status = "okay"; + + pruss: pruss@4a300000 { + status = "okay"; + + pru0: pru@4a334000 { + status = "okay"; + }; + + pru1: pru@4a338000 { + status = "okay"; + }; + }; +}; + +/* the cape manager */ +/ { + bone_capemgr { + compatible = "ti,bone-capemgr"; + status = "okay"; + + nvmem-cells = <&baseboard_data &cape0_data &cape1_data &cape2_data &cape3_data>; + nvmem-cell-names = "baseboard", "slot0", "slot1", "slot2", "slot3"; + #slots = <4>; + + /* map board revisions to compatible definitions */ + baseboardmaps { + baseboard_beaglebone: board@0 { + board-name = "A335BONE"; + compatible-name = "ti,beaglebone"; + }; + + baseboard_beaglebone_black: board@1 { + board-name = "A335BNLT"; + compatible-name = "ti,beaglebone-black"; + }; + }; + }; +}; diff --git a/mpu6050/mpu6050-regs.h b/mpu6050/mpu6050-regs.h new file mode 100644 index 0000000..9d51680 --- /dev/null +++ b/mpu6050/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/mpu6050/mpu6050.c b/mpu6050/mpu6050.c new file mode 100644 index 0000000..b91bf6b --- /dev/null +++ b/mpu6050/mpu6050.c @@ -0,0 +1,278 @@ +#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; + + if (g_mpu6050_data.drv_client == 0) + return -ENODEV; + + /* accel */ + g_mpu6050_data.accel_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_XOUT_H)); + g_mpu6050_data.accel_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_YOUT_H)); + g_mpu6050_data.accel_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_ZOUT_H)); + /* gyro */ + g_mpu6050_data.gyro_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_XOUT_H)); + g_mpu6050_data.gyro_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_YOUT_H)); + g_mpu6050_data.gyro_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_ZOUT_H)); + /* temp */ + /* Temperature in degrees C = (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ + temp = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_TEMP_OUT_H)); + g_mpu6050_data.temperature = (temp + 12420 + 170) / 340; + + printk(KERN_INFO "mpu6050: sensor data read:\n"); + printk(KERN_INFO "mpu6050: 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]); + printk(KERN_INFO "mpu6050: 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]); + printk(KERN_INFO "mpu6050: 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; + + printk(KERN_INFO "mpu6050: 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)) { + printk(KERN_ERR "mpu6050: i2c_smbus_read_byte_data() failed with error: %d\n", ret); + return ret; + } + if (ret != MPU6050_WHO_AM_I) { + printk(KERN_ERR "mpu6050: wrong i2c device found: expected 0x%X, found 0x%X\n", MPU6050_WHO_AM_I, ret); + return -1; + } + printk(KERN_INFO "mpu6050: 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; + + printk(KERN_INFO "mpu6050: i2c driver probed\n"); + return 0; +} + +static int mpu6050_remove(struct i2c_client *drv_client) +{ + g_mpu6050_data.drv_client = 0; + + printk(KERN_INFO "mpu6050: i2c driver removed\n"); + return 0; +} + +static const struct i2c_device_id mpu6050_idtable [] = { + { "gl_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 = 0; + +static int mpu6050_init(void) +{ + int ret; + + /* Create i2c driver */ + ret = i2c_add_driver(&mpu6050_i2c_driver); + if (ret) { + printk(KERN_ERR "mpu6050: failed to add new i2c driver: %d\n", ret); + return ret; + } + printk(KERN_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); + printk(KERN_ERR "mpu6050: failed to create sysfs class: %d\n", ret); + return ret; + } + printk(KERN_INFO "mpu6050: sysfs class created\n"); + + /* Create accel_x */ + ret = class_create_file(attr_class, &class_attr_accel_x); + if (ret) { + printk(KERN_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) { + printk(KERN_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) { + printk(KERN_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) { + printk(KERN_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) { + printk(KERN_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) { + printk(KERN_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) { + printk(KERN_ERR "mpu6050: failed to create sysfs class attribute temperature: %d\n", ret); + return ret; + } + + printk(KERN_INFO "mpu6050: sysfs class attributes created\n"); + + printk(KERN_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); + printk(KERN_INFO "mpu6050: sysfs class attributes removed\n"); + + class_destroy(attr_class); + printk(KERN_INFO "mpu6050: sysfs class destroyed\n"); + } + + i2c_del_driver(&mpu6050_i2c_driver); + printk(KERN_INFO "mpu6050: i2c driver deleted\n"); + + printk(KERN_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 1353e20d9bccf0c71a693e9c2648981f0c22be44 Mon Sep 17 00:00:00 2001 From: ruvi Date: Sun, 26 Nov 2017 23:15:55 +0200 Subject: [PATCH 6/9] Simplified show interface for sysfs added --- mpu6050/mpu6050.c | 182 +++++++++++++--------------------------------- 1 file changed, 50 insertions(+), 132 deletions(-) diff --git a/mpu6050/mpu6050.c b/mpu6050/mpu6050.c index b91bf6b..3af5d37 100644 --- a/mpu6050/mpu6050.c +++ b/mpu6050/mpu6050.c @@ -6,14 +6,13 @@ #include #include "mpu6050-regs.h" - + +#define MPU6050_VALUE_NUM 7 struct mpu6050_data { struct i2c_client *drv_client; - int accel_values[3]; - int gyro_values[3]; - int temperature; + int values[MPU6050_VALUE_NUM]; }; static struct mpu6050_data g_mpu6050_data; @@ -26,28 +25,28 @@ static int mpu6050_read_data(void) return -ENODEV; /* accel */ - g_mpu6050_data.accel_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_XOUT_H)); - g_mpu6050_data.accel_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_YOUT_H)); - g_mpu6050_data.accel_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_ZOUT_H)); + g_mpu6050_data.values[0] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_XOUT_H)); + g_mpu6050_data.values[1] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_YOUT_H)); + g_mpu6050_data.values[2] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_ACCEL_ZOUT_H)); /* gyro */ - g_mpu6050_data.gyro_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_XOUT_H)); - g_mpu6050_data.gyro_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_YOUT_H)); - g_mpu6050_data.gyro_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_ZOUT_H)); + g_mpu6050_data.values[3] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_XOUT_H)); + g_mpu6050_data.values[4] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_YOUT_H)); + g_mpu6050_data.values[5] = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_GYRO_ZOUT_H)); /* temp */ /* Temperature in degrees C = (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ temp = (s16)((u16)i2c_smbus_read_word_swapped(g_mpu6050_data.drv_client, REG_TEMP_OUT_H)); - g_mpu6050_data.temperature = (temp + 12420 + 170) / 340; + g_mpu6050_data.values[6] = (temp + 12420 + 170) / 340; printk(KERN_INFO "mpu6050: sensor data read:\n"); - printk(KERN_INFO "mpu6050: 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]); - printk(KERN_INFO "mpu6050: 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]); - printk(KERN_INFO "mpu6050: TEMP = %d\n", g_mpu6050_data.temperature); + printk(KERN_INFO "mpu6050: ACCEL=%d, %d, %d\n", + g_mpu6050_data.values[0], + g_mpu6050_data.values[1], + g_mpu6050_data.values[2]); + printk(KERN_INFO "mpu6050: GYRO=%d, %d, %d\n", + g_mpu6050_data.values[3], + g_mpu6050_data.values[4], + g_mpu6050_data.values[5]); + printk(KERN_INFO "mpu6050: TEMP = %d\n", g_mpu6050_data.values[6]); return 0; } @@ -111,75 +110,34 @@ 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) -{ - 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 show_value(struct class *class, struct class_attribute *attr, char *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); -} +struct class_attribute class_attr_array[MPU6050_VALUE_NUM] = { + { .attr = { .name = "accel_x", .mode = S_IRUGO }, .show = &show_value, }, + { .attr = { .name = "accel_y", .mode = S_IRUGO }, .show = &show_value, }, + { .attr = { .name = "accel_z", .mode = S_IRUGO }, .show = &show_value, }, + { .attr = { .name = "gyro_x", .mode = S_IRUGO }, .show = &show_value, }, + { .attr = { .name = "gyro_y", .mode = S_IRUGO }, .show = &show_value, }, + { .attr = { .name = "gyro_z", .mode = S_IRUGO }, .show = &show_value, }, + { .attr = { .name = "temperature", .mode = S_IRUGO }, .show = &show_value, }, +}; -static ssize_t temp_show(struct class *class, struct class_attribute *attr, char *buf) -{ +static ssize_t show_value(struct class *class, struct class_attribute *attr, char *buf) { + int index = attr - class_attr_array; + int value = 0; mpu6050_read_data(); - - sprintf(buf, "%d\n", g_mpu6050_data.temperature); + if (index >=0 && index < MPU6050_VALUE_NUM) { + value = g_mpu6050_data.values[index]; + } + sprintf(buf, "%d\n", value); 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 = 0; static int mpu6050_init(void) { - int ret; + int ret, i; /* Create i2c driver */ ret = i2c_add_driver(&mpu6050_i2c_driver); @@ -198,48 +156,13 @@ static int mpu6050_init(void) } printk(KERN_INFO "mpu6050: sysfs class created\n"); - /* Create accel_x */ - ret = class_create_file(attr_class, &class_attr_accel_x); - if (ret) { - printk(KERN_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) { - printk(KERN_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) { - printk(KERN_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) { - printk(KERN_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) { - printk(KERN_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) { - printk(KERN_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) { - printk(KERN_ERR "mpu6050: failed to create sysfs class attribute temperature: %d\n", ret); - return ret; - } + for (i = 0; i < MPU6050_VALUE_NUM; ++i) { + ret = class_create_file(attr_class, &class_attr_array[i]); + if (ret) { + printk(KERN_ERR "mpu6050: failed to create sysfs class attribute accel_x: %d\n", ret); + return ret; + } + } printk(KERN_INFO "mpu6050: sysfs class attributes created\n"); @@ -250,17 +173,12 @@ static int mpu6050_init(void) 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); - printk(KERN_INFO "mpu6050: sysfs class attributes removed\n"); - - class_destroy(attr_class); - printk(KERN_INFO "mpu6050: sysfs class destroyed\n"); + int i; + + for (i = 0; i < MPU6050_VALUE_NUM; ++i) { + class_remove_file(attr_class, &class_attr_array[i]); + } + class_destroy(attr_class); } i2c_del_driver(&mpu6050_i2c_driver); From f36b8e7f2ffe720a6445e01a9bef36a8cc06e10c Mon Sep 17 00:00:00 2001 From: ruvi Date: Mon, 27 Nov 2017 14:09:26 +0200 Subject: [PATCH 7/9] Mpu6050 devfs driver added. Mpu6050 read procedure is initiated by request from user space. --- mpu6050/Makefile | 1 + mpu6050/mpu6050_dev.c | 402 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+) create mode 100644 mpu6050/mpu6050_dev.c diff --git a/mpu6050/Makefile b/mpu6050/Makefile index 7e1e885..58dd4e0 100644 --- a/mpu6050/Makefile +++ b/mpu6050/Makefile @@ -4,6 +4,7 @@ ifneq ($(KERNELRELEASE),) obj-m += mpu6050.o +obj-m += mpu6050_dev.o else diff --git a/mpu6050/mpu6050_dev.c b/mpu6050/mpu6050_dev.c new file mode 100644 index 0000000..ea01212 --- /dev/null +++ b/mpu6050/mpu6050_dev.c @@ -0,0 +1,402 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mpu6050-regs.h" + +MODULE_LICENSE( "GPL" ); +MODULE_AUTHOR( "Ruslan V. Sukhov " ); +MODULE_VERSION( "1.1" ); + +static struct i2c_client* mpu6050_i2c_client; + +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; +#define MPU6050_BUF_SIZE 10 +struct mpu6050_buf_data +{ + unsigned int size; + unsigned int read_index; + unsigned int write_index; + unsigned int count; + struct mpu6050_data buf[MPU6050_BUF_SIZE]; +}; + +//static struct mpu6050_buf_data* buf_data; + +static struct mpu6050_buf_data buf_data = { + .size = MPU6050_BUF_SIZE, + .read_index = 0, + .write_index = 0, + .count = 0, +}; +/* +static int mpu6050_buf_init(struct mpu6050_buf_data* bd) +{ + bd = kmalloc(sizeof(struct mpu6050_buf_data),GFP_KERNEL); + bd->size = MPU6050_BUF_SIZE; + bd->read_index = 0; + bd->write_index = 0; + bd->count = 0; + bd->buf = kmalloc(sizeof(struct mpu6050_data) * (bd->size),GFP_KERNEL); + return 0; +} + +static int mpu6050_buf_deinit(struct mpu6050_buf_data* bd) +{ + kfree(bd->buf); + kfree(bd); + return 0; +} +*/ +static int mpu6050_buf_add(struct mpu6050_buf_data* bd, struct mpu6050_data* d) +{ + int i; + for (i = 0; i < 3; i++) { + (bd->buf[bd->write_index]).accel_values[i] = (d->accel_values)[i]; + (bd->buf[bd->write_index]).gyro_values[i] = (d->gyro_values)[i]; + } + (bd->buf[bd->write_index]).temperature = d->temperature; + + if (bd->count < bd->size) { + (bd->write_index)++; + if (bd->write_index == bd->size) bd->write_index = 0; + (bd->count)++; + } + else { + (bd->write_index)++; + (bd->read_index)++; + if (bd->write_index == bd->size) bd->write_index = 0; + if (bd->read_index == bd->size) bd->read_index = 0; + } + return 0; +} + +static int mpu6050_buf_get(struct mpu6050_buf_data* bd, struct mpu6050_data* d) +{ + if (bd->count > 0) { + int i; + for (i = 0; i < 3; i++) { + (d->accel_values)[i] = (bd->buf[bd->read_index]).accel_values[i]; + (d->gyro_values)[i] = (bd->buf[bd->read_index]).gyro_values[i]; + } + d->temperature = (bd->buf[bd->read_index]).temperature; + (bd->read_index)++; + if (bd->read_index == bd->size) bd->read_index = 0; + (bd->count)--; + } + return 0; +} +static int mpu6050_read_data(struct mpu6050_data* p_mpu6050_data) +{ + int temp; + + if (mpu6050_i2c_client == 0) + return -ENODEV; + + /* accel */ + p_mpu6050_data->accel_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_ACCEL_XOUT_H)); + p_mpu6050_data->accel_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_ACCEL_YOUT_H)); + p_mpu6050_data->accel_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_ACCEL_ZOUT_H)); + /* gyro */ + p_mpu6050_data->gyro_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_GYRO_XOUT_H)); + p_mpu6050_data->gyro_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_GYRO_YOUT_H)); + p_mpu6050_data->gyro_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_GYRO_ZOUT_H)); + /* temp */ + /* Temperature in degrees C = (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ + temp = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_TEMP_OUT_H)); + p_mpu6050_data->temperature = (temp + 12420 + 170) / 340; + + printk(KERN_INFO "mpu6050: sensor data read:\n"); + printk(KERN_INFO "ACC = %d; %d; %d\n", + p_mpu6050_data->accel_values[0], + p_mpu6050_data->accel_values[1], + p_mpu6050_data->accel_values[2]); + printk(KERN_INFO "GYRO = %d; %d; %d\n", + p_mpu6050_data->gyro_values[0], + p_mpu6050_data->gyro_values[1], + p_mpu6050_data->gyro_values[2]); + printk(KERN_INFO "TEMP = %d\n", p_mpu6050_data->temperature); + return 0; +} + +static int mpu6050_probe(struct i2c_client *drv_client, const struct i2c_device_id *id) +{ + int ret; + + printk(KERN_INFO "mpu6050: 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)) { + printk(KERN_ERR "mpu6050: i2c_smbus_read_byte_data() failed with error: %d\n", ret); + return ret; + } + if (ret != MPU6050_WHO_AM_I) { + printk(KERN_ERR "mpu6050: wrong i2c device found: expected 0x%X, found 0x%X\n", MPU6050_WHO_AM_I, ret); + return -1; + } + printk(KERN_INFO "mpu6050: 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); + + mpu6050_i2c_client = drv_client; + + printk(KERN_INFO "mpu6050: i2c driver probed\n"); + return 0; +} + +static int mpu6050_remove(struct i2c_client *drv_client) +{ + mpu6050_i2c_client = 0; + + printk(KERN_INFO "mpu6050: i2c driver removed\n"); + return 0; +} + +static const struct i2c_device_id mpu6050_idtable [] = { + { "gl_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 char mpu6050_str[100]; // buffer! + +static int major_current = 0; +static int major_all = 0; +module_param( major_current, int, S_IRUGO ); +module_param( major_all, int, S_IRUGO ); + +static ssize_t dev_current_read( struct file * file, char * buf,size_t count, loff_t *ppos ) { + struct mpu6050_data tmp_data; + int len; + static int execute = 0; + if (execute == 0) { + mpu6050_read_data(&tmp_data); + mpu6050_buf_add(&buf_data, &tmp_data); + sprintf(mpu6050_str, "ACC = %d; %d; %d\nGYRO = %d; %d; %d;\nT = %d\n\0", tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], + tmp_data.gyro_values[0], tmp_data.gyro_values[1], tmp_data.gyro_values[2], + tmp_data.temperature); + + len = strlen( mpu6050_str ); + execute = 1; + } + else { + len = 0; + execute = 0; + } + + if( count < len ) return -EINVAL; + if( *ppos != 0 ) return 0; + if( copy_to_user( buf, mpu6050_str, len ) ) return -EINVAL; + *ppos = len; + return len; +} + +static const struct file_operations dev_current_fops = { + .owner = THIS_MODULE, + .read = dev_current_read, +}; + +static ssize_t dev_all_read( struct file * file, char * buf,size_t count, loff_t *ppos ) { + struct mpu6050_data tmp_data; + int offset = 0, len = 0, i, tmp; + static int execute = 0; + if (execute == 0) { + sprintf(mpu6050_str,"Buffer contains %d samples:\n",buf_data.count); + len = strlen( mpu6050_str ); + if( count < len ) return -EINVAL; + if( *ppos != 0 ) return 0; + offset += len; + if( copy_to_user( buf, mpu6050_str, len ) ) return -EINVAL; + tmp = buf_data.count; + for(i = 0; i < tmp; i++) { + mpu6050_buf_get(&buf_data, &tmp_data); + sprintf(mpu6050_str, "ACC = %d; %d; %d\nGYRO = %d; %d; %d\nT = %d\n%d\0", tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], + tmp_data.gyro_values[0], tmp_data.gyro_values[1], tmp_data.gyro_values[2], + tmp_data.temperature, buf_data.count); + len = strlen( mpu6050_str ); + if( copy_to_user( buf + offset, mpu6050_str, len ) ) return -EINVAL; + offset += len; + } + execute = 1; + } + else { + offset = 0; + execute = 0; + } + *ppos = offset; + return offset; +} + +static const struct file_operations dev_all_fops = { + .owner = THIS_MODULE, + .read = dev_all_read, +}; + +#define DEVICE_CURRENT_FIRST 0 +#define DEVICE_ALL_FIRST 0 +#define DEVICE_COUNT 1 +#define MOD_CURRENT_NAME "mpu6050_current_mod" +#define MOD_ALL_NAME "mpu6050_all_mod" + +static struct cdev h_current_cdev; +static struct cdev h_all_cdev; +static struct class *devclass; + +static int __init dev_init( void ) { + int ret, i; + dev_t dev; + ret = i2c_add_driver(&mpu6050_i2c_driver); + if (ret < 0) { + printk(KERN_ERR "mpu6050: failed to add new i2c driver: %d\n", ret); + goto err; + } + printk(KERN_INFO "mpu6050: i2c driver created\n"); +//current + if( major_current != 0 ) { + dev = MKDEV( major_current, DEVICE_CURRENT_FIRST ); + ret = register_chrdev_region( dev, DEVICE_COUNT, MOD_CURRENT_NAME ); + } + else { + ret = alloc_chrdev_region( &dev, DEVICE_CURRENT_FIRST, DEVICE_COUNT, MOD_CURRENT_NAME ); + major_current = MAJOR( dev ); // не забыть зафиксировать! + } + if( ret < 0 ) { + printk( KERN_ERR "Can not register char device region\n" ); + goto err; + } + cdev_init( &h_current_cdev, &dev_current_fops ); + h_current_cdev.owner = THIS_MODULE; + h_current_cdev.ops = &dev_current_fops; // обязательно! - cdev_init() недостаточно? + ret = cdev_add( &h_current_cdev, dev, DEVICE_COUNT ); + if( ret < 0 ) { + unregister_chrdev_region( MKDEV( major_current, DEVICE_CURRENT_FIRST ), DEVICE_COUNT ); + printk( KERN_ERR "Can not add char device\n" ); + goto err; + } + devclass = class_create( THIS_MODULE, "mpu6050" ); + for (i = 0; i < DEVICE_COUNT; i++) { + #define DEV_CURRENT_NAME "mpu6050current" + dev = MKDEV( major_current, DEVICE_CURRENT_FIRST + i); + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26) + /* struct device *device_create( struct class *cls, struct device *parent, + dev_t devt, const char *fmt, ...); */ + device_create( devclass, NULL, dev, "%s_%d", DEV_CURRENT_NAME, i ); + #else // прототип device_create() изменился! + /* struct device *device_create( struct class *cls, struct device *parent, + dev_t devt, void *drvdata, const char *fmt, ...); */ + device_create( devclass, NULL, dev, NULL, "%s_%d", DEV_CURRENT_NAME, i ); + #endif + } + printk( KERN_INFO "=========== module installed %d:%d ==============\n", + MAJOR( dev ), MINOR( dev ) ); + +//all + if( major_all != 0 ) { + dev = MKDEV( major_all, DEVICE_ALL_FIRST ); + ret = register_chrdev_region( dev, DEVICE_COUNT, MOD_ALL_NAME ); + } + else { + ret = alloc_chrdev_region( &dev, DEVICE_ALL_FIRST, DEVICE_COUNT, MOD_ALL_NAME ); + major_all = MAJOR( dev ); // не забыть зафиксировать! + } + if( ret < 0 ) { + printk( KERN_ERR "Can not register char device region\n" ); + goto err; + } + cdev_init( &h_all_cdev, &dev_all_fops ); + h_all_cdev.owner = THIS_MODULE; + h_all_cdev.ops = &dev_all_fops; // обязательно! - cdev_init() недостаточно? + ret = cdev_add( &h_all_cdev, dev, DEVICE_COUNT ); + if( ret < 0 ) { + unregister_chrdev_region( MKDEV( major_all, DEVICE_ALL_FIRST ), DEVICE_COUNT ); + printk( KERN_ERR "Can not add char device\n" ); + goto err; + } + for (i = 0; i < DEVICE_COUNT; i++) { + #define DEV_ALL_NAME "mpu6050all" + dev = MKDEV( major_all, DEVICE_ALL_FIRST + i); + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26) + /* struct device *device_create( struct class *cls, struct device *parent, + dev_t devt, const char *fmt, ...); */ + device_create( devclass, NULL, dev, "%s_%d", DEV_ALL_NAME, i ); + #else // прототип device_create() изменился! + /* struct device *device_create( struct class *cls, struct device *parent, + dev_t devt, void *drvdata, const char *fmt, ...); */ + device_create( devclass, NULL, dev, NULL, "%s_%d", DEV_ALL_NAME, i ); + #endif + } + printk( KERN_INFO "=========== module installed %d:%d ==============\n", + MAJOR( dev ), MINOR( dev ) ); + +// mpu6050_buf_init(buf_data); + + err: + return ret; +} + +static void __exit dev_exit( void ) { + dev_t dev; + int i; +// mpu6050_buf_deinit(buf_data); + i2c_del_driver(&mpu6050_i2c_driver); +//current + for (i = 0; i < DEVICE_COUNT; i++) { + dev = MKDEV( major_current, DEVICE_CURRENT_FIRST + i); + device_destroy( devclass, dev ); + } +//all + for (i = 0; i < DEVICE_COUNT; i++) { + dev = MKDEV( major_all, DEVICE_CURRENT_FIRST + i); + device_destroy( devclass, dev ); + } + + class_destroy( devclass ); +//currnet + cdev_del( &h_current_cdev ); + unregister_chrdev_region( MKDEV( major_current, DEVICE_CURRENT_FIRST ), DEVICE_COUNT ); + printk( KERN_INFO "=============== module removed ==================\n" ); +//all + cdev_del( &h_all_cdev ); + unregister_chrdev_region( MKDEV( major_all, DEVICE_ALL_FIRST ), DEVICE_COUNT ); + printk( KERN_INFO "=============== module removed ==================\n" ); +} + +module_init( dev_init ); +module_exit( dev_exit ); From 1eedc446478293a66758283c8333a0b30c88bcb3 Mon Sep 17 00:00:00 2001 From: ruvi Date: Mon, 4 Dec 2017 20:11:52 +0200 Subject: [PATCH 8/9] Dynamic memory management implemented --- mpu6050/Makefile | 1 + mpu6050/mpu6050_dev.c | 445 ++++++++++++++++++++++++++---------------- 2 files changed, 275 insertions(+), 171 deletions(-) diff --git a/mpu6050/Makefile b/mpu6050/Makefile index 58dd4e0..6d5168d 100644 --- a/mpu6050/Makefile +++ b/mpu6050/Makefile @@ -5,6 +5,7 @@ ifneq ($(KERNELRELEASE),) obj-m += mpu6050.o obj-m += mpu6050_dev.o +#obj-m += mpu6050_test.o else diff --git a/mpu6050/mpu6050_dev.c b/mpu6050/mpu6050_dev.c index ea01212..079c227 100644 --- a/mpu6050/mpu6050_dev.c +++ b/mpu6050/mpu6050_dev.c @@ -9,61 +9,78 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "mpu6050-regs.h" MODULE_LICENSE( "GPL" ); MODULE_AUTHOR( "Ruslan V. Sukhov " ); MODULE_VERSION( "1.1" ); -static struct i2c_client* mpu6050_i2c_client; - -struct mpu6050_data -{ -// struct i2c_client *drv_client; +struct mpu6050_data { + struct timespec t; int accel_values[3]; int gyro_values[3]; int temperature; }; -//static struct mpu6050_data g_mpu6050_data; #define MPU6050_BUF_SIZE 10 -struct mpu6050_buf_data -{ +struct mpu6050_buf_data { unsigned int size; unsigned int read_index; unsigned int write_index; unsigned int count; - struct mpu6050_data buf[MPU6050_BUF_SIZE]; + struct mpu6050_data* buf; }; -//static struct mpu6050_buf_data* buf_data; - -static struct mpu6050_buf_data buf_data = { - .size = MPU6050_BUF_SIZE, - .read_index = 0, - .write_index = 0, - .count = 0, -}; -/* -static int mpu6050_buf_init(struct mpu6050_buf_data* bd) -{ +struct mpu6050_dev_context { + struct cdev cdev; + struct class* devclass; + struct i2c_client* dev_client; + struct mpu6050_buf_data* buf_data; + struct task_struct* read_task; + struct rt_mutex buf_mutex; + struct rt_mutex dev_current_mutex; + struct rt_mutex dev_all_mutex; +}* context; + +static struct mpu6050_buf_data* mpu6050_buf_init(void) { + struct mpu6050_buf_data* bd; bd = kmalloc(sizeof(struct mpu6050_buf_data),GFP_KERNEL); + if (!bd) + { + printk(KERN_ERR "mpu6050: mpu6050_buf_data memory allocation error"); + return 0; + } bd->size = MPU6050_BUF_SIZE; bd->read_index = 0; bd->write_index = 0; bd->count = 0; + + bd->buf = 0; bd->buf = kmalloc(sizeof(struct mpu6050_data) * (bd->size),GFP_KERNEL); - return 0; + if (!(bd->buf)) { + kfree(bd); + printk(KERN_ERR "mpu6050: mpu6050_buf memory allocation error"); + return 0; + } + return bd; } -static int mpu6050_buf_deinit(struct mpu6050_buf_data* bd) +static void mpu6050_buf_deinit(struct mpu6050_buf_data* bd) { kfree(bd->buf); kfree(bd); - return 0; + bd = 0; + return; } -*/ + static int mpu6050_buf_add(struct mpu6050_buf_data* bd, struct mpu6050_data* d) { int i; @@ -72,6 +89,7 @@ static int mpu6050_buf_add(struct mpu6050_buf_data* bd, struct mpu6050_data* d) (bd->buf[bd->write_index]).gyro_values[i] = (d->gyro_values)[i]; } (bd->buf[bd->write_index]).temperature = d->temperature; + (bd->buf[bd->write_index]).t = d->t; if (bd->count < bd->size) { (bd->write_index)++; @@ -96,32 +114,35 @@ static int mpu6050_buf_get(struct mpu6050_buf_data* bd, struct mpu6050_data* d) (d->gyro_values)[i] = (bd->buf[bd->read_index]).gyro_values[i]; } d->temperature = (bd->buf[bd->read_index]).temperature; + d->t = (bd->buf[bd->read_index]).t; (bd->read_index)++; if (bd->read_index == bd->size) bd->read_index = 0; (bd->count)--; } return 0; } + static int mpu6050_read_data(struct mpu6050_data* p_mpu6050_data) { int temp; - if (mpu6050_i2c_client == 0) + if (context->dev_client == 0) return -ENODEV; /* accel */ - p_mpu6050_data->accel_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_ACCEL_XOUT_H)); - p_mpu6050_data->accel_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_ACCEL_YOUT_H)); - p_mpu6050_data->accel_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_ACCEL_ZOUT_H)); + p_mpu6050_data->accel_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_ACCEL_XOUT_H)); + p_mpu6050_data->accel_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_ACCEL_YOUT_H)); + p_mpu6050_data->accel_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_ACCEL_ZOUT_H)); /* gyro */ - p_mpu6050_data->gyro_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_GYRO_XOUT_H)); - p_mpu6050_data->gyro_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_GYRO_YOUT_H)); - p_mpu6050_data->gyro_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_GYRO_ZOUT_H)); + p_mpu6050_data->gyro_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_GYRO_XOUT_H)); + p_mpu6050_data->gyro_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_GYRO_YOUT_H)); + p_mpu6050_data->gyro_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_GYRO_ZOUT_H)); /* temp */ /* Temperature in degrees C = (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ - temp = (s16)((u16)i2c_smbus_read_word_swapped(mpu6050_i2c_client, REG_TEMP_OUT_H)); + temp = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_TEMP_OUT_H)); p_mpu6050_data->temperature = (temp + 12420 + 170) / 340; - + p_mpu6050_data->t = current_kernel_time(); + printk(KERN_INFO "mpu6050: sensor data read:\n"); printk(KERN_INFO "ACC = %d; %d; %d\n", p_mpu6050_data->accel_values[0], @@ -135,6 +156,26 @@ static int mpu6050_read_data(struct mpu6050_data* p_mpu6050_data) return 0; } +static int period_ms = 1000; +module_param( period_ms, int, S_IRUGO ); +#define MPU6050_THREAD_NAME "mpu6050_read_thread" +static int mpu6050_read_thread( void* data ) { + struct mpu6050_data tmp_data; + struct timespec start_time, stop_time; + for(;;) { + start_time = current_kernel_time(); + set_current_state(TASK_UNINTERRUPTIBLE); + if(kthread_should_stop()) break; + mpu6050_read_data(&tmp_data); + rt_mutex_lock(&context->buf_mutex); + mpu6050_buf_add(context->buf_data, &tmp_data); + rt_mutex_unlock(&context->buf_mutex); + stop_time = current_kernel_time(); + msleep(period_ms - (stop_time.tv_nsec - start_time.tv_nsec)/1000000); + } + return 0; +} + static int mpu6050_probe(struct i2c_client *drv_client, const struct i2c_device_id *id) { int ret; @@ -165,7 +206,7 @@ static int mpu6050_probe(struct i2c_client *drv_client, const struct i2c_device_ i2c_smbus_write_byte_data(drv_client, REG_PWR_MGMT_1, 0); i2c_smbus_write_byte_data(drv_client, REG_PWR_MGMT_2, 0); - mpu6050_i2c_client = drv_client; + context->dev_client = drv_client; printk(KERN_INFO "mpu6050: i2c driver probed\n"); return 0; @@ -173,7 +214,7 @@ static int mpu6050_probe(struct i2c_client *drv_client, const struct i2c_device_ static int mpu6050_remove(struct i2c_client *drv_client) { - mpu6050_i2c_client = 0; + context->dev_client = 0; printk(KERN_INFO "mpu6050: i2c driver removed\n"); return 0; @@ -195,32 +236,88 @@ static struct i2c_driver mpu6050_i2c_driver = { .id_table = mpu6050_idtable, }; -static char mpu6050_str[100]; // buffer! +#define MPU6050_STR_SIZE 100 +static char mpu6050_str[MPU6050_STR_SIZE]; -static int major_current = 0; -static int major_all = 0; -module_param( major_current, int, S_IRUGO ); -module_param( major_all, int, S_IRUGO ); +#define DEVICE_FIRST 0 +#define DEVICE_COUNT 2 + +#define DEVICE_CURRENT_NUMBER 0 +#define DEVICE_ALL_NUMBER 1 + +static int major = 0; +module_param( major, int, S_IRUGO ); + +static int dev_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct mpu6050_dev_context *ctx = container_of(inode->i_cdev, struct mpu6050_dev_context, cdev); + switch (minor) { + case DEVICE_CURRENT_NUMBER: + if (rt_mutex_trylock(&ctx->dev_current_mutex) == 0) { + printk(KERN_INFO "mpu6050: %d minor device is busy\n", minor); + return -EBUSY; + } + break; + case DEVICE_ALL_NUMBER: + if (rt_mutex_trylock(&ctx->dev_all_mutex) == 0) { + printk(KERN_INFO "mpu6050: %d minor device is busy\n", minor); + return -EBUSY; + } + break; + default: + printk(KERN_ERR "mpu6050: unknown minor number: %d\n", minor); + return -EFAULT; + } + printk(KERN_INFO "mpu6050: %d minor device is locked\n", minor); + file->private_data = ctx; + return 0; +} + +static int dev_release(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct mpu6050_dev_context *ctx = file->private_data; + switch (minor) { + case DEVICE_CURRENT_NUMBER: + rt_mutex_unlock(&ctx->dev_current_mutex); + break; + case DEVICE_ALL_NUMBER: + rt_mutex_unlock(&ctx->dev_all_mutex); + break; + default: + printk(KERN_ERR "mpu6050: unknown minor number: %d\n", minor); + return -EFAULT; + } + printk(KERN_INFO "mpu6050: %d minor device is unlocked\n", minor); + return 0; +} static ssize_t dev_current_read( struct file * file, char * buf,size_t count, loff_t *ppos ) { + struct mpu6050_dev_context *ctx = file->private_data; + struct mpu6050_buf_data * p_buf_data = ctx->buf_data; struct mpu6050_data tmp_data; - int len; - static int execute = 0; - if (execute == 0) { - mpu6050_read_data(&tmp_data); - mpu6050_buf_add(&buf_data, &tmp_data); - sprintf(mpu6050_str, "ACC = %d; %d; %d\nGYRO = %d; %d; %d;\nT = %d\n\0", tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], + unsigned long local_time; + struct rtc_time rtc_t; + + int len = 0; + memset((void*)mpu6050_str, 0, MPU6050_STR_SIZE); + rt_mutex_lock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is locked\n"); + if (p_buf_data->count > 0) { + mpu6050_buf_get(p_buf_data, &tmp_data); + local_time = (u32)(tmp_data.t.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time_to_tm(local_time, &rtc_t); + sprintf(mpu6050_str, "%04d-%02d-%02d %02d:%02d:%02d:%03d\nACC = %d; %d; %d\nGYRO = %d; %d; %d;\nT = %d\n", + rtc_t.tm_year + 1900, rtc_t.tm_mon + 1, rtc_t.tm_mday, + rtc_t.tm_hour, rtc_t.tm_min, rtc_t.tm_sec, tmp_data.t.tv_nsec / 1000000, + tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], tmp_data.gyro_values[0], tmp_data.gyro_values[1], tmp_data.gyro_values[2], tmp_data.temperature); - - len = strlen( mpu6050_str ); - execute = 1; - } - else { - len = 0; - execute = 0; + len = strlen( mpu6050_str ); } - + rt_mutex_unlock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is unlocked\n"); if( count < len ) return -EINVAL; if( *ppos != 0 ) return 0; if( copy_to_user( buf, mpu6050_str, len ) ) return -EINVAL; @@ -228,175 +325,181 @@ static ssize_t dev_current_read( struct file * file, char * buf,size_t count, lo return len; } -static const struct file_operations dev_current_fops = { - .owner = THIS_MODULE, - .read = dev_current_read, -}; - static ssize_t dev_all_read( struct file * file, char * buf,size_t count, loff_t *ppos ) { + struct mpu6050_dev_context *ctx = file->private_data; + struct mpu6050_buf_data * p_buf_data = ctx->buf_data; struct mpu6050_data tmp_data; int offset = 0, len = 0, i, tmp; - static int execute = 0; - if (execute == 0) { - sprintf(mpu6050_str,"Buffer contains %d samples:\n",buf_data.count); + unsigned long local_time; + struct rtc_time rtc_t; + rt_mutex_lock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is locked\n"); + tmp = p_buf_data->count; + memset((void*)mpu6050_str, 0, MPU6050_STR_SIZE); + sprintf(mpu6050_str,"Buffer contains %d samples:\n", tmp); + len = strlen( mpu6050_str ); + + offset += len; + if( copy_to_user( buf, mpu6050_str, len ) ) return -EINVAL; + for(i = 0; i < tmp; i++) { + len = 0; + memset((void*)mpu6050_str, 0, MPU6050_STR_SIZE); + mpu6050_buf_get(p_buf_data, &tmp_data); + local_time = (u32)(tmp_data.t.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time_to_tm(local_time, &rtc_t); + sprintf(mpu6050_str, "%04d-%02d-%02d %02d:%02d:%02d:%03d\nACC = %d; %d; %d\nGYRO = %d; %d; %d;\nT = %d\n", + rtc_t.tm_year + 1900, rtc_t.tm_mon + 1, rtc_t.tm_mday, + rtc_t.tm_hour, rtc_t.tm_min, rtc_t.tm_sec, tmp_data.t.tv_nsec / 1000000, + tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], + tmp_data.gyro_values[0], tmp_data.gyro_values[1], tmp_data.gyro_values[2], + tmp_data.temperature); len = strlen( mpu6050_str ); - if( count < len ) return -EINVAL; - if( *ppos != 0 ) return 0; + if( copy_to_user( buf + offset, mpu6050_str, len ) ) return -EINVAL; offset += len; - if( copy_to_user( buf, mpu6050_str, len ) ) return -EINVAL; - tmp = buf_data.count; - for(i = 0; i < tmp; i++) { - mpu6050_buf_get(&buf_data, &tmp_data); - sprintf(mpu6050_str, "ACC = %d; %d; %d\nGYRO = %d; %d; %d\nT = %d\n%d\0", tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], - tmp_data.gyro_values[0], tmp_data.gyro_values[1], tmp_data.gyro_values[2], - tmp_data.temperature, buf_data.count); - len = strlen( mpu6050_str ); - if( copy_to_user( buf + offset, mpu6050_str, len ) ) return -EINVAL; - offset += len; - } - execute = 1; - } - else { - offset = 0; - execute = 0; } + rt_mutex_unlock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is unlocked\n"); + if( count < offset ) return -EINVAL; + if( *ppos != 0 ) return 0; *ppos = offset; return offset; } -static const struct file_operations dev_all_fops = { - .owner = THIS_MODULE, - .read = dev_all_read, +static ssize_t dev_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { + struct inode *inode = file_inode(file); + int minor = MINOR(inode->i_rdev); + struct mpu6050_dev_context *ctx = file->private_data; + + if (!(ctx->dev_client)) { + printk(KERN_INFO "mpu6050: i2c driver not probed\n"); + return -EIO; + } + + switch (minor) { + + case DEVICE_CURRENT_NUMBER: + return dev_current_read(file, buf, size, pos); + + case DEVICE_ALL_NUMBER: + return dev_all_read(file, buf, size, pos); + + default: + printk(KERN_ERR "mpu6050: unknown minor number: %d\n", minor); + return -EFAULT; + } +} + +static const struct file_operations dev_fops = { + .owner = THIS_MODULE, + .open = dev_open, + .release = dev_release, + .read = dev_read, }; -#define DEVICE_CURRENT_FIRST 0 -#define DEVICE_ALL_FIRST 0 -#define DEVICE_COUNT 1 -#define MOD_CURRENT_NAME "mpu6050_current_mod" -#define MOD_ALL_NAME "mpu6050_all_mod" +static char* DEVICE_FILE_SUFFIX[DEVICE_COUNT] = { + "current", + "all" +}; -static struct cdev h_current_cdev; -static struct cdev h_all_cdev; -static struct class *devclass; +#define MOD_NAME "mpu6050_mod" -static int __init dev_init( void ) { +static int mpu6050_dev_init(void) { int ret, i; dev_t dev; + + context = kmalloc(sizeof(struct mpu6050_dev_context), GFP_KERNEL); + if (!context) { + ret = -ENOMEM; + goto err; + } ret = i2c_add_driver(&mpu6050_i2c_driver); if (ret < 0) { printk(KERN_ERR "mpu6050: failed to add new i2c driver: %d\n", ret); goto err; } printk(KERN_INFO "mpu6050: i2c driver created\n"); -//current - if( major_current != 0 ) { - dev = MKDEV( major_current, DEVICE_CURRENT_FIRST ); - ret = register_chrdev_region( dev, DEVICE_COUNT, MOD_CURRENT_NAME ); - } - else { - ret = alloc_chrdev_region( &dev, DEVICE_CURRENT_FIRST, DEVICE_COUNT, MOD_CURRENT_NAME ); - major_current = MAJOR( dev ); // не забыть зафиксировать! - } - if( ret < 0 ) { - printk( KERN_ERR "Can not register char device region\n" ); - goto err; - } - cdev_init( &h_current_cdev, &dev_current_fops ); - h_current_cdev.owner = THIS_MODULE; - h_current_cdev.ops = &dev_current_fops; // обязательно! - cdev_init() недостаточно? - ret = cdev_add( &h_current_cdev, dev, DEVICE_COUNT ); - if( ret < 0 ) { - unregister_chrdev_region( MKDEV( major_current, DEVICE_CURRENT_FIRST ), DEVICE_COUNT ); - printk( KERN_ERR "Can not add char device\n" ); - goto err; - } - devclass = class_create( THIS_MODULE, "mpu6050" ); - for (i = 0; i < DEVICE_COUNT; i++) { - #define DEV_CURRENT_NAME "mpu6050current" - dev = MKDEV( major_current, DEVICE_CURRENT_FIRST + i); - #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26) - /* struct device *device_create( struct class *cls, struct device *parent, - dev_t devt, const char *fmt, ...); */ - device_create( devclass, NULL, dev, "%s_%d", DEV_CURRENT_NAME, i ); - #else // прототип device_create() изменился! - /* struct device *device_create( struct class *cls, struct device *parent, - dev_t devt, void *drvdata, const char *fmt, ...); */ - device_create( devclass, NULL, dev, NULL, "%s_%d", DEV_CURRENT_NAME, i ); - #endif - } - printk( KERN_INFO "=========== module installed %d:%d ==============\n", - MAJOR( dev ), MINOR( dev ) ); -//all - if( major_all != 0 ) { - dev = MKDEV( major_all, DEVICE_ALL_FIRST ); - ret = register_chrdev_region( dev, DEVICE_COUNT, MOD_ALL_NAME ); + if( major != 0 ) { + dev = MKDEV( major, DEVICE_FIRST ); + ret = register_chrdev_region( dev, DEVICE_COUNT, MOD_NAME ); } else { - ret = alloc_chrdev_region( &dev, DEVICE_ALL_FIRST, DEVICE_COUNT, MOD_ALL_NAME ); - major_all = MAJOR( dev ); // не забыть зафиксировать! + ret = alloc_chrdev_region( &dev, DEVICE_FIRST, DEVICE_COUNT, MOD_NAME ); + major = MAJOR( dev ); // не забыть зафиксировать! } if( ret < 0 ) { - printk( KERN_ERR "Can not register char device region\n" ); + printk( KERN_ERR "mpu6050: can not register char device region\n" ); goto err; } - cdev_init( &h_all_cdev, &dev_all_fops ); - h_all_cdev.owner = THIS_MODULE; - h_all_cdev.ops = &dev_all_fops; // обязательно! - cdev_init() недостаточно? - ret = cdev_add( &h_all_cdev, dev, DEVICE_COUNT ); + + printk( KERN_INFO "mpu6050: context = 0x%X\n", (unsigned int)context ); + rt_mutex_init(&context->buf_mutex); + rt_mutex_init(&context->dev_current_mutex); + rt_mutex_init(&context->dev_all_mutex); + cdev_init(&context->cdev,&dev_fops); + context->cdev.owner = THIS_MODULE; + context->cdev.ops = &dev_fops; + ret = cdev_add( &context->cdev, dev, DEVICE_COUNT ); if( ret < 0 ) { - unregister_chrdev_region( MKDEV( major_all, DEVICE_ALL_FIRST ), DEVICE_COUNT ); - printk( KERN_ERR "Can not add char device\n" ); + unregister_chrdev_region( MKDEV( major, DEVICE_FIRST ), DEVICE_COUNT ); + printk( KERN_ERR "mpu6050: can not add char device\n" ); goto err; } + context->devclass = class_create( THIS_MODULE, "mpu6050" ); for (i = 0; i < DEVICE_COUNT; i++) { - #define DEV_ALL_NAME "mpu6050all" - dev = MKDEV( major_all, DEVICE_ALL_FIRST + i); + #define DEV_NAME "mpu6050" + dev = MKDEV( major, DEVICE_FIRST + i); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26) /* struct device *device_create( struct class *cls, struct device *parent, dev_t devt, const char *fmt, ...); */ - device_create( devclass, NULL, dev, "%s_%d", DEV_ALL_NAME, i ); + device_create( context->devclass, NULL, dev, "%s_%s", DEV_NAME, DEVICE_FILE_SUFFIX[i] ); #else // прототип device_create() изменился! /* struct device *device_create( struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...); */ - device_create( devclass, NULL, dev, NULL, "%s_%d", DEV_ALL_NAME, i ); + device_create( context->devclass, NULL, dev, NULL, "%s_%s", DEV_NAME, DEVICE_FILE_SUFFIX[i] ); #endif } - printk( KERN_INFO "=========== module installed %d:%d ==============\n", + printk( KERN_INFO "mpu6050:======= module installed %d:%d ==========\n", MAJOR( dev ), MINOR( dev ) ); -// mpu6050_buf_init(buf_data); - - err: - return ret; + context->buf_data = mpu6050_buf_init(); + if (!(context->buf_data)) { + unregister_chrdev_region( MKDEV( major, DEVICE_FIRST ), DEVICE_COUNT ); + printk( KERN_ERR "mpu6050: can not allocate buffer for mpu6050 data\n" ); + goto err; + } + context->read_task = kthread_run( mpu6050_read_thread, (void*)NULL, MPU6050_THREAD_NAME ); +err: + return ret; } -static void __exit dev_exit( void ) { +static void mpu6050_dev_deinit(void) { dev_t dev; int i; -// mpu6050_buf_deinit(buf_data); + mpu6050_buf_deinit(context->buf_data); + kthread_stop(context->read_task); i2c_del_driver(&mpu6050_i2c_driver); -//current - for (i = 0; i < DEVICE_COUNT; i++) { - dev = MKDEV( major_current, DEVICE_CURRENT_FIRST + i); - device_destroy( devclass, dev ); - } -//all + for (i = 0; i < DEVICE_COUNT; i++) { - dev = MKDEV( major_all, DEVICE_CURRENT_FIRST + i); - device_destroy( devclass, dev ); + dev = MKDEV( major, DEVICE_FIRST + i); + device_destroy( context->devclass, dev ); } + class_destroy( context->devclass ); + cdev_del( &context->cdev ); + unregister_chrdev_region( MKDEV( major, DEVICE_FIRST ), DEVICE_COUNT ); + printk( KERN_INFO "mpu6050:=========== module removed ==============\n" ); +} - class_destroy( devclass ); -//currnet - cdev_del( &h_current_cdev ); - unregister_chrdev_region( MKDEV( major_current, DEVICE_CURRENT_FIRST ), DEVICE_COUNT ); - printk( KERN_INFO "=============== module removed ==================\n" ); -//all - cdev_del( &h_all_cdev ); - unregister_chrdev_region( MKDEV( major_all, DEVICE_ALL_FIRST ), DEVICE_COUNT ); - printk( KERN_INFO "=============== module removed ==================\n" ); +static int __init dev_init( void ) { + int ret; + ret = mpu6050_dev_init(); + return ret; +} + +static void __exit dev_exit( void ) { + mpu6050_dev_deinit(); } module_init( dev_init ); module_exit( dev_exit ); + From 113561e409af0a2fd068236bb5a3cacbd378dccf Mon Sep 17 00:00:00 2001 From: ruvi Date: Wed, 6 Dec 2017 18:13:53 +0200 Subject: [PATCH 9/9] Timer+shedulework mechanism --- mpu6050/Makefile | 2 +- mpu6050/mpu6050_dev_2.c | 513 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 514 insertions(+), 1 deletion(-) create mode 100644 mpu6050/mpu6050_dev_2.c diff --git a/mpu6050/Makefile b/mpu6050/Makefile index 6d5168d..d51050d 100644 --- a/mpu6050/Makefile +++ b/mpu6050/Makefile @@ -5,7 +5,7 @@ ifneq ($(KERNELRELEASE),) obj-m += mpu6050.o obj-m += mpu6050_dev.o -#obj-m += mpu6050_test.o +obj-m += mpu6050_dev_2.o else diff --git a/mpu6050/mpu6050_dev_2.c b/mpu6050/mpu6050_dev_2.c new file mode 100644 index 0000000..1843e8d --- /dev/null +++ b/mpu6050/mpu6050_dev_2.c @@ -0,0 +1,513 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mpu6050-regs.h" + +MODULE_LICENSE( "GPL" ); +MODULE_AUTHOR( "Ruslan V. Sukhov " ); +MODULE_VERSION( "1.1" ); + +struct mpu6050_data { + struct timespec t; + int accel_values[3]; + int gyro_values[3]; + int temperature; + +}; + +#define MPU6050_BUF_SIZE 10 +struct mpu6050_buf_data { + unsigned int size; + unsigned int read_index; + unsigned int write_index; + unsigned int count; + struct mpu6050_data* buf; +}; + +struct mpu6050_dev_context { + struct cdev cdev; + struct class* devclass; + struct i2c_client* dev_client; + struct mpu6050_buf_data* buf_data; + struct work_struct read_struct; + struct timer_list read_timer; + struct rt_mutex buf_mutex; + struct rt_mutex dev_current_mutex; + struct rt_mutex dev_all_mutex; +}* context; + +static struct mpu6050_buf_data* mpu6050_buf_init(void) { + struct mpu6050_buf_data* bd; + bd = kmalloc(sizeof(struct mpu6050_buf_data),GFP_KERNEL); + if (!bd) + { + printk(KERN_ERR "mpu6050: mpu6050_buf_data memory allocation error"); + return 0; + } + bd->size = MPU6050_BUF_SIZE; + bd->read_index = 0; + bd->write_index = 0; + bd->count = 0; + + bd->buf = 0; + bd->buf = kmalloc(sizeof(struct mpu6050_data) * (bd->size),GFP_KERNEL); + if (!(bd->buf)) { + kfree(bd); + printk(KERN_ERR "mpu6050: mpu6050_buf memory allocation error"); + return 0; + } + return bd; +} + +static void mpu6050_buf_deinit(struct mpu6050_buf_data* bd) +{ + kfree(bd->buf); + kfree(bd); + bd = 0; + return; +} + +static int mpu6050_buf_add(struct mpu6050_buf_data* bd, struct mpu6050_data* d) +{ + int i; + for (i = 0; i < 3; i++) { + (bd->buf[bd->write_index]).accel_values[i] = (d->accel_values)[i]; + (bd->buf[bd->write_index]).gyro_values[i] = (d->gyro_values)[i]; + } + (bd->buf[bd->write_index]).temperature = d->temperature; + (bd->buf[bd->write_index]).t = d->t; + + if (bd->count < bd->size) { + (bd->write_index)++; + if (bd->write_index == bd->size) bd->write_index = 0; + (bd->count)++; + } + else { + (bd->write_index)++; + (bd->read_index)++; + if (bd->write_index == bd->size) bd->write_index = 0; + if (bd->read_index == bd->size) bd->read_index = 0; + } + return 0; +} + +static int mpu6050_buf_get(struct mpu6050_buf_data* bd, struct mpu6050_data* d) +{ + if (bd->count > 0) { + int i; + for (i = 0; i < 3; i++) { + (d->accel_values)[i] = (bd->buf[bd->read_index]).accel_values[i]; + (d->gyro_values)[i] = (bd->buf[bd->read_index]).gyro_values[i]; + } + d->temperature = (bd->buf[bd->read_index]).temperature; + d->t = (bd->buf[bd->read_index]).t; + (bd->read_index)++; + if (bd->read_index == bd->size) bd->read_index = 0; + (bd->count)--; + } + return 0; +} + +static int mpu6050_read_data(struct mpu6050_data* p_mpu6050_data) +{ + int temp; + + if (context->dev_client == 0) + return -ENODEV; + + /* accel */ + p_mpu6050_data->accel_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_ACCEL_XOUT_H)); + p_mpu6050_data->accel_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_ACCEL_YOUT_H)); + p_mpu6050_data->accel_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_ACCEL_ZOUT_H)); + /* gyro */ + p_mpu6050_data->gyro_values[0] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_GYRO_XOUT_H)); + p_mpu6050_data->gyro_values[1] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_GYRO_YOUT_H)); + p_mpu6050_data->gyro_values[2] = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_GYRO_ZOUT_H)); + /* temp */ + /* Temperature in degrees C = (TEMP_OUT Register Value as a signed quantity)/340 + 36.53 */ + temp = (s16)((u16)i2c_smbus_read_word_swapped(context->dev_client, REG_TEMP_OUT_H)); + p_mpu6050_data->temperature = (temp + 12420 + 170) / 340; + p_mpu6050_data->t = current_kernel_time(); + + printk(KERN_INFO "mpu6050: sensor data read:\n"); + printk(KERN_INFO "ACC = %d; %d; %d\n", + p_mpu6050_data->accel_values[0], + p_mpu6050_data->accel_values[1], + p_mpu6050_data->accel_values[2]); + printk(KERN_INFO "GYRO = %d; %d; %d\n", + p_mpu6050_data->gyro_values[0], + p_mpu6050_data->gyro_values[1], + p_mpu6050_data->gyro_values[2]); + printk(KERN_INFO "TEMP = %d\n", p_mpu6050_data->temperature); + return 0; +} + +static int period_ms = 1000; +module_param( period_ms, int, S_IRUGO ); + +static void mpu6050_read_func(struct work_struct *ws) +{ + struct mpu6050_data tmp_data; + mpu6050_read_data(&tmp_data); + rt_mutex_lock(&context->buf_mutex); + mpu6050_buf_add(context->buf_data, &tmp_data); + rt_mutex_unlock(&context->buf_mutex); +} + +void IRQ_TimerHandler(unsigned long data) +{ + static unsigned int i = 0; + schedule_work(&(context->read_struct)); + (context->read_timer).expires = get_jiffies_64() + period_ms/(1000/HZ); + add_timer(&(context->read_timer)); + i++; +} + +static int mpu6050_probe(struct i2c_client *drv_client, const struct i2c_device_id *id) +{ + int ret; + + printk(KERN_INFO "mpu6050: 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)) { + printk(KERN_ERR "mpu6050: i2c_smbus_read_byte_data() failed with error: %d\n", ret); + return ret; + } + if (ret != MPU6050_WHO_AM_I) { + printk(KERN_ERR "mpu6050: wrong i2c device found: expected 0x%X, found 0x%X\n", MPU6050_WHO_AM_I, ret); + return -1; + } + printk(KERN_INFO "mpu6050: 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); + + context->dev_client = drv_client; + + printk(KERN_INFO "mpu6050: i2c driver probed\n"); + return 0; +} + +static int mpu6050_remove(struct i2c_client *drv_client) +{ + context->dev_client = 0; + + printk(KERN_INFO "mpu6050: i2c driver removed\n"); + return 0; +} + +static const struct i2c_device_id mpu6050_idtable [] = { + { "gl_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, +}; + +#define MPU6050_STR_SIZE 100 +static char mpu6050_str[MPU6050_STR_SIZE]; + +#define DEVICE_FIRST 0 +#define DEVICE_COUNT 2 + +#define DEVICE_CURRENT_NUMBER 0 +#define DEVICE_ALL_NUMBER 1 + +static int major = 0; +module_param( major, int, S_IRUGO ); + +static int dev_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct mpu6050_dev_context *ctx = container_of(inode->i_cdev, struct mpu6050_dev_context, cdev); + switch (minor) { + case DEVICE_CURRENT_NUMBER: + if (rt_mutex_trylock(&ctx->dev_current_mutex) == 0) { + printk(KERN_INFO "mpu6050: %d minor device is busy\n", minor); + return -EBUSY; + } + break; + case DEVICE_ALL_NUMBER: + if (rt_mutex_trylock(&ctx->dev_all_mutex) == 0) { + printk(KERN_INFO "mpu6050: %d minor device is busy\n", minor); + return -EBUSY; + } + break; + default: + printk(KERN_ERR "mpu6050: unknown minor number: %d\n", minor); + return -EFAULT; + } + printk(KERN_INFO "mpu6050: %d minor device is locked\n", minor); + file->private_data = ctx; + return 0; +} + +static int dev_release(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct mpu6050_dev_context *ctx = file->private_data; + switch (minor) { + case DEVICE_CURRENT_NUMBER: + rt_mutex_unlock(&ctx->dev_current_mutex); + break; + case DEVICE_ALL_NUMBER: + rt_mutex_unlock(&ctx->dev_all_mutex); + break; + default: + printk(KERN_ERR "mpu6050: unknown minor number: %d\n", minor); + return -EFAULT; + } + printk(KERN_INFO "mpu6050: %d minor device is unlocked\n", minor); + return 0; +} + +static ssize_t dev_current_read( struct file * file, char * buf,size_t count, loff_t *ppos ) { + struct mpu6050_dev_context *ctx = file->private_data; + struct mpu6050_buf_data * p_buf_data = ctx->buf_data; + struct mpu6050_data tmp_data; + unsigned long local_time; + struct rtc_time rtc_t; + + int len = 0; + memset((void*)mpu6050_str, 0, MPU6050_STR_SIZE); + rt_mutex_lock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is locked\n"); + if (p_buf_data->count > 0) { + mpu6050_buf_get(p_buf_data, &tmp_data); + local_time = (u32)(tmp_data.t.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time_to_tm(local_time, &rtc_t); + sprintf(mpu6050_str, "%04d-%02d-%02d %02d:%02d:%02d:%03d\nACC = %d; %d; %d\nGYRO = %d; %d; %d;\nT = %d\n", + rtc_t.tm_year + 1900, rtc_t.tm_mon + 1, rtc_t.tm_mday, + rtc_t.tm_hour, rtc_t.tm_min, rtc_t.tm_sec, tmp_data.t.tv_nsec / 1000000, + tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], + tmp_data.gyro_values[0], tmp_data.gyro_values[1], tmp_data.gyro_values[2], + tmp_data.temperature); + len = strlen( mpu6050_str ); + } + rt_mutex_unlock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is unlocked\n"); + if( count < len ) return -EINVAL; + if( *ppos != 0 ) return 0; + if( copy_to_user( buf, mpu6050_str, len ) ) return -EINVAL; + *ppos = len; + return len; +} + +static ssize_t dev_all_read( struct file * file, char * buf,size_t count, loff_t *ppos ) { + struct mpu6050_dev_context *ctx = file->private_data; + struct mpu6050_buf_data * p_buf_data = ctx->buf_data; + struct mpu6050_data tmp_data; + int offset = 0, len = 0, i, tmp; + unsigned long local_time; + struct rtc_time rtc_t; + rt_mutex_lock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is locked\n"); + tmp = p_buf_data->count; + memset((void*)mpu6050_str, 0, MPU6050_STR_SIZE); + sprintf(mpu6050_str,"Buffer contains %d samples:\n", tmp); + len = strlen( mpu6050_str ); + + offset += len; + if( copy_to_user( buf, mpu6050_str, len ) ) return -EINVAL; + for(i = 0; i < tmp; i++) { + len = 0; + memset((void*)mpu6050_str, 0, MPU6050_STR_SIZE); + mpu6050_buf_get(p_buf_data, &tmp_data); + local_time = (u32)(tmp_data.t.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time_to_tm(local_time, &rtc_t); + sprintf(mpu6050_str, "%04d-%02d-%02d %02d:%02d:%02d:%03d\nACC = %d; %d; %d\nGYRO = %d; %d; %d;\nT = %d\n", + rtc_t.tm_year + 1900, rtc_t.tm_mon + 1, rtc_t.tm_mday, + rtc_t.tm_hour, rtc_t.tm_min, rtc_t.tm_sec, tmp_data.t.tv_nsec / 1000000, + tmp_data.accel_values[0], tmp_data.accel_values[1], tmp_data.accel_values[2], + tmp_data.gyro_values[0], tmp_data.gyro_values[1], tmp_data.gyro_values[2], + tmp_data.temperature); + len = strlen( mpu6050_str ); + if( copy_to_user( buf + offset, mpu6050_str, len ) ) return -EINVAL; + offset += len; + } + rt_mutex_unlock(&ctx->buf_mutex); + printk(KERN_INFO "mpu6050: sample buffer is unlocked\n"); + if( count < offset ) return -EINVAL; + if( *ppos != 0 ) return 0; + *ppos = offset; + return offset; +} + +static ssize_t dev_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { + struct inode *inode = file_inode(file); + int minor = MINOR(inode->i_rdev); + struct mpu6050_dev_context *ctx = file->private_data; + + if (!(ctx->dev_client)) { + printk(KERN_INFO "mpu6050: i2c driver not probed\n"); + return -EIO; + } + + switch (minor) { + + case DEVICE_CURRENT_NUMBER: + return dev_current_read(file, buf, size, pos); + + case DEVICE_ALL_NUMBER: + return dev_all_read(file, buf, size, pos); + + default: + printk(KERN_ERR "mpu6050: unknown minor number: %d\n", minor); + return -EFAULT; + } +} + +static const struct file_operations dev_fops = { + .owner = THIS_MODULE, + .open = dev_open, + .release = dev_release, + .read = dev_read, +}; + +static char* DEVICE_FILE_SUFFIX[DEVICE_COUNT] = { + "current", + "all" +}; + +#define MOD_NAME "mpu6050_mod" + +static int mpu6050_dev_init(void) { + int ret, i; + dev_t dev; + + context = kmalloc(sizeof(struct mpu6050_dev_context), GFP_KERNEL); + if (!context) { + ret = -ENOMEM; + goto err; + } + ret = i2c_add_driver(&mpu6050_i2c_driver); + if (ret < 0) { + printk(KERN_ERR "mpu6050: failed to add new i2c driver: %d\n", ret); + goto err; + } + printk(KERN_INFO "mpu6050: i2c driver created\n"); + + if( major != 0 ) { + dev = MKDEV( major, DEVICE_FIRST ); + ret = register_chrdev_region( dev, DEVICE_COUNT, MOD_NAME ); + } + else { + ret = alloc_chrdev_region( &dev, DEVICE_FIRST, DEVICE_COUNT, MOD_NAME ); + major = MAJOR( dev ); // не забыть зафиксировать! + } + if( ret < 0 ) { + printk( KERN_ERR "mpu6050: can not register char device region\n" ); + goto err; + } + + printk( KERN_INFO "mpu6050: context = 0x%X\n", (unsigned int)context ); + rt_mutex_init(&context->buf_mutex); + rt_mutex_init(&context->dev_current_mutex); + rt_mutex_init(&context->dev_all_mutex); + cdev_init(&context->cdev,&dev_fops); + context->cdev.owner = THIS_MODULE; + context->cdev.ops = &dev_fops; + ret = cdev_add( &context->cdev, dev, DEVICE_COUNT ); + if( ret < 0 ) { + unregister_chrdev_region( MKDEV( major, DEVICE_FIRST ), DEVICE_COUNT ); + printk( KERN_ERR "mpu6050: can not add char device\n" ); + goto err; + } + context->devclass = class_create( THIS_MODULE, "mpu6050" ); + for (i = 0; i < DEVICE_COUNT; i++) { + #define DEV_NAME "mpu6050" + dev = MKDEV( major, DEVICE_FIRST + i); + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,26) + /* struct device *device_create( struct class *cls, struct device *parent, + dev_t devt, const char *fmt, ...); */ + device_create( context->devclass, NULL, dev, "%s_%s", DEV_NAME, DEVICE_FILE_SUFFIX[i] ); + #else // прототип device_create() изменился! + /* struct device *device_create( struct class *cls, struct device *parent, + dev_t devt, void *drvdata, const char *fmt, ...); */ + device_create( context->devclass, NULL, dev, NULL, "%s_%s", DEV_NAME, DEVICE_FILE_SUFFIX[i] ); + #endif + } + printk( KERN_INFO "mpu6050:======= module installed %d:%d ==========\n", + MAJOR( dev ), MINOR( dev ) ); + + context->buf_data = mpu6050_buf_init(); + if (!(context->buf_data)) { + unregister_chrdev_region( MKDEV( major, DEVICE_FIRST ), DEVICE_COUNT ); + printk( KERN_ERR "mpu6050: can not allocate buffer for mpu6050 data\n" ); + goto err; + } + INIT_WORK(&(context->read_struct), mpu6050_read_func); + init_timer(&(context->read_timer)); + (context->read_timer).expires = get_jiffies_64() + period_ms/(1000/HZ); + (context->read_timer).data = 0; + (context->read_timer).function = IRQ_TimerHandler; + add_timer(&(context->read_timer)); +err: + return ret; +} + +static void mpu6050_dev_deinit(void) { + dev_t dev; + int i; + mpu6050_buf_deinit(context->buf_data); + del_timer(&(context->read_timer)); + i2c_del_driver(&mpu6050_i2c_driver); + + for (i = 0; i < DEVICE_COUNT; i++) { + dev = MKDEV( major, DEVICE_FIRST + i); + device_destroy( context->devclass, dev ); + } + class_destroy( context->devclass ); + cdev_del( &context->cdev ); + unregister_chrdev_region( MKDEV( major, DEVICE_FIRST ), DEVICE_COUNT ); + printk( KERN_INFO "mpu6050:=========== module removed ==============\n" ); +} + +static int __init dev_init( void ) { + int ret; + ret = mpu6050_dev_init(); + return ret; +} + +static void __exit dev_exit( void ) { + mpu6050_dev_deinit(); +} + +module_init( dev_init ); +module_exit( dev_exit ); +